language-agnostic - ماذا يعني "البرنامج إلى واجهة"؟




oop interface (25)

لقد رأيت هذا ذكر عدة مرات وأنا لست واضحا على ما يعنيه. متى ولماذا تفعل هذا؟

أعرف ما هي الأسطح البينية ، لكن الحقيقة التي لم أكن واضحة بشأنها تجعلني أعتقد أنني أفتقد استخدامها بشكل صحيح.

هل الأمر كذلك إذا كنت ستفعل:

IInterface classRef = new ObjectWhatever()

هل يمكن استخدام أي فصل يقوم بتنفيذ IInterface ؟ متى ستحتاج إلى القيام بذلك؟ الشيء الوحيد الذي يمكنني التفكير فيه هو إذا كان لديك طريقة وأنت غير متأكد من أي كائن سوف يتم تمريره متوقعًا من أجل تطبيق IInterface . لا أستطيع أن أفكر كم من المرات ستحتاج إلى القيام بذلك ... (أيضا ، كيف يمكن أن تكتب طريقة تأخذ في كائن يقوم بتنفيذ واجهة؟ هل هذا ممكن؟)

آسف إذا غاب عن النقطة تماما.


Answers

Let's start out with some definitions first:

Interface n. The set of all signatures defined by an object's operations is called the interface to the object

Type n. A particular interface

A simple example of an interface as defined above would be all the PDO object methods such as query() , commit() , close() etc., as a whole, not separately. These methods, ie its interface define the complete set of messages, requests that can be sent to the object.

A type as defined above is a particular interface. I will use the made-up shape interface to demonstrate: draw() , getArea() , getPerimeter() etc..

If an object is of the Database type we mean that it accepts messages/requests of the database interface, query() , commit() etc.. Objects can be of many types. You can have a database object be of the shape type as long as it implements its interface, in which case this would be sub-typing .

Many objects can be of many different interfaces/types and implement that interface differently. This allows us to substitute objects, letting us choose which one to use. Also known as polymorphism.

The client will only be aware of the interface and not the implementation.

So in essence programming to an interface would involve making some type of abstract class such as Shape with the interface only specified ie draw() , getCoordinates() , getArea() etc.. And then have different concrete classes implement those interfaces such as a Circle class, Square class, Triangle class. Hence program to an interface not an implementation.


البرمجة إلى واجهات رهيبة ، ويعزز اقتران فضفاض. كما ذكرناlassevk ، فإن انقلاب التحكم هو استخدام عظيم لهذا.

بالإضافة إلى ذلك ، ننظر إلى مديري SOLID . هنا سلسلة فيديو

ينتقل من خلال المشفر الثابت (على سبيل المثال إلى حد كبير) ثم ينظر إلى الواجهات ، ويتقدم في النهاية إلى أداة IoC / DI (NInject)


تخيل أن لديك منتجًا يسمى "Zebra" يمكن تمديده عن طريق المكونات الإضافية. يجدها الإضافات عن طريق البحث عن ملفات DLL في بعض الدليل. يقوم بتحميل كافة ملفات DLL هذه ويستخدم انعكاسات البحث عن أي فئات تقوم بتطبيق IZebraPlugin ، ثم يقوم باستدعاء أساليب هذه الواجهة للاتصال IZebraPlugin الإضافية.

وهذا يجعلها مستقلة تمامًا عن أي فئة إضافية معينة - فهي لا تهتم بالصفوف. انها تهتم فقط أنها تلبي مواصفات الواجهة.

Interfaces are a way of defining points of extensibility like this. Code that talks to an interface is more loosely coupled - in fact it is not coupled at all to any other specific code. It can inter-operate with plugins written years later by people who have never met the original developer.

You could instead use a base class with virtual functions - all plugins would be derived from the base class. But this is much more limiting because a class can only have one base class, whereas it can implement any number of interfaces.


في ما يلي مثال بسيط لتوضيحه عند برمجة نظام حجز رحلة.

