وجوه المنحى أفضل الممارسات - الميراث v v تكوين واجهات

StackOverflow https://stackoverflow.com/questions/216523

  •  03-07-2019
  •  | 
  •  

سؤال

أريد أن أسأل سؤال حول كيف سيكون نهج بسيط تصميم وجوه المنحى المشكلة.لدي بعض الأفكار الخاصة عن ما أفضل طريقة لمعالجة هذا السيناريو ، ولكن أود أن تكون مهتمة في السمع بعض الآراء من تجاوز سعة مكدس المجتمع.روابط ذات صلة مقالات على الانترنت هي أيضا موضع تقدير.أنا باستخدام C#, ولكن المسألة ليست لغة معينة.

لنفترض أنا أكتب فيديو مخزن التطبيق الذي يحتوي لقاعدة البيانات Person الجدول ، PersonId, Name, DateOfBirth و Address المجالات.كما أن لديها Staff الجدول الذي يحتوي على رابط PersonId, ، Customer الجدول الذي يربط أيضا إلى PersonId.

كائن بسيط النهج الموجه يمكن أن نقول أن Customer "هو" Person وبالتالي إنشاء فصول قليلا مثل هذا:

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer : Person {
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff : Person {
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}

الآن يمكننا كتابة دالة أقول أن ترسل رسائل البريد الإلكتروني لجميع العملاء:

static void SendEmailToCustomers(IEnumerable<Person> everyone) { 
    foreach(Person p in everyone)
        if(p is Customer)
            SendEmail(p);
}

هذا النظام يعمل بشكل جيد حتى نحصل على شخص كل من العملاء و الموظفين.على افتراض أننا لا نريد everyone قائمة أن يكون نفس الشخص مرتين ، مرة واحدة كما Customer و مرة Staff, هل نجعل التعسفي الاختيار بين:

class StaffCustomer : Customer { ...

و

class StaffCustomer : Staff { ...

من الواضح الأولى فقط من هذين لن كسر SendEmailToCustomers وظيفة.

ماذا يمكنك أن تفعل ؟

  • جعل Person الدرجة اختياري مراجع StaffDetails و CustomerDetails الفصل ؟
  • إنشاء فئة جديدة الوارد Person, بالإضافة إلى اختياري StaffDetails و CustomerDetails?
  • جعل كل شيء واجهة (مثل IPerson, IStaff, ICustomer) و إنشاء ثلاث فئات بتنفيذ واجهات?
  • تأخذ آخر مختلف تماما النهج ؟
هل كانت مفيدة؟

المحلول

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

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

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

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

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

نصائح أخرى

قد ترغب في التفكير في استخدام أنماط الحزب والمساءلة

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

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

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

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

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

اسمحوا لي أن أعرف إذا فهمت إجابة Foredecker بشكل صحيح.هذا هو الكود الخاص بي (في Python؛آسف، لا أعرف C#).الاختلاف الوحيد هو أنني لن أقوم بإخطار شيء ما إذا كان الشخص "عميلًا"، وسأفعل ذلك إذا كان أحد أدواره "مهتمًا" بهذا الشيء.هل هذا مرن بما فيه الكفاية؟

# --------- PERSON ----------------

class Person:
    def __init__(self, personId, name, dateOfBirth, address):
        self.personId = personId
        self.name = name
        self.dateOfBirth = dateOfBirth
        self.address = address
        self.roles = []

    def addRole(self, role):
        self.roles.append(role)

    def interestedIn(self, subject):
        for role in self.roles:
            if role.interestedIn(subject):
                return True
        return False

    def sendEmail(self, email):
        # send the email
        print "Sent email to", self.name

# --------- ROLE ----------------

NEW_DVDS = 1
NEW_SCHEDULE = 2

class Role:
    def __init__(self):
        self.interests = []

    def interestedIn(self, subject):
        return subject in self.interests

class CustomerRole(Role):
    def __init__(self, customerId, joinedDate):
        self.customerId = customerId
        self.joinedDate = joinedDate
        self.interests.append(NEW_DVDS)

class StaffRole(Role):
    def __init__(self, staffId, jobTitle):
        self.staffId = staffId
        self.jobTitle = jobTitle
        self.interests.append(NEW_SCHEDULE)

# --------- NOTIFY STUFF ----------------

def notifyNewDVDs(emailWithTitles):
    for person in persons:
        if person.interestedIn(NEW_DVDS):
            person.sendEmail(emailWithTitles)

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

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

ما الخطأ في إرسال بريد إلكتروني إلى عميل هو أحد أعضاء فريق العمل؟إذا كان عميلاً، فيمكن إرسال البريد الإلكتروني إليه.هل أنا مخطئ في التفكير بذلك؟ولماذا يجب أن تأخذ "الجميع" كقائمة بريدك الإلكتروني؟هل من الأفضل أن يكون لدينا قائمة عملاء لأننا نتعامل مع طريقة "sendEmailToCustomer" وليس طريقة "sendEmailToEveryone"؟حتى إذا كنت تريد استخدام قائمة "الجميع"، فلا يمكنك السماح بالتكرارات في تلك القائمة.

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

فصولك هي مجرد هياكل بيانات:لا أحد منهم لديه أي سلوك، فقط المستوطنون والمستوطنون.الميراث غير مناسب هنا.

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

هنا بعض النصائح:من فئة "لا تفكر حتى في القيام بذلك" وإليك بعض الأمثلة السيئة من التعليمات البرمجية التي واجهتها:

الباحث الأسلوب بإرجاع كائن

المشكلة:اعتمادا على عدد من الحوادث وجد الباحث الأسلوب بإرجاع رقم يمثل عدد من الحوادث – أو!إذا وجدت واحدة فقط يعود الكائن الفعلي.

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

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

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

الحل:بعد هذا على يدي سأعود مجموعة من طول 1(واحد) إذا كان واحد فقط من وقوع وجدت مجموعة مع طول >1 إذا كان أكثر الحوادث وجدت.علاوة على ذلك, العثور على أي تواجد في كل ما من شأنه العودة فارغة أو مجموعة من طول 0 اعتمادا على التطبيق.

برمجة واجهة استخدام أنواع الإرجاع طردي

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

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

الطبقات مع أكثر من 1000 خطوط الخطر الكامنة الأساليب مع أكثر من 100 خطوط الخطر الكامنة أيضا!

المشكلة:بعض المطورين الاشياء الكثير من الوظائف في صف واحد/طريقة, كونه كسول جدا لكسر وظيفة – وهذا يؤدي إلى انخفاض التماسك وربما إلى ارتفاع اقتران – معكوس مبدأ مهم جدا في OOP!الحل:تجنب استخدام الكثير من الداخلية/الطبقات المتداخلة – هذه الطبقات تستخدم إلا في ضرورة الاساس ليس عليك أن تفعل عادة باستخدام لهم!استخدامها يمكن أن يؤدي إلى المزيد من المشاكل مثل الحد من الميراث.للاطلاع على رمز مكررة!نفس أو مماثلة أيضا رمز يمكن أن توجد بالفعل في بعض supertype التنفيذ أو ربما في آخر الصف.إذا كان في آخر الصف وهو ليس supertype لك انتهكت أيضا تماسك القاعدة.احترس من أساليب ثابتة – ربما كنت بحاجة إلى المساعدة الطبقة لإضافة!
المزيد في:http://centraladvisor.com/it/oop-what-are-the-best-practices-in-oop

ربما لا ترغب في استخدام الميراث لهذا الغرض.جرب هذا بدلاً من ذلك:

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer{
    public Person PersonInfo;
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff {
    public Person PersonInfo;
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top