كيفية تنفيذ السلوك المشترك بين الفئات (بدون وراثة متعددة بالطبع) في C#

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

سؤال

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

public abstract class SalaryStrategy {
    public abstract decimal Salary { get; set; }
    public abstract decimal Min { get; set; }
    public abstract decimal Mid { get; set; }
    public decimal CompaRatio {
        get {
            if (this.Mid == 0) { return 0; }
            else { return this.Salary / this.Mid; }
        }
    }
}

public class InternalCurrentSalaryStrategy {
    public override decimal Salary { get; set; }
    public override decimal Min {
        get { return this.Salary * .25m; }
        set { }
    }
    public override decimal Mid { get; set; }
}

public class Candidate {
    public int Id { get; set; }
    public string Name { get; set; }
    public SalaryStrategy CurrentSalaryStrategy { get; set; }
}

public static void Main(string[] args) {
    var internal = new Candidate();
    internal.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy();
    var internalElp = new Candidate();
    internalElp.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy();
    var elp = new Candidate();
    // elp.CurrentSalaryStrategy can stay null cause it's not used for elps
}

أي تعليقات أو اقتراحات؟


السؤال الأصلي:

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

public class Candidate {
    public int Id { get; set; }
    public string Comments { get; set; }
    // lots more properties and behaviors...
}

public class InternalCandidate : Candidate {
    public decimal CurrentMid { get; set; }
    public decimal CurrentMax {
         get { return this.CurrentMin * 1.3m;
    }
    // lots more properties and behaviors...
}

public class EntryLevelCandidate : Candidate {
    public string Gpa { get; set; }
    // lots more properties and behaviors...
}

public class InternalEntryLevelCandidate /* what do I inherit here??? */ {
    // needs all of the properties and behaviors of
    // EntryLevelCandidate but also needs the CurrentMin and
    // CurrentMax (and possibly more) in InternalCandidate
}

فئة InternalEntryLevelCandidate هي في المقام الأول فئة EntryLevelCandidate ولكنها تحتاج إلى مشاركة بعض من التطبيقات من المرشح الداخليأقول تطبيقات لأنني لا أريد أن تكون عمليات التنفيذ مختلفة أو متكررة، وإلا سأستخدم واجهة للعقود المشتركة ولدي تطبيقات ملموسة في كل فئة.يجب أن تكون بعض تطبيقات خصائص وسلوكيات InternalCandidate شائعة أو مشتركة.لقد قرأت عن مزيج C++ وRuby، والذي يبدو أنه شيء مشابه لما أريد القيام به.لقد قرأت أيضًا منشور المدونة المثير للاهتمام هذا والذي يناقش فكرة عن نوع السلوك حيث سيكون الفصل قادرًا على وراثة سلوكيات متعددة مع الحفاظ على علاقة واحدة "هي": http://www.deftflux.net/blog/post/A-good-design-for-multiple-implementation-inheritance.aspx.يبدو أن هذا ينقل ما أريد.هل يمكن لأي شخص أن يقدم لي بعض التوجيهات حول كيفية تحقيق ذلك باستخدام ممارسات التصميم الجيدة؟

هل كانت مفيدة؟

المحلول

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

طرق التمديد. يمكن تحميلها بشكل زائد للعمل مع أي فئة.

سأتجنب نمط الديكور وألتزم بالوظائف المترجمة/القابلة للعكس.

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

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

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

نصائح أخرى

وهنا ورقة علمية حول هذا الموضوع وأعتقد أن المثير للاهتمام جدا ( PDF رابط ).

ولكن، أعتقد أنك تحاول فرض <م> منطق الأعمال في التعميم الخاص بك. كنت أعلم أن InternalCandidate لن يكون بدا له GPA في. ولكن، على InternalCandidate بالتأكيد لديه GPA. إذن، لقد تصدع من هذا الرجل الغريب يسمى InternalEntryLevelCandidate لأنك أعلم أنك تريد أن ننظر إلى GPA هذا الرجل. معماريا، وأعتقد أن EntryLevelCandidate غير صحيح. وأود أن أضيف "مستوى" المفهوم لمرشح ومنحه GPA. والامر متروك لل<م> منطق الأعمال ليقرروا ما اذا كانوا ننظر إلى GPA أم لا.

وتحرير: أيضا، سكوت مايرز يقوم بعمل كبير من تشريح هذه المسألة في كتبه.

تنصل:

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

بالعودة إلى السؤال، لا يوجد حل نظيف، ولكن لديك بعض الخيارات:

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

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

  • حدد واجهة لكل سلوك واطلب من الأساليب الموجودة في القاعدة التحقق مما إذا this is IInterface قبل تنفيذ السلوك.(يسمح لك بسحب تعريفات السلوك إلى القاعدة)

بالقرب من التكرار:الوراثة المتعددة في C#

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

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

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

public class InternalEntryLevelCandidate : EntryLevelCandidate {
    private  InternalCandidate internalCandidateDelegate
        = new InternalCandidate();

    public decimal CurrentMid { 
        get { return internalCandidateDelegate.CurrentMid; }
        set { internalCandidateDelegate.CurrentMid = value; }
    }

    public decimal CurrentMax {
        get { return internalCandidateDelegate.CurrentMax }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top