//This interface is very flexible and abstract
    addPassenger(Plane seat, Ticket ticket); 

//Boeing is implementation of Plane
    addPassenger(Boeing747 seat, EconomyTicket ticket); 
    addPassenger(Cessna, BusinessClass ticket);


    addPassenger(J15, E87687); 

تقول البرمجة إلى واجهة: "أحتاج إلى هذه الوظيفة ولا يهمني من أين تأتي".

خذ بعين الاعتبار (في Java) ، واجهة List مقابل فئات ملموسة ArrayList و LinkedList . إذا كان كل ما يهمني هو أن لدي بنية بيانات تحتوي على عناصر بيانات متعددة يجب الوصول إليها عبر التكرار ، فسأختار List (وهذا يمثل 99٪ من الوقت). إذا كنت أعلم أنني أحتاج إلى إدراج / حذف في الوقت الثابت من أيٍّ من طرفي القائمة ، فقد أختار التنفيذ الملموس لـ LinkedList (أو على الأرجح ، استخدم واجهة Queue ). إذا كنت أعلم أنني بحاجة إلى الوصول العشوائي عن طريق الفهرس ، فسأقوم باختيار فئة الخرسانة ArrayList .


هناك الكثير من التفسير هناك ، ولكن لجعله أكثر بساطة. خذ على سبيل المثال List . يمكن للمرء تنفيذ قائمة مع:

  1. مجموعة داخلية
  2. قائمة مرتبطة
  3. التنفيذ الأخرى

من خلال البناء على واجهة ، قل List . أنت فقط رمز لتعريف قائمة أو ما يعني List في الواقع.

يمكنك استخدام أي نوع من التنفيذ array تنفيذ array داخليًا. ولكن لنفترض أنك ترغب في تغيير التنفيذ لسبب ما ، لنقل خطأ أو أداء. ثم عليك فقط تغيير List<String> ls = new ArrayList<String>() تعريف List<String> ls = new ArrayList<String>() إلى List<String> ls = new LinkedList<String>() .

لا يوجد مكان آخر في التعليمات البرمجية ، سيكون عليك تغيير أي شيء آخر ؛ لأن كل شيء آخر تم بناؤه على تعريف List .


لذا ، فقط للحصول على هذا الحق ، فإن ميزة الواجهة هي أنني أستطيع فصل استدعاء الأسلوب من أي فئة معينة. بدلاً من إنشاء مثيل للواجهة ، حيث يتم منح التطبيق من أي فئة أختارها والتي تنفذ هذه الواجهة. مما يتيح لي الحصول على العديد من الفصول ، والتي لها وظائف مماثلة ولكنها مختلفة قليلاً وفي بعض الحالات (الحالات المتعلقة بنية الواجهة) لا تهتم بالموضوع الذي هي عليه.

على سبيل المثال ، يمكن أن يكون لدي واجهة حركة. يمكن تمرير طريقة تجعل شيء ما "يتحرك" وأي كائن (شخص أو سيارة أو قطة) يقوم بتنفيذ واجهة الحركة ويطلب منه الانتقال. دون هذه الطريقة كل معرفة نوع الطبقة هو.


أنا في وقت متأخر قادم على هذا السؤال ، ولكن أريد أن أذكر هنا أن سطر "البرنامج إلى واجهة ، وليس تنفيذ" كان بعض النقاش الجيد في كتاب أنماط (Goop of Four) تصميم GoF.

وذكر ، في ص. 18:

البرنامج إلى واجهة ، وليس التنفيذ

لا تعلن المتغيرات لتكون حالات من فئات محددة ملموسة. بدلا من ذلك ، ارتكبت فقط إلى واجهة المعرفة من قبل فئة مجردة. ستجد هذا موضوعًا مشتركًا لأنماط التصميم في هذا الكتاب.

وفوق ذلك ، بدأت مع:

