سؤال

ما رأيك في قرار التصميم هذا؟ ما المزايا التي لديها وما عيوب؟

الروابط:

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

المحلول

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

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

(بالنسبة لأي قراء مطلعون على TCL، شعرت أن SNIT لتكون مباراة أوثق قليلا مع "الشعور" باللغة [incr Tcl] كنت. TCL هو كل شيء عن الوفد، على الأقل في طريقي في التفكير.)

نصائح أخرى

ال عصابة 4.مبدأ حاسم هو "تفضيل التركيب للميراث"؛ اذهب يصنع أنت تتبع ذلك ؛-).

الاستخدامات الحقيقية الوحيدة للميراث هي:

  • تعدد الأشكال

    • Go's Interface's "Static Duck Constring" System يحل هذه المشكلة
  • استعارة التنفيذ من فئة أخرى

    • هذا هو ما تضمين

لن يكون نهج الذهاب خريطة 1 إلى-1 بالضبط، والنظر في هذا المثال الكلاسيكي للميراث والشلل الخلوي في جافا (بناء على هذا):

//roughly in Java (omitting lots of irrelevant details)
//WARNING: don't use at all, not even as a test

abstract class BankAccount
{
    int balance; //in cents
    void Deposit(int money)
    {
        balance += money;
    }

    void withdraw(int money)
    {
        if(money > maxAllowedWithdrawl())
            throw new NotEnoughMoneyException();
        balance -= money;
    }

    abstract int maxAllowedWithdrawl();
}

class Account extends BankAccount
{
    int maxAllowedWithdrawl()
    {
        return balance;
    }
}

class OverdraftAccount extends BankAccount
{
    int overdraft; //amount of negative money allowed

    int maxAllowedWithdrawl()
    {
        return balance + overdraft;
    }
}

هنا، يتم دمج الميراث والشامعة الجنسية، ولا يمكنك ترجمة هذا للذهاب دون تغيير الهيكل الأساسي.

لم أكن تميل بعمق في الذهاب، لكنني أفترض أنه سيبدو شيئا مثل هذا:

//roughly Go? .... no?
//for illustrative purposes only; not likely to compile
//
//WARNING: This is totally wrong; it's programming Java in Go

type Account interface {
    AddToBalance(int)
    MaxWithdraw() int
}

func Deposit(account Account, amount int) {
    account.AddToBalance(amount)
}

func Withdraw(account Account, amount int) error {
    if account.MaxWithdraw() < amount {
        return errors.New("Overdraft!")
    }
    account.AddToBalance(-amount)
    return nil
}

type BankAccount {
    balance int
}

func (account *BankAccount) AddToBalance(amount int) {
    account.balance += amount;
}

type RegularAccount {
    *BankAccount
}

func (account *RegularAccount) MaxWithdraw() int {
    return account.balance //assuming it's allowed
}

type OverdraftAccount {
    *BankAccount
    overdraft int
}

func (account *OverdraftAccount) MaxWithdraw() int {
    return account.balance + account.overdraft
}

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

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

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

إليك مقال يحلف OOP مع الذهاب أكثر قليلا: http://nathany.com/good.

أنا الآن أتعلم الآن عن الذهاب، ولكن نظرا لأنك تطلب رأي، سأقدم واحدة بناء على ما أعرفه حتى الآن. يبدو أن التضمين نموذجيا للعديد من الأشياء الأخرى أثناء التنقل، وهو دعم لغة صريحة لأفضل الممارسات التي يتم بالفعل القيام بها بالفعل في اللغات الحالية. على سبيل المثال، كما لاحظ أليكس مارتلي، فإن عصابة 4 يقول "تفضل التكوين للميراث". لا يزيل الميراث فقط، ولكن يجعل التكوين أسهل وأقوى من في C ++ / Java / C #.

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

طلب الناس روابط للحصول على معلومات حول التضمين في الذهاب.

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

http://golang.org/doc/effective_go.html#embedding.

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

لمزيد من المعلومات حول الهياكل، يمكنك رؤية مواصفات اللغة GO، والتي تذكر صراحة أعضاء الهياكل المسجلة بشكل صريح لأنواع مضمنة:

http://golang.org/ref/spec#sult_types.

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

https://github.com/ecashin/go-getting/blob/master/bpaxos.go#l30.

احب ذلك.

تؤثر اللغة التي تستخدمها أنماط تفكيرك. (فقط اسأل مبرمج C لتنفيذ "عدد الكلمات". من المحتمل أن يستخدمون قائمة مرتبطة، ثم قم بالتبديل إلى شجرة ثنائية للأداء. ولكن كل مبرمج Java / Ruby / Python سيستخدم القاموس / التجزئة. أدمغت كثيرا أنهم لا يستطيعون التفكير في استخدام أي بنية بيانات أخرى.)

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

عند الذهاب، يمكنك "نموذج" فصولك بهذه الطريقة (مع واجهات). لكنك لا تفعل (لا تستطيع) رمز بهذه الطريقة.

بدلا من ذلك، يمكنك استخدام التضمين. يمكن تقسيم التعليمات البرمجية الخاصة بك إلى وحدات صغيرة معزولة، ولكل منها بياناتها الخاصة. هذا يجعل إعادة استخدام تافهة. هذا الوضع لديه القليل من القيام به مع كائنات "الكبار". (أي في الذهاب، يمكنك كتابة طريقة "الدجال ()" لا يعرف حتى عن فئة البط الخاصة بك. ولكن في لغة Oop نموذجية، لا يمكنك إعلان "تطبيق Duck.quack () لا يحتوي على تبعيات أي طرق أخرى من البط. ")

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top