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



Answers

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

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

Question

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

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

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

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




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

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

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

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




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




أنا أحب الجيش التشبيه.

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

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

القدرة على التصرف مثل الجنود تسمى تعدد الأشكال.

واجهات هي تركيبات البرمجيات التي تساعد على تحقيق تعدد الأشكال.

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

Polymorphism ، الذي يعني اشتقاقاً "العديد من النماذج" ، هو القدرة على معالجة كائن من أي فئة فرعية من فئة أساسية كما لو كان كائنًا من الفئة الأساسية. لذلك ، تشتمل فئة أساسية على العديد من النماذج: الفئة الأساسية نفسها ، وأي فئة من الفئات الفرعية الخاصة بها.

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




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

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

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

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




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

لذلك في مثال 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 صالحًا طالما أنهم يحققون ذلك الاتفاق. إنه يعمل بشكل جيد دون معرفة أي شيء آخر الطبقة ".

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




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

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



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

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

  1. في نموذج المكوِّن الإضافي ، فإننا نحمّل المكونات الإضافية عن طريق الواجهة ونقدم هذه الواجهة للكتاب الإضافيين للتوافق معها.

  2. في نظام المراسلة بين اللغات ، تقوم جميع فئات الرسائل بتطبيق واجهة محددة وهي "غير ملفوفة" باستخدام الواجهة.

  3. يحدد نظام إدارة التكوين لدينا واجهة تستخدم لتعيين إعدادات التكوين واستردادها.

  4. لدينا واجهة واحدة نستخدمها لتجنب مشكلة مرجعية دائرية سيئة. (لا تفعل هذا إذا لم يكن لديك.)

أعتقد أنه إذا كانت هناك قاعدة ، فيمكنك استخدام الواجهات عندما تريد تجميع عدة فئات داخل علاقة is-a ، لكنك لا تريد توفير أي تطبيق في الفئة الأساسية.




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

لدي مجموعة من عناصر التحكم المخصصة على 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. (تعطيك الطبقات الأساسية هذه الميزة أيضًا.)

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




إذا قمت باستعراض التجميعات .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.




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

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




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

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

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

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




Related