هناك فائدتان للتعامل مع الكائنات فقط من حيث الواجهة المعرّفة بواسطة فئات الملخصات:

  1. يظل العملاء غير مدركين لأنواع محددة من الكائنات التي يستخدمونها ، طالما أن الكائنات تلتزم بالواجهة التي يتوقعها العملاء.
  2. يظل العملاء غير مدركين للفئات التي تطبق هذه الكائنات. لا يعرف العملاء إلا عن الفئة (الخانات) المجردة التي تحدد الواجهة.

لذلك بعبارة أخرى ، لا تكتبها في فصلك بحيث تحتوي على طريقة quack() للبط ، ثم طريقة bark() للكلاب ، لأنها محددة جدًا لتنفيذ معين لفئة (أو فئة فرعية) . بدلاً من ذلك ، قم بكتابة الطريقة باستخدام أسماء عامة كافية لاستخدامها في الفئة الأساسية ، مثل giveSound() أو move() ، بحيث يمكن استخدامها للبط ، أو الكلاب ، أو حتى السيارات ، ثم العميل الخاص بك يمكن القول ببساطة الطبقات .giveSound() بدلاً من التفكير في ما إذا كنت تريد استخدام quack() أو bark() أو حتى تحديد النوع قبل إصدار الرسالة الصحيحة ليتم إرسالها إلى الكائن.


يعد استخدام الواجهات عاملًا رئيسيًا في جعل الكود الخاص بك قابلًا للاختبار بسهولة بالإضافة إلى إزالة الروابط غير الضرورية بين فصولك الدراسية. من خلال إنشاء واجهة تعرّف العمليات في فصلك ، فإنك تسمح للفصول الدراسية التي ترغب في استخدام هذه الوظيفة بالقدرة على استخدامها دون الاعتماد على فئة التنفيذ الخاصة بك مباشرةً. إذا قررت في وقت لاحق تغيير واستخدام تطبيق مختلف ، فإنك تحتاج فقط إلى تغيير جزء الرمز الذي يتم إنشاء التطبيق فيه. لا يلزم تغيير بقية التعليمات البرمجية لأنها تعتمد على الواجهة وليس على فئة التطبيق.

هذا مفيد جدا في إنشاء اختبارات الوحدة. في الفصل تحت الاختبار لديك تعتمد على واجهة وحقن مثيل واجهة في الفئة (أو مصنع يتيح لها بناء مثيلات من واجهة حسب الحاجة) عن طريق منشئ أو مستقر خاصية. يستخدم الفصل الواجهة المتوفرة (أو التي تم إنشاؤها) في أساليبها. عندما تذهب إلى كتابة اختباراتك ، يمكنك أن تسخر أو تخدع الواجهة وتوفر واجهة تستجيب للبيانات التي تم تكوينها في اختبار الوحدة. يمكنك القيام بذلك لأن صفك الدراسي يخضع فقط للواجهة وليس للتنفيذ الفعلي. أي فئة تقوم بتنفيذ الواجهة ، بما في ذلك صفحتك الوهمية أو المزيفة ، ستفعل.

EDIT: يوجد أدناه رابط لمقالة حيث يناقش Erich Gamma اقتباسه ، "Program to an interface، not a implementation".

http://www.artima.com/lejava/articles/designprinciples.html


Program to an interface allows to change implementation of contract defined by interface seamlessly. It allows loose coupling between contract and specific implementations.

IInterface classRef = new ObjectWhatever()

You could use any class that implements IInterface? When would you need to do that?

Have a look at this SE question for good example.

Why should the interface for a Java class be preferred?

does using an Interface hit performance?

if so how much?

نعم فعلا. It will have slight performance overhead in sub-seconds. But if your application has requirement to change the implementation of interface dynamically, don't worry about performance impact.

how can you avoid it without having to maintain two bits of code?

Don't try to avoid multiple implementations of interface if your application need them. In absence of tight coupling of interface with one specific implementation, you may have to deploy the patch to change one implementation to other implementation.

One good use case: Implementation of Strategy pattern:

Real World Example of the Strategy Pattern


