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




oop interface (25)

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

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

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

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

IInterface classRef = new ObjectWhatever()

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

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


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

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

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


Interface is like contract where you want your implementation class to implement methods written in contract(Interface).Since java does not provide multiple inheritance,programming to interface is a good way to achieve purpose of multiple inheritance.If you have a class A that is already extending some other class B but you want that class A should also follow certain guidelines or implement certain contract then you can do so by programming to interface strategy.


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


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.


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

// 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());
    }
}

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

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

IInterface classRef = new ObjectWhatever();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

على سبيل المثال - لنفترض أن لديك لعبة SIM ولديك الصفوف التالية:

 class HouseFly inherits Insect {
   void FlyAroundYourHead(){}
   void LandOnThings(){}
 }

 class Telemarketer inherits Person {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}
 }

من الواضح أن هذين الجسمين لا يشتركان في شيء فيما يتعلق بالميراث المباشر. ولكن ، يمكنك القول أنها مزعجة.

لنفترض أن لعبتنا تحتاج إلى شيء عشوائي يزعج لاعب اللعبة عندما يتناول وجبة العشاء. هذا يمكن أن يكون HouseFly أو Telemarketer أو كليهما - ولكن كيف تسمح HouseFly بوظيفة واحدة؟ وكيف تطلب من كل نوع مختلف من الأشياء "القيام بشيء مزعج" بنفس الطريقة؟

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

 interface IPest {
    void BeAnnoying();
 }

 class HouseFly inherits Insect implements IPest {
   void FlyAroundYourHead(){}
   void LandOnThings(){}

   void BeAnnoying() {
     FlyAroundYourHead();
     LandOnThings();
   }
 }

 class Telemarketer inherits Person implements IPest {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}

   void BeAnnoying() {
      CallDuringDinner();
      ContinueTalkingWhenYouSayNo();
   }
 }

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

 class DiningRoom {

   DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

   void ServeDinner() {
     when diningPeople are eating,

       foreach pest in pests
         pest.BeAnnoying();
   }
 }

هنا لدينا غرفة الطعام التي تقبل عددا من داينرز وعدد من الآفات - لاحظ استخدام الواجهة. هذا يعني أنه في عالمنا الصغير ، يمكن لعضو في مجموعة pests أن يكون بالفعل كائن Telemarketer أو كائن HouseFly .

يتم ServeDinner طريقة ServeDinner عند تقديم وجبة العشاء ServeDinner يقوم ServeDinner لدينا بتناول الطعام. في لعبتنا الصغيرة ، عندما تقوم IPest كل آفة أن تكون مزعجة عن طريق واجهة IPest . وبهذه الطريقة ، يمكننا بسهولة أن يكون كل من Telemarketers و HouseFlys مزعجين في كل من الطرق الخاصة بهم - نحن نهتم فقط أن لدينا شيء في DiningRoom الكائن الذي هو الآفة ، نحن لا نهتم حقا ما هو ويمكنهم ليس لديهم شيء مشترك مع الآخرين.

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


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


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.


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

//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); 

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).


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

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

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


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

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


رمز إلى واجهة ليس لدى التنفيذ NOTHING القيام به مع جافا ، ولا بناء واجهة.

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

تم إنشاء بنية واجهة جافا للمساعدة في هذه الفكرة (من بين أشياء أخرى) ، وأصبح الناس أكثر تركيزًا على البناء كمركز للمعنى وليس الهدف الأصلي. ومع ذلك ، هذا هو السبب في أن لدينا أساليب وسمات عامة وخاصة في Java و C ++ و C # ، وما إلى ذلك.

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

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

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


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


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

وذكر ، في ص. 18:

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

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

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

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

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

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


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.


تخيل أن لديك منتجًا يسمى "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.


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

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


المثال المحدد الذي اعتدت أن أقدمه للطلاب هو أنهم يجب أن يكتبوا

List myList = new ArrayList(); // programming to the List interface

بدلا من

ArrayList myList = new ArrayList(); // this is bad

هذه تبدو نفسها بالضبط في برنامج قصير ، ولكن إذا ذهبت إلى استخدام myList 100 مرة في البرنامج الخاص بك ، يمكنك البدء في رؤية الفرق. يضمن الإعلان الأول أن تقوم فقط باستدعاء الطرق الموجودة على myList التي تم تعريفها بواسطة واجهة List (لذلك لا توجد أساليب محددة من ArrayList ). إذا كنت مبرمجًا على الواجهة بهذه الطريقة ، في وقت لاحق يمكنك أن تقرر أنك تحتاج بالفعل

List myList = new TreeList();

وعليك فقط تغيير شفرتك في تلك النقطة. أنت تعلم بالفعل أن بقية شفرتك لا تفعل أي شيء يمكن كسره عن طريق تغيير التطبيق لأنك مبرمج على الواجهة .

الفوائد أكثر وضوحا (على ما أظن) عندما كنت تتحدث عن معلمات الأسلوب وقيم العودة. خذ هذا على سبيل المثال:

public ArrayList doSomething(HashMap map);

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

public List doSomething(Map map);

لا يهم الآن نوع List التي ترجع إليها ، أو نوع Map يتم تمريرها كمعلمة. لن تجبر التغييرات التي تجريها داخل طريقة doSomething على تغيير رمز الاتصال.


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

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

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

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


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

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

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

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


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.


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

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

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

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





language-agnostic oop interface