design-patterns شرح - كيف سأعرف متى يجب إنشاء واجهة؟





composite examples (21)


يتم استخدام الواجهات عمومًا عندما تريد تحديد سلوك يمكن أن تعرضه الكائنات.

مثال جيد على ذلك في عالم .NET هو واجهة IDisposable ، التي يتم استخدامها في أي من فئات Microsoft التي تستخدم موارد النظام التي يجب تحريرها يدويًا. يتطلب أن يكون للصنف الذي يقوم بتطبيقه أسلوب Dispose ().

(تسمى أيضاً طريقة التخلص (Dispose ()) باستخدام بنية اللغة المستخدمة لـ VB.NET و C# ، والتي لا تعمل إلا مع إمكانية IDisposable

ضع في اعتبارك أنه يمكنك التحقق مما إذا كان كائن يقوم بتنفيذ واجهة معينة باستخدام بنى مثل TypeOf ... Is (VB.NET) ، is (C #) ، instanceof (Java) ، الخ ...

أنا في مرحلة تعلم التطوير التي أشعر بها ، يجب أن أتعلم المزيد عن واجهات التعامل.

كثيرا ما أقرأ عنهم ولكن يبدو أنني لا أستطيع فهمهم.

لقد قرأت أمثلة مثل: الطبقة الأساسية للحيوانات ، مع واجهة IAnimal لأشياء مثل "المشي" ، "تشغيل" ، "GetLegs" ، وما إلى ذلك - ولكن لم أكن أعمل على شيء وشعرت مثل "يا ينبغي استخدام واجهة هنا!"

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




هناك الكثير من الأغراض لاستخدام واجهة.

  1. استخدامها في سلوك متعدد الأشكال. حيث تريد استدعاء أساليب معينة من فئة تابعة مع inteface إحالة إلى الفصل التابع.

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

  3. لتقليل استخدام الذاكرة عن طريق تحميل طرق محددة في الفصل. كما لو كان لديك ثلاثة عناصر أعمال ويتم تنفيذها في فئة واحدة ، يمكنك استخدام ثلاث واجهات.

على سبيل المثال IUser ، IOrder ، IOrderItem

public interface IUser()
{

void AddUser(string name ,string fname);

}

// Same for IOrder and IOrderItem
//


public class  BusinessLayer: IUser, IOrder, IOrderItem

{    
    public void AddUser(string name ,string fname)
    {
        // Do stuffs here.
    }

    // All methods from all interfaces must be implemented.

}

إذا كنت تريد فقط إضافة مستخدم ، ففعل هذا:

IUser user = new (IUser)BusinessLayer();

// It will load  all methods into memory which are declared in the IUser interface.

user.AddUser();



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

http://www.nmock.org/




يجب عليك تعريف الواجهة بمجرد أن تحتاج إلى فرض سلوك لصفك.

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

مثال عملي آخر هو واجهة ActionListener (أو Runnable). سوف تنفذها عندما تحتاج إلى تتبع حدث معين. لذلك ، تحتاج إلى توفير تطبيق actionPerformed(Event e) في الفصل الدراسي (أو الفئة الفرعية). وبالمثل ، بالنسبة لواجهة Runnable ، تقوم بتوفير تطبيق لأسلوب public void run() .

أيضا ، يمكن أن يكون لديك هذه الواجهات التي نفذتها أي عدد من الفئات.

مثيل آخر حيث يتم استخدام الواجهات (في Java) هو تنفيذ الوراثة المتعددة المتوفرة في C ++.




أسهل مثال لإعطاء شيء مثل معالجات الدفع (Paypal ، PDS ، الخ).

لنفترض أنك أنشأت واجهة IPaymentProcessor يحتوي على طرق ProcessACH و ProcessCreditCard.

يمكنك الآن تنفيذ تنفيذ ملموسة باي بال. جعل هذه الطرق استدعاء وظائف محددة PayPal.

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




يحل هذه المشكلة الملموسة:

لديك a ، b ، c ، d من 4 أنواع مختلفة. في جميع أنحاء رمزك لديك شيء من هذا القبيل:

a.Process();
b.Process();
c.Process();
d.Process();

لماذا لا يكون لهم تنفيذ IProcessable ، ومن ثم القيام به

List<IProcessable> list;

foreach(IProcessable p in list)
    p.Process();

سيكون هذا أفضل بكثير عندما تضيف ، على سبيل المثال ، 50 نوعا من الطبقات التي تفعل كل شيء نفس الشيء.

مشكلة أخرى ملموسة:

هل سبق لك أن ألقيت نظرة على System.Linq.Enumerable؟ وهو يحدد طن من طرق التمديد التي تعمل على أي نوع يقوم بتنفيذ IEnumerable. لأن أي شيء يقوم بتطبيق IEnumerable يقول "أنا أؤيد التكرار في نمط foreach-type غير مرتبة" ، يمكنك تعريف السلوكيات المعقدة (Count ، Max ، Where ، Select ، إلخ.) لأي نوع قابل للتعداد.




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

هناك الكثير من الأسباب الأخرى التي وجدتها لتعزيز الممارسة الجيدة لإعادة التعمير إلى واجهات ، ولكن كان اختبار الوحدة / السخرية هو ما قدم "لحظة" من الخبرة العملية.

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




إذا قمت باستعراض التجميعات .NET Framework ثم الانتقال لأسفل إلى الفئات الأساسية لأي من الكائنات القياسية ، ستلاحظ العديد من الواجهات (أعضاء تسمى ISomeName).

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

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

المثال الكلاسيكي: بدون تنفيذ واجهة IDisposable ، لا يمكنك استخدام بناء الكلمة الأساسية "using ()" في C # ، لأنها تتطلب أن أي كائن تم تحديده كمعلمة يمكن ضمه ضمنيًا إلى IDisposable.

COMPLEX EXAMPLE: مثال أكثر تعقيدًا سيكون فئة System.ComponentModel.Component. تطبق هذه الفئة على حد سواء IDisposable و IComponent. معظم ، إن لم يكن كافة الكائنات .NET التي لديها مصمم مرئي المقترنة بها تطبيق IComponent بحيث سيتم IDE قادرة على التفاعل مع المكون.

الخلاصة: عندما تصبح أكثر دراية بـ .NET Framework ، فإن أول شيء ستقوم به عندما تواجه فئة جديدة في مستعرض الكائنات أو داخل أداة .NET Reflector (المجانية) ( http://www.red-gate.com/products/reflector/ ) هو التحقق لمعرفة الفئة التي يرثها وكذلك من الواجهات التي تنفذها. يعتبر .NET Reflector أفضل من برنامج Object Browser لأنه يسمح لك بمشاهدة الفئات المشتقة كذلك. يسمح لك ذلك بالتعرف على جميع الكائنات المستمدة من فئة معينة ، ومن المحتمل أن تتعلم عن وظيفة الإطار التي لم تكن تعرف بوجودها. هذا مهم بشكل خاص عند إضافة أو تحديث مساحات أسماء جديدة إلى .NET Framework.




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

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




مثال على الكود (مزيج من أندروز مع منجم إضافي في what-is-the-purpose-of-interfaces ) ، والذي يجعل أيضًا حالة حول الواجهة بدلاً من فئة تجريدية على اللغات بدون دعم للوراثة المتعددة (c # و جافا):

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

لاحظ أن FileLogger و DataBaseLogger لا يحتاجان إلى الواجهة (يمكن أن يكونا فئة أساسية مجردة في Logger). ولكن يجب عليك استخدام مسجل من جهة خارجية يفرض عليك استخدام فئة أساسية (دعنا نقول إنه يعرض الطرق المحمية التي تحتاج إلى استخدامها). بما أن اللغة لا تدعم الميراث المتعدد فلن تتمكن من استخدام نهج الطبقة الأساسية المجردة.

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




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

هناك مقايضة هنا ، كالعادة: المرونة على الصيانة. أيهما أكثر أهمية؟ لا توجد إجابات - ذلك يعتمد على المشروع. ولكن تذكر فقط أنه يجب الحفاظ على كل برامج ...

لذلك نصيحتي: لا تستخدم الواجهات حتى تحتاج إليها حقًا. (باستخدام Visual Studio ، يمكنك استخراج واجهة من فئة موجودة في ثانيتين - لذا لا تتعجل).

بعد قولي هذا ، متى تحتاج إلى إنشاء واجهة؟

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

ويعمل: س)

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

PS: عندما أكتب "واجهة" ، أعني: "واجهة أي فئة أساسية" ، بما في ذلك الطبقات واجهة نقية. لاحظ أن الطبقات التجريبية غالبًا ما تكون رهانًا أفضل ثم واجهات نقية نظرًا لأنه يمكنك إضافة منطق إليها.

التحيات ، سيلفان.




لقد استخدمت الواجهات بين الحين والآخر ، وهنا أحدث استخدام لي (تم تعميم الأسماء):

لدي مجموعة من عناصر التحكم المخصصة على WinForm التي تحتاج إلى حفظ البيانات إلى كائن العمل الخاص بي. أحد الأساليب هو استدعاء كل عنصر تحكم بشكل منفصل:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

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

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

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

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

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




استخدم الواجهات عندما تختلف عمليات تنفيذ نفس الوظيفة.

استخدم فئات مجردة / أساسية عندما تحتاج إلى مشاركة تنفيذ ملموس مشترك.




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

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

لماذا هذا مفيد؟ قد ترغب في بناء وظيفة تعتمد على حقيقة أنك يجب أن تكون قادرًا على الاتصال بـ Run and Walk ، على سبيل المثال ، على الكائن. يمكن أن يكون لديك ما يلي:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

... وأكرر أنه لجميع الأشياء التي تعرفها يمكن تشغيلها والمشي. ومع ذلك ، باستخدام واجهة IAnimal الخاصة بك ، يمكنك تعريف الدالة مرة واحدة كما يلي:

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

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

هناك أيضا مناقشة جيدة في هذا السؤال ذي الصلة .




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

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

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

أوصي بقراءة الفصل الخاص بالواجهات في تصميم جافا بواسطة Coad ، Mayfield ، و Kern. يشرحونها أفضل قليلاً من النص التمهيدي المتوسط. إذا كنت لا تستخدم جافا ، يمكنك فقط قراءة بداية الفصل ، والذي هو مجرد مفاهيم في المقام الأول.




افترض أنك تريد أن تطرح مضايقات نموذجية قد تحدث عند محاولة النوم.

نموذج قبل واجهات

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

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

استخدام الطبقات دون واجهات

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

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

نموذج مع واجهات

للتغلب على هذا البروتيم ، يمكننا إدخال iterface

interface Annoying{
   public void annoy();

}

وتنفيذها داخل الطبقات

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

الاستخدام مع واجهات

مما سيجعل استخدام هذه الفئات أسهل بكثير

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}



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

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

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

انظر أيضا هذا السؤال ذات الصلة حول الترميز لواجهة .




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




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

الآن استبدال IWalkable مع شيء أكثر برمجيات y مثل IPersistable ، ويصبح التشابه أقرب إلى ما تراه في برنامج حقيقي.




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

يمكننا الحصول على واجهة Gun التي تحدد shoot() وظيفة shoot() .

نحن بحاجة إلى فئات فرعية مختلفة من فئة Gun وهي ShotGun Sniper وهلم جرا.

ShotGun implements Gun{
    public void shoot(){
       \\shotgun implementation of shoot.
    } 
}

Sniper implements Gun{
    public void shoot(){
       \\sniper implementation of shoot.
    } 
}

فئة مطلق النار

مطلق النار لديه كل المدافع في درعه. يتيح إنشاء List لتمثيله.

List<Gun> listOfGuns = new ArrayList<Gun>();

يدور مطلق النار من خلال switchGun() ، عند الحاجة ، باستخدام وظيفة switchGun()

public void switchGun(){
    //code to cycle through the guns from the list of guns.
    currentGun = //the next gun in the list.
}

يمكننا ضبط Gun الحالي ، باستخدام الوظيفة المذكورة أعلاه و ببساطة استدعاء الدالة shoot() ، عندما يتم استدعاء fire() .

public void fire(){
    currentGun.shoot();
}

يختلف سلوك وظيفة التصوير وفقًا للتطبيقات المختلفة لواجهة Gun .

استنتاج

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

على سبيل المثال fire() وظيفة من الطبقة Shooter تتوقع البنادق ( Sniper ، ShotGun ) لتنفيذ وظيفة shoot() . لذلك إذا قمنا بتبديل البندقية والنار.

shooter.switchGun();
shooter.fire();

لقد قمنا بتغيير سلوك fire() وظيفة.




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.





design-patterns oop interface class-design