يبدو أنك تفهم كيف تعمل الواجهات ولكنها غير متأكدة من وقت استخدامها وما هي المزايا التي تقدمها. في ما يلي بعض الأمثلة على ما إذا كانت الواجهة منطقية:

// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

بعد ذلك يمكنني إنشاء GoogleSearchProvider و YahooSearchProvider و LiveSearchProvider وما إلى ذلك.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

ثم إنشاء JpegImageLoader ، GifImageLoader ، PngImageLoader ، إلخ.

تعمل معظم الوظائف الإضافية ووظائف المكون الإضافي خارج الواجهات.

استخدام شائع آخر هو لنمط مستودع التخزين. قل أرغب في تحميل قائمة الرموز البريدية من مصادر مختلفة

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

ثم يمكنني إنشاء XMLZipCodeRepository ، SQLZipCodeRepository ، CSVZipCodeRepository ، إلخ. بالنسبة لتطبيقات الويب الخاصة بي ، غالباً ما أقوم بإنشاء مستودعات XML في وقت مبكر حتى أتمكن من الحصول على شيء وتشغيله قبل أن تكون قاعدة بيانات Sql جاهزة. بمجرد أن تكون قاعدة البيانات جاهزة ، اكتب SQLRepository لاستبدال إصدار XML. تبقى بقية التعليمة البرمجية الخاصة بي دون تغيير منذ تشغيل soley إيقاف الواجهات.

يمكن أن تقبل الطرق واجهات مثل:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

It can be advantageous to program to interfaces, even when we are not depending on abstractions.

Programming to interfaces forces us to use a contextually appropriate subset of an object. That helps because it:

  1. prevents us from doing contextually inappropriate things, and
  2. lets us safely change the implementation in the future.

For example, consider a Person class that implements the Friend and the Employee interface.

class Person implements AbstractEmployee, AbstractFriend {

}

In the context of the person's birthday, we program to the Friend interface, to prevent treating the person like an Employee .

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

In the context of the person's work, we program to the Employee interface, to prevent blurring workplace boundaries.

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

عظيم. We have behaved appropriately in different contexts, and our software is working well.

Far into the future, if our business changes to work with dogs, we can change the software fairly easily. First, we create Dog class that implements both Friend and Employee . Then, we safely change new Person() to new Dog() . Even if both functions have thousands of lines of code, that simple edit will work because we know the following are true:

  1. Function party uses only the Friend subset of Person .
  2. Function workplace uses only the Employee subset of Person .
  3. Class Dog implements both the Friend and Employee interfaces.

On the other hand, if either party or workplace were to have programmed against Person , there would be a risk of both having Person -specific code. Changing from Person to Dog would require us to comb through the code to extirpate any Person -specific code that Dog does not support.

The moral : programming to interfaces helps our code to behave appropriately and to be ready for change. It also prepares our code to depend on abstractions, which brings even more advantages.


In simple terms... If I'm writing a new class Swimmer to add the functionality swim() and need to use an object of class say Dog, and this Dog class implements interface Animal which declares swim()[To better understand...you may draw a diagram as to what I am talking about]. At the top of the hierarchy(Animal) it's very abstract while at the bottom (Dog) it's very concrete. The way I think about "programming to interfaces" is that, as I write Swimmer class, I want to write my code against the interface that's as far up that hierarchy which in this case is Animal object. An interface is free from implementation details and thus makes your code loosely-coupled. The implementation details can be changed with time, however it would not affect the remaining code since all you are interacting is with the interface and not the implementation. You don't care what the implementation is like...all you know is that there will be a class that would implement the interface.


