design-patterns factory - كيفية شرح حقن التبعية إلى 5 سنوات من العمر؟





c# abstract (6)


أعطي لك حقن التبعية للأطفال في سن الخامسة.

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

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

ما هي طريقة جيدة لتفسير حقن التبعية ؟

لقد عثرت على العديد من البرامج التعليمية على Google ، ولكن لم يكن أي منها من المفترض أن يكون القارئ هو مبتدئ جافا. كيف تفسر هذا للمبتدئ؟




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

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

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




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

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

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




ماذا عن هذا؟

إذا كان لديك Employee فئة وله هذا الموظف Address فيمكنك تحديد فئة Employee كما يلي:

class Employee {
    private Address address;

    // constructor 
    public Employee( Address newAddress ) {
        this.address = newAddress;
    }

    public Address getAddress() {
    return this.address;
    }
    public void setAddress( Address newAddress ) {
        this.address = newAddress;
    }
}

كل شيء يبدو على ما يرام حتى الآن.

يوضح هذا الرمز وجود علاقة HAS-A بين الموظف وعنوانه ، فلا بأس.

الآن ، هذه العلاقة HAS-A خلقت التبعية بينهما. تأتي المشكلة داخل المُنشئ.

في كل مرة تريد فيها إنشاء نسخة Employee تحتاج إلى نسخة Address :

 Address someAddress = ....
 Employee oscar = new Employee( someAddress ); 

يصبح العمل بهذه الطريقة مشكلة خاصة عند الرغبة في إجراء اختبار الوحدة.

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

لتجنب هذا ، يمكنك تغيير المُنشئ كما يلي:

  public Employee(){
  }

باستخدام منشئ لا أرجس.

بعد ذلك ، يمكنك تعيين العنوان متى أردت:

 Address someAddress = ....
 Employee oscar = new Employee();
 oscar.setAddress( someAddress ); 

الآن ، قد يكون هذا عائقًا ، إذا كان لديك العديد من السمات أو إذا كان من الصعب إنشاء الكائنات.

ومع ذلك ، فكر في هذا ، دعنا نقول ، يمكنك إضافة سمة Department :

  class Employee {
      private Address address;
      private Department department;

  ....

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

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

لنفترض وجود ملف خصائص لحاقن التبعية الوهمية:

  #mock employee
  employee.address = MockAddress.class
  employee.department = MockDepartment.class

  #production setup 
  employee.address = RealAddress.class
  employee.department = RealDepartment.class

ستعرف ما الذي يجب إدخاله لسيناريو معين.

ما سيفعله إطار عمل Dependency Injector هو إعداد الكائنات الصحيحة لك ، حتى لا تضطر إلى ترميز setAddress أو setDepartment . وسيتم ذلك إما عن طريق التفكير أو عن طريق توليد الكود أو تقنيات أخرى.

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

هذا إلى حد كبير حول هذا الموضوع.

ما زلت لا أعتقد أن هذا التفسير مناسب لمدة 5 سنوات كما طلبت.

آمل أن تكون ما زلت تجده مفيدًا.




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

ولكن في مصنع نينتندو ، يحتاجون إلى معرفة كيفية وضع واحد معًا.

عندما يخرج الأشخاص الأذكياء في المصنع جهاز Nintendo DS ، سيكون الأمر مختلفًا في الداخل ، ولكنك ستظل تعرف كيفية استخدامه.




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

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

class PaymentProcessor{

    private String type;

    public PaymentProcessor(String type){
        this.type = type;
    }

    public void authorize(){
        if (type.equals(Consts.PAYPAL)){
            // Do this;
        }
        else if(type.equals(Consts.OTHER_PROCESSOR)){
            // Do that;
        }
    }
}

الآن تخيل أنك ستحتاج الآن إلى الحفاظ على كل هذا الكود في فصل واحد لأنه لا يتم فصله بشكل صحيح ، يمكنك أن تتخيل أنه بالنسبة لكل معالج جديد ستدعمه ، ستحتاج إلى إنشاء حالة جديدة لـ // switch ومع كل طريقة ، فإن هذا الأمر يصبح أكثر تعقيدًا ، وذلك باستخدام "Dependency Injection" (أو Inversion of Control - كما يطلق عليه أحيانًا ، مما يعني أن من يتحكم في تشغيل البرنامج لا يعرف إلا في وقت التشغيل ، وليس التعقيد) ، يمكنك تحقيق شيء ما أنيق جدا وصيانتها.

class PaypalProcessor implements PaymentProcessor{

    public void authorize(){
        // Do PayPal authorization
    }
}

class OtherProcessor implements PaymentProcessor{

    public void authorize(){
        // Do other processor authorization
    }
}

class PaymentFactory{

    public static PaymentProcessor create(String type){

        switch(type){
            case Consts.PAYPAL;
                return new PaypalProcessor();

            case Consts.OTHER_PROCESSOR;
                return new OtherProcessor();
        }
    }
}

interface PaymentProcessor{
    void authorize();
}

** لن يتم تجميع الرمز ، أعرف :)





design-patterns dependency-injection inversion-of-control