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




الفرق بين abstract & interface (14)

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

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


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

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


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


إذا كنت تنظر إلى جافا بلغة 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؟


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


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

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

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


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


سنتى:

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

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

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

مثال (بدائية جداً!): ضع في الاعتبار فئة أساسية تسمى "العميل" والتي تحتوي على طرق مجردة مثل CalculatePayment() و CalculateRewardPoints() وبعض الطرق غير المجردة مثل GetName() و SavePaymentDetails() .

سوف ترث الفئات المتخصصة مثل RegularCustomer و GoldCustomer من الفئة الأساسية Customer وتنفيذ منطق الأسلوب CalculateRewardPoints() الخاص به و CalculateRewardPoints() ولكن إعادة استخدام أساليب GetName() و SavePaymentDetails() .

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

فئة مجردة مع جميع أعضاء مجردة ستكون مشابهة لواجهة.


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

في معظم الأحيان أرى أن الطبقات المجردة تستخدم (mis) ، وذلك لأن مؤلف الطبقة المجردة يستخدم نمط "أسلوب القالب".

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

(مبسط بشكل مفرط) مثال:

abstract class QuickSorter
{
    public void Sort(object[] items)
    {
        // implementation code that somewhere along the way calls:
        bool less = compare(x,y);
        // ... more implementation code
    }
    abstract bool compare(object lhs, object rhs);
}

لذلك ، كتب مؤلف هذا الفصل خوارزمية عامة ويعتزم الناس استخدامها من خلال "التخصص" من خلال توفير "خطاطيف" خاصة بهم - في هذه الحالة ، طريقة "مقارنة".

إذن الاستخدام المقصود هو شيء من هذا القبيل:

class NameSorter : QuickSorter
{
    public bool compare(object lhs, object rhs)
    {
        // etc.
    }
}

المشكلة في هذا هي أنك اقتربت بشكل غير ملائم بين مفهومين:

  1. طريقة للمقارنة بين عنصرين (ما هو العنصر الذي يجب أن يذهب أولاً)
  2. طريقة فرز العناصر (مثل الفرز السريع مقابل دمج الفرز وما إلى ذلك)

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

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

الطريقة البديلة هي استخدام نمط تصميم "الإستراتيجية" بدلاً من ذلك:

interface IComparator
{
    bool compare(object lhs, object rhs);
}

class QuickSorter
{
    private readonly IComparator comparator;
    public QuickSorter(IComparator comparator)
    {
        this.comparator = comparator;
    }

    public void Sort(object[] items)
    {
        // usual code but call comparator.Compare();
    }
}

class NameComparator : IComparator
{
    bool compare(object lhs, object rhs)
    {
        // same code as before;
    }
}

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

من أجل "إخفاء" حقيقة أننا قمنا بتنفيذ "فرز الأسماء" باستخدام فئة "QuickSort" و "NameComparator" ، قد لا نزال نكتب طريقة مصنع في مكان ما:

ISorter CreateNameSorter()
{
    return new QuickSorter(new NameComparator());
}

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

فكرة أخيرة: كل ما قمنا به أعلاه هو "إنشاء" وظيفة "NameSorting" باستخدام وظيفة "QuickSort" ووظيفة "NameComparison" ... في لغة برمجة عملية ، يصبح هذا النمط من البرمجة أكثر طبيعية ، مع رمز أقل.


فئة مجردة يمكن أن يكون لها تطبيقات.

واجهة ليس لديها تطبيقات ، فهي ببساطة تحدد نوع العقد.

يمكن أن يكون هناك أيضًا بعض الاختلافات التي تعتمد على اللغة: على سبيل المثال ، لا يحتوي C # على ميراث متعدد ، ولكن يمكن تنفيذ واجهات متعددة في فصل دراسي.


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

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

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

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

Source


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

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


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

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

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

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



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

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





abstract-class