أنا لا أحتفظ interfaceبأهم شيء في اللغة: إنه أكثر شيوعًا هو استخدام الطبقة الموروثة. لكن على أي حال هم مهمون!
على سبيل المثال (هذا هو Javaرمز ، ولكن يمكن ببساطة تكييفه مع C#أو العديد من اللغات الأخرى):

interface Convertable<T> {

    T convert();
}

public class NumerableText implements Convertable<Integer> {

    private String text = "";

    public NumerableText() { }

    public NumerableText(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Integer convert() {
        return this.text.hashCode();
    }
}

public class NumerableTextArray implements Convertable<Integer> {

    private String[] textArray = "";

    public NumerableTextArray() { }

    public NumerableTextArray(String[] textArray) {
        this.textArray = textArray;
    }

    public String[] getTextArray() {
        return this.textArray;
    }

    public void setTextArray(String[] text) {
        this.textArray = textArray;
    }

    public Integer convert() {
        Integer value = 0;
        for (String text : textArray)
            value += text.hashCode();
        return value;
    }
}

public class Foo {

    public static void main() {
        Convertable<Integer> num1 = new NumerableText("hello");
        Convertable<Integer> num2 = new NumerableTextArray(new String[] { "test n°1", "test n°2" });
        System.out.println(String.valueOf(num1.convert()));
        System.out.println(String.valueOf(num2.convert()));
        //Here are you two numbers generated from two classes of different type, but both with the method convert(), which allows you to get that number.
    }
}

Also I see a lot of good and explanatory answers here, so I want to give my point of view here, including some extra information what I noticed when using this method.

Unit testing

For the last two years, I have written a hobby project and I did not write unit tests for it. After writing about 50K lines I found out it would be really necessary to write unit tests. I did not use interfaces (or very sparingly) ... and when I made my first unit test, I found out it was complicated. لماذا ا؟

Because I had to make a lot of class instances, used for input as class variables and/or parameters. So the tests look more like integration tests (having to make a complete 'framework' of classes since all was tied together).

Fear of interfaces So I decided to use interfaces. My fear was that I had to implement all functionality everywhere (in all used classes) multiple times. In some way this is true, however, by using inheritance it can be reduced a lot.

Combination of interfaces and inheritance I found out the combination is very good to be used. I give a very simple example.

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

This way copying code is not necessary, while still having the benefit of using a car as interface (ICar).


C++ explanation.

Think of an interface as your classes public methods.

You then could create a template that 'depends' on these public methods in order to carry out it's own function (it makes function calls defined in the classes public interface). Lets say this template is a container, like a Vector class, and the interface it depends on is a search algorithm.

Any algorithm class that defines the functions/interface Vector makes calls to will satisfy the 'contract' (as someone explained in the original reply). The algorithms don't even need to be of the same base class; the only requirement is that the functions/methods that the Vector depends on (interface) is defined in your algorithm.

The point of all of this is that you could supply any different search algorithm/class just as long as it supplied the interface that Vector depends on (bubble search, sequential search, quick search).

You might also want to design other containers (lists, queues) that would harness the same search algorithm as Vector by having them fulfill the interface/contract that your search algorithms depends on.

This saves time (OOP principle 'code reuse') as you are able to write an algorithm once instead of again and again and again specific to every new object you create without over-complicating the issue with an overgrown inheritance tree.

As for 'missing out' on how things operate; big-time (at least in C++), as this is how most of the Standard TEMPLATE Library's framework operates.

Of course when using inheritance and abstract classes the methodology of programming to an interface changes; but the principle is the same, your public functions/methods are your classes interface.

This is a huge topic and one of the the cornerstone principles of Design Patterns.


يجب أن تنظر إلى انقلاب التحكم:

في مثل هذا السيناريو ، لن تكتب هذا:

IInterface classRef = new ObjectWhatever();

ستكتب شيئًا كهذا:

IInterface classRef = container.Resolve<IInterface>();

من شأن ذلك الانتقال إلى إعداد مستند إلى القواعد في كائن container ، وإنشاء الكائن الفعلي لك ، والذي قد يكون ObjectWhatever. الشيء المهم هو أنه يمكنك استبدال هذه القاعدة بشيئ استخدم نوعًا آخر من الكائنات تمامًا ، وستظل الشفرة تعمل.

إذا تركنا IoC خارج الطاولة ، فيمكنك كتابة كود يعرف أنه يستطيع التحدث إلى كائن يقوم بعمل شيء معين ، ولكن ليس نوع الكائن أو كيفية عمله.

هذا من شأنه أن يأتي في متناول اليدين عند تمرير المعلمات.

أما بالنسبة للسؤال الذي يتلقاها بين قوسين "أيضاً ، كيف يمكنك كتابة أسلوب يأخذ في كائن يقوم بتنفيذ واجهة؟ هل هذا ممكن؟" ، في C # ، يمكنك ببساطة استخدام نوع الواجهة لنوع المعلمة ، كما يلي:

public void DoSomethingToAnObject(IInterface whatever) { ... }

هذا المقابس الحق في "التحدث إلى كائن يفعل شيئا محددا." تعرف الطريقة الموضحة أعلاه ما يمكن توقعه من الكائن ، وتنفذ كل شيء في IInterface ، ولكنها لا تهتم بأي نوع من الكائنات ، ولكنها فقط تلتزم بالعقد ، وهو ما هي الواجهة.

على سبيل المثال ، ربما تكون على دراية بالآلات الحاسبة وربما تكون قد استخدمت عددًا قليلًا في أيامك ، ولكن في معظم الأوقات تكون مختلفة. من ناحية أخرى ، تعرف كيف يجب أن تعمل الآلة الحاسبة القياسية ، بحيث يمكنك استخدامها جميعًا ، حتى إذا كنت لا تستطيع استخدام الميزات المحددة التي تمتلكها كل آلة حاسبة والتي لا تحتويها أي آلة أخرى.

هذا هو جمال واجهات. يمكنك كتابة جزء من التعليمات البرمجية ، يعلم أنه سيتم تمرير الكائنات إليه يمكن أن تتوقع سلوك معين من. لا يهمني أحد ما هو نوع الكائن ، فقط أنه يدعم السلوك المطلوب.

اسمحوا لي أن أقدم لكم مثالا ملموسا.

لدينا نظام ترجمة مخصص لأشكال النوافذ. يقوم هذا النظام بتكرار عناصر التحكم في نموذج وترجمة النص في كل منها. يعرف النظام كيفية التعامل مع عناصر التحكم الأساسية ، مثل الخاصية من نوع عنصر التحكم ، التي تحتوي على نص ، والأشياء الأساسية المماثلة ، ولكن لأي شيء أساسي ، فإنه لا يعمل.

الآن ، بما أن عناصر التحكم ترث من فئات محددة مسبقًا لا نملك السيطرة عليها ، يمكننا القيام بأحد الأمور الثلاثة التالية:

  1. بناء الدعم لنظام الترجمة لدينا للكشف على وجه التحديد عن نوع التحكم الذي تعمل معه ، وترجمة البتات الصحيحة (كابوس الصيانة)
  2. بناء الدعم في الطبقات الأساسية (المستحيل ، لأن جميع عناصر التحكم ترث من فئات مختلفة محددة مسبقا)
  3. إضافة دعم واجهة

لذا فعلنا. 3. جميع أدوات التحكم الخاصة بنا تعمل على تطبيقية لا يمكن التحكم بها ، وهي واجهة تعطينا طريقة واحدة ، وهي القدرة على ترجمة "نفسها" إلى حاوية نص / قواعد الترجمة. على هذا النحو ، لا يحتاج النموذج إلى معرفة نوع التحكم الذي وجده ، فقط أنه يقوم بتطبيق واجهة معينة ، ويعرف أن هناك طريقة يمكن من خلالها استدعاء توطين التحكم.


short story:Postman is asked to go home by home and receive the covers contains (letters,documents,cheques,giftcard,application,loveletter) with address written on it to deliver.

Suppose there is no cover and ask post man to go home by home and receive all the things and deliver to other person the postman can get confuse,

so better wrap it with cover(in our story it is interface) then he will do his job fine.

Now postman job is to receive and deliver the covers only..(he dont bothered what is inside in the cover).

Create type of interface not actual type, but implement with actual type.

Create to interface means your components get Fits into the rest of code easily

I give you example.

you have AirPlane interface as below.

interface Airplane{
    parkPlane();
    servicePlane();
}

Suppose you have methods in your Controller class of Planes like

parkPlane(Airplane plane)

و

servicePlane(Airplane plane)

implemented in your program. It will not BREAK your code. I mean, it need not to change as long as it accepts arguments as AirPlane .

Because it will accept any Airplane despite of actual type, flyer , highflyr , fighter , etc.

Also, in a collection:

List<Airplane> plane; // Will take all your planes.

The following example will clear your understanding.

You have a fighter plane that implements it, so

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

The same thing for HighFlyer and other clasess:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

Now think your controller classes using AirPlane several times,

Suppose your Controller class is ControlPlane like below,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

here magic comes as

you may make your new AirPlane type instances as many as you want and you are not changing

code of ControlPlane class.

you can add instance..

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

you may remove instances.. of previously created types too.


من الجيد أيضًا اختبار الوحدة ، يمكنك حقن الطبقات الخاصة بك (التي تلبي متطلبات الواجهة) في فئة تعتمد عليها


Q: - ... "You could use any class that implements interface?"
A: - Yes.

Q: -... "When would you need to do that?"
A: - Each time you need a class(es) that implements interface(s).

Note: we couldn't instantiate an interface not implemented by a class - True.

  • لماذا ا؟
  • because interface has only methods prototypes, not definitions (just functions names, not their logic)

AnIntf anInst = new Aclass();
// we could do this only if Aclass implements AnIntf.
// anInst will have Aclass reference.

ملحوظة:
Now we could understand what happend if Bclass and Cclass implements same Dintf.

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

What we have:
same interface prototypes (functions names in interface), and call different implementations.

Bibliography:
Prototypes - wikipedia


In Java these concrete classes all implement the CharSequence interface:

CharBuffer, String, StringBuffer, StringBuilder

These concrete classes do not have a common parent class other than Object, so there is nothing that relates them, other than the fact they each have something to do with arrays of characters, representing such, or manipulating such. For instance, the characters of String cannot be changed once a String object is instantiated, whereas the characters of StringBuffer or StringBuilder can be edited.

Yet each one of these classes is capable of suitably implementing the CharSequence interface methods:

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()

In some cases Java class library classes that used to accept String have been revised to now accept the CharSequence interface. So if you have an instance of StringBuilder, instead of extracting a String object (which means instantiating a new object instance), can instead just pass the StringBuilder itself as it implements the CharSequence interface.

The Appendable interface that some classes implement has much the same kind of benefit for any situation where characters can be appended to an instance of the underlying concrete class object instance. All of these concrete classes implement the Appendable interface:

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer


إنه يجعل التعليمة البرمجية الخاصة بك أكثر توسعًا وأسهل في الحفاظ عليها عندما يكون لديك مجموعات من الفصول الدراسية المماثلة. أنا مبرمج صغير ، لذلك لست خبيراً ، لكني انتهيت للتو من مشروع يتطلب شيئاً مماثلاً.

أعمل على برنامج العميل الذي يتحدث إلى خادم يقوم بتشغيل جهاز طبي. نحن نعمل على تطوير إصدار جديد من هذا الجهاز يحتوي على بعض المكونات الجديدة التي يجب على العميل تكوينها في بعض الأحيان. هناك نوعان من المكونات الجديدة ، وهما مختلفان ، لكنهما متشابهان للغاية أيضًا. في الأساس ، كان علي إنشاء نموذجين للتهيئة ، فئتين من القوائم ، واثنين من كل شيء.

لقد قررت أنه من الأفضل إنشاء فئة أساسية مجردة لكل نوع تحكم من شأنه أن يحمل كل المنطق الحقيقي تقريبًا ، ومن ثم الأنواع المشتقة للاعتناء بالاختلافات بين العنصرين. ومع ذلك ، لم تكن الطبقات الأساسية قادرة على القيام بعمليات على هذه المكونات إذا كان عليّ أن أقلق حول أنواع الوقت (حسناً ، يمكن أن يكون لديهم ، ولكن كان من الممكن أن يكون هناك عبارة "إذا" أو التبديل في كل طريقة) .

قمت بتعريف واجهة بسيطة لهذه المكونات وتتحدث جميع الطبقات الأساسية إلى هذه الواجهة. الآن عندما أقوم بتغيير شيء ما ، فإنه "يعمل فقط" إلى حد كبير في كل مكان وليس لدي أي تكرار للرمز.


البرمجة على واجهة لا علاقة لها على الإطلاق مع واجهات مجردة كما نرى في جافا أو .NET. انها ليست حتى مفهوم OOP.

ما يعنيه حقًا هو عدم الخوض في الدوام مع عناصر كائن أو بنية بيانات. استخدم واجهة Program Abstract أو API للتفاعل مع بياناتك. في Java أو C # يعني استخدام الخصائص والأساليب العامة بدلاً من الوصول إلى الحقل الخام. بالنسبة إلى C ، يعني استخدام الوظائف بدلاً من المؤشرات الأولية.

تحرير: ومع قواعد البيانات فهذا يعني استخدام طرق العرض والإجراءات المخزنة بدلاً من الوصول المباشر للجدول.


إذا قمت بالبرمجة في Java ، فإن JDBC مثال جيد. يحدد JDBC مجموعة من الواجهات ولكنه لا يذكر شيئًا عن التنفيذ. يمكن كتابة التطبيقات الخاصة بك ضد هذه المجموعة من الواجهات. من الناحية النظرية ، يمكنك اختيار بعض السائق JDBC وسوف تعمل التطبيق الخاص بك فقط. إذا اكتشفت وجود برنامج تشغيل JDBC أسرع أو "أفضل" أو أرخص ، أو لأي سبب من الأسباب ، يمكنك مرة أخرى من الناحية النظرية إعادة تهيئة ملف خاصية الخاص بك ، ودون الحاجة إلى إجراء أي تغيير في التطبيق الخاص بك ، ستظل تعمل التطبيق الخاص بك.


This هو التفسير الأكثر بساطة حول حاوية التبعية وحقن حقن التبعية التي رأيتها على الإطلاق:

بدون حقن التبعية

  • يحتاج التطبيق إلى Foo (على سبيل المثال وحدة التحكم) ، لذلك:
  • تطبيق يخلق فو
  • تطبيق يدعو Foo
    • يحتاج Foo إلى Bar (على سبيل المثال خدمة) ، لذلك:
    • فو يخلق بار
    • فو يدعو شريط
      • يحتاج شريط Bim (خدمة ، مستودع ، ...) ، لذلك:
      • شريط يخلق بيم
      • شريط يفعل شيئا

مع Dependency Injection

  • يحتاج تطبيق Foo ، الذي يحتاج إلى Bar ، الذي يحتاج إلى Bim ، لذلك:
  • يخلق التطبيق بيم
  • تطبيق يخلق شريط ويعطيها بيم
  • يخلق التطبيق Foo ويعطيها شريط
  • تطبيق يدعو Foo
    • فو يدعو شريط
      • شريط يفعل شيئا

باستخدام حاوية حقن التبعية

  • يحتاج التطبيق إلى Foo حتى:
  • يحصل التطبيق على Foo من الحاوية ، لذلك:
    • حاوية يخلق بيم
    • حاوية يخلق شريط ويعطيها بيم
    • حاوية يخلق فو ويعطيه شريط
  • تطبيق يدعو Foo
    • فو يدعو شريط
      • شريط يفعل شيئا

Dependency Injection والاعتمادية حقن حاويات أشياء مختلفة:

  • Dependency Injection هي طريقة لكتابة كود أفضل
  • حاوية DI هي أداة تساعد على حقن التبعيات

لا تحتاج إلى حاوية للقيام بحقن التبعية. ومع ذلك يمكن للحاوية مساعدتك.





language-agnostic oop interface