oop الفرق - متى تستخدم واجهة بدلاً من صف مجردة والعكس بالعكس؟




بين شرح (18)

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

متى يريد المرء استخدام واجهة ومتى يريد المرء استخدام فئة مجردة ؟


Answers

هذا يمكن أن يكون دعوة صعبة للغاية لجعل ...

مؤشر واحد يمكنني تقديمه: يمكن للعنصر تنفيذ العديد من الواجهات ، في حين أن كائن ما يمكن أن يرث طبقة أساسية واحدة فقط (بلغة OO الحديثة مثل c # ، أعرف أن C ++ لها ميراث متعدد - لكن أليس هذا مستاءً؟)


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

2. إذا كنت تقوم بإنشاء شيء للكائنات التي ترتبط ارتباطًا وثيقًا في التسلسل الهرمي ، استخدم فئة مجردة.


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

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


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


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

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

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

تم النسخ من:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx


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


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


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


فكر في استخدام فئات الملخصات إذا كانت أي من هذه العبارات تنطبق على موقفك:

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

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

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

Source


أعتقد أن الطريقة الأكثر إيجازًا لوضعها هي ما يلي:

الخصائص المشتركة => فئة مجردة.
الوظيفة المشتركة => الواجهة.

ووضعها بإيجاز أقل ...

مثال عن فئة الملخص:

public abstract class BaseAnimal
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }
}

public class Dog : BaseAnimal
{
    public Dog() : base(4) { }
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }
}

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

public static int CountAllLegs(List<BaseAnimal> animals)
{
    int legCount = 0;
    foreach (BaseAnimal animal in animals)
    {
        legCount += animal.NumberOfLegs;
    }
    return legCount;
}

مثال على واجهة:

public interface IMakeSound
{
    void MakeSound();
}

public class Car : IMakeSound
{
    public void MakeSound() => Console.WriteLine("Vroom!");
}

public class Vuvuzela : IMakeSound
{
    public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
}

لاحظ هنا أن Vuvuzelas and Cars هما شيئان مختلفان تمامًا ، ولكنهما يتمتعان بوظيفة مشتركة: إجراء صوت. وبالتالي ، فإن الواجهة منطقية هنا. علاوة على ذلك ، سيسمح للمبرمجين بتجميع أشياء تجعل الأصوات معًا تحت واجهة مشتركة - IMakeSound في هذه الحالة. باستخدام هذا التصميم ، يمكنك كتابة التعليمة البرمجية التالية:

List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());

foreach (IMakeSound soundMaker in soundMakers)
{
    soundMaker.MakeSound();
}

هل يمكن أن تخبر ماذا سينتج؟

أخيرا ، يمكنك الجمع بين الاثنين.

المثال المشترك:

public interface IMakeSound
{
    void MakeSound();
}

public abstract class BaseAnimal : IMakeSound
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }

    public abstract void MakeSound();
}

public class Cat : BaseAnimal
{
    public Cat() : base(4) { }

    public override void MakeSound() => Console.WriteLine("Meow!");
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }

    public override void MakeSound() => Console.WriteLine("Hello, world!");
}

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

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


حسناً ، بعد أن قمت بـ "grokked" هذا بنفسي - هنا هي شروط الشخص العادي (لا تتردد في تصحيحي إذا كنت مخطئًا) - أعلم أن هذا الموضوع هو أمر مغرور ، لكن شخصًا آخر قد يتعثر عبره يومًا ما ...

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

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

لماذا استخدام واحد على الآخر؟ الأول يسمح بتعريف أكثر واقعية للأحفاد - وهذا الأخير يسمح لتعدد الأشكال أكبر. هذه النقطة الأخيرة مهمة للمستخدم النهائي / المبرمج ، الذي يمكنه استخدام هذه المعلومات لتنفيذ AP I (nterface) في مجموعة متنوعة من الأشكال / الأشكال لتناسب احتياجاتهم.

أعتقد أن هذه كانت لحظة "lightbulb" بالنسبة لي - فكر في واجهات أقل من منظور المؤلف وأكثر من ذلك من أي مبرمج قادم لاحقًا في السلسلة يضيف تنفيذًا إلى مشروع ، أو يوسع واجهة برمجة التطبيقات.


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

تشير الفصول في OO generaly إلى التنفيذ. أستخدم فصول دراسية مجردة عندما أريد فرض بعض تفاصيل التنفيذ على الأطفال الآخرين الذين أذهب معهم باستخدام واجهات.

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


في java ، يمكنك أن ترث من فئة واحدة (مجردة) إلى وظيفة "توفير" ، ويمكنك تنفيذ العديد من الواجهات "لضمان" وظيفة


إذا كنت تنظر إلى جافا بلغة OOP ،

" الواجهة لا توفر تطبيق الطريقة " لم تعد صالحة مع إطلاق Java 8. الآن java يوفر التطبيق في واجهة للطرق الافتراضية.

بعبارات بسيطة ، أود أن استخدم

واجهة: لتنفيذ العقد من قبل كائنات متعددة غير ذات صلة. يوفر إمكانية " HAS A ".

الفئة التجريدية: لتنفيذ السلوك نفسه أو مختلف بين كائنات متعددة ذات صلة. إنها تنشئ علاقة " IS A ".

يوفر website Oracle الاختلافات الرئيسية بين interface abstract .

فكر في استخدام دروس الملخص إذا:

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

فكر في استخدام واجهات إذا:

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

مثال:

فئة الملخص (علاقة A )

Reader هو فئة مجردة.

BufferedReader هو Reader

FileReader هو Reader

FileReader و BufferedReader للأغراض العامة: قراءة البيانات ، وترتبط من خلال فئة Reader .

واجهة (قدرة HAS )

Serializable هو واجهة.

افترض أن لديك فئتين في التطبيق الخاص بك ، والتي تقوم بتنفيذ واجهة Serializable

Employee implements Serializable

Game implements Serializable

هنا لا يمكنك إنشاء أي علاقة من خلال واجهة Serializable بين Employee Game ، والتي تهدف لأغراض مختلفة. كلاهما قادر على تسلسل الدولة وتنتهي المقارنة هناك.

إلقاء نظرة على هذه الوظائف:

كيف يمكنني شرح الفرق بين فئة Interface و Abstract؟


قاعدة الإبهام الأساسية هي: بالنسبة لـ "Nouns" ، استخدم Class Abstract و for "Verbs" use interface

على سبيل المثال: car هي فئة مجردة drive ، يمكننا أن نجعلها واجهة.


عندما تفعل ما هو شيء بسيط للغاية إذا كان لديك مفهوم واضح في ذهنك.

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

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

آمل أن أكون واضحا.


كتبت مقالا عن ذلك:

دروس مجردة وواجهات

تلخيص:

عندما نتحدث عن الفصول التجريدية ، فإننا نحدد خصائص نوع الكائن. تحديد ما هو كائن .

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


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

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





oop inheritance interface abstract-class