design-patterns - شرح - singleton design pattern




ما الفرق بين نمط تصميم Builder ونمط تصميم المصنع؟ (18)

ما هو الفرق بين نمط تصميم Builder ونمط تصميم المصنع؟

أيهما أكثر فائدة ولماذا؟

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


IMHO

منشئ هو نوع من مصنع أكثر تعقيدا.

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

لذا ، عند الحديث عن تطور "الأنماط الخلاقة" من خلال التعقيد ، يمكنك التفكير في الأمر بهذه الطريقة:

Dependency Injection Container -> Service Locator -> Builder -> Factory

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

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

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

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


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

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


باني ومصنع مجردة

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

واجهة مشتركة للمنتجات

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

من: http://www.oodesign.com/builder-pattern.html


كان أحد الاختلافات اللافتة للنظر بين شركة Builder & factory التي يمكنني إعدادها ما يلي

لنفترض أن لدينا سيارة

class Car
{
  bool HasGPS;
  bool IsCityCar;
  bool IsSportsCar;
  int   Cylenders;
  int Seats;

  public:
     void Car(bool hasGPs=false,bool IsCityCar=false,bool IsSportsCar=false, int Cylender=2, int Seats=4);
 };

في الواجهة السابقة يمكننا الحصول على السيارة بالطريقة التالية:

 int main()
 {
    BadCar = new Car(false,false,true,4,4);
  }

ولكن ماذا لو حدث بعض الاستثناء أثناء إنشاء المقاعد ؟؟؟ لن تحصل على أي شيء على الإطلاق // ولكن

افترض أن لديك تنفيذ مثل ما يلي

class Car
 {
    bool mHasGPS;
    bool mIsCityCar;
    bool mIsSportsCar;
    int mCylenders;
    int mSeats;

 public:
    void Car() : mHasGPs(false), mIsCityCar(false), mIsSportsCar(false), mCylender(2), mSeats(4) {}
    void SetGPS(bool hasGPs=false)  {mHasGPs = hasGPs;}
    void SetCity(bool CityCar)  {mIsCityCar = CityCar;}
    void SetSports(bool SportsCar)  {mIsSportsCar = SportsCar;}
    void SetCylender(int Cylender)  {mCylenders = Cylender;}    
    void SetSeats(int seat) {mSeats = seat;}    
};

 class CarBuilder 
 {
    Car* mCar;
public:
        CarBuilder():mCar(NULL) {   mCar* = new Car();  }
        ~CarBuilder()   {   if(mCar)    {   delete mCar;    }
        Car* GetCar()   {   return mCar; mCar=new Car();    }
        CarBuilder* SetSeats(int n) {   mCar->SetSeats(n); return this; }
        CarBuilder* SetCylender(int n)  {   mCar->SetCylender(n); return this;  }
        CarBuilder* SetSports(bool val) {   mCar->SetSports(val); return this;  }
        CarBuilder* SetCity(bool val)   {   mCar->SetCity(val); return this;    }
        CarBuilder* SetGPS(bool val)    {   mCar->SetGPS(val); return this; }
}

الآن يمكنك إنشاء مثل هذا

 int main()
 {
   CarBuilder* bp =new CarBuilder;
    Car* NewCar  = bp->SetSeats(4)->SetSports(4)->SetCity(ture)->SetGPS(false)->SetSports(true)->GetCar();

     bp->SetSeats(2);

     bp->SetSports(4);

     bp->SetCity(ture);

     bp->SetSports(true)

     Car* Car_II=  bp->GetCar();

  }

هنا في الحالة الثانية ، حتى إذا فشلت إحدى العمليات ، فستستمر في الحصول على السيارة.

قد تكون هذه السيارة لا تعمل بشكل جيد في وقت لاحق ولكن ، سيكون لديك الكائن.

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

على الرغم من ذلك ، فإنه يعتمد على احتياجات التوفيق الذي ينبغي للمرء أن يذهب.


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

هناك طريقتان معروفتان للسيطرة على هذا التعقيد:

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

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


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


مثال

1) باستخدام مصنع مجردة:

GUIFactory factory = new WindowsGUIFactory();
Button button = factory.createButton(); // **creates a WindowsButton**

2) باستخدام البناء:

GUIBuilder builder = new WindowsGUIBuilder();
Button button = builder.createButton(); // **creates a Button.** 
button.setOS = OSEnum.Windows;

نظرًا لعدم وجود فصل WindowsButton ، يجب أن يكون المسؤول عن البناء هو المسؤول عن إنشاء الزر بشكل صحيح ، أي: button.setOS = windows .

وهو مشابه لمقارنة TPH ضد TPT في تصميم ديسيبل.


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

يؤكد نمط Factory Abstract على إنشاء الكائنات لعائلات الكائنات ذات الصلة حيث:

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

يركز نمط البناء على بناء عنصر معقد خطوة بخطوة. ينقض التمثيل من عملية بناء الكائن المعقد ، بحيث يمكن استخدام نفس عملية البناء لتمثيل مختلف.

  • كائن منشئ يغلف تكوين الكائن المعقد.
  • يعرف كائن المدير بروتوكول استخدام Builder ، حيث يحدد البروتوكول كافة الخطوات المنطقية المطلوبة لإنشاء الكائن المعقد.

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

عينة

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace ConsoleApp_Design_Patterns
{

    class BuilderDesignPattern
    {
        static void Main(string[] args)
        {
            //create a constructor object to start building
            Kid aKid = new Kid();
            aKid.Name = "Elizabeth";

            //Elizabeth use Monkey mold to make a monkey
            Console.WriteLine("{0} start making a monkey", aKid.Name);
            AnimalBuilder builderA = new MonkeyBuilder();
            aKid.MakeAnimal(builderA);
            builderA.aAnimal.ShowMe();

            //Elizabeth use Kitten mold to make a kitten
            Console.WriteLine("{0} start making a kitten", aKid.Name);
            AnimalBuilder builderB = new KittenBuilder();
            aKid.MakeAnimal(builderB);
            builderB.aAnimal.ShowMe();

            Console.Read();
        }
    }
    public abstract class AnimalBuilder
    {
        public Animal aAnimal;

        public abstract void BuildAnimalHeader();
        public abstract void BuildAnimalBody();
        public abstract void BuildAnimalLeg();
        public abstract void BuildAnimalArm();
        public abstract void BuildAnimalTail();
    }
    public class MonkeyBuilder : AnimalBuilder
    {

        public MonkeyBuilder()
        {
            aAnimal = new Monkey();
        }

        public override void BuildAnimalHeader()
        {
            aAnimal.Head = "Moneky's Head has been built";
        }

        public override void BuildAnimalBody()
        {
            aAnimal.Body = "Moneky's Body has been built";
        }

        public override void BuildAnimalLeg()
        {
            aAnimal.Leg = "Moneky's Leg has been built";
        }

        public override void BuildAnimalArm()
        {
            aAnimal.Arm = "Moneky's Arm has been built";
        }

        public override void BuildAnimalTail()
        {
            aAnimal.Tail = "Moneky's Tail has been built";
        }
    }
    public class KittenBuilder : AnimalBuilder
    {
        public KittenBuilder()
        {
            aAnimal = new Kitten();
        }

        public override void BuildAnimalHeader()
        {
            aAnimal.Head = "Kitten's Head has been built";
        }

        public override void BuildAnimalBody()
        {
            aAnimal.Body = "Kitten's Body has been built";
        }

        public override void BuildAnimalLeg()
        {
            aAnimal.Leg = "Kitten's Leg has been built";
        }

        public override void BuildAnimalArm()
        {
            aAnimal.Arm = "Kitten's Arm has been built";
        }

        public override void BuildAnimalTail()
        {
            aAnimal.Tail = "Kitten's Tail has been built";
        }
    }
    public abstract class Animal
    {
        public string Head { get; set; }
        public string Body { get; set; }
        public string Leg { get; set; }
        public string Arm { get; set; }
        public string Tail { get; set; }


        //helper method for demo the Polymorphism, so we can 
        //easily tell what type object it is from client.
        public abstract void Eat();

        //helper method for demo the result from client
        public void ShowMe()
        {
            Console.WriteLine(Head);
            Console.WriteLine(Body);
            Console.WriteLine(Leg);
            Console.WriteLine(Arm);
            Console.WriteLine(Tail);
            Eat();

        }
    }
    public class Monkey : Animal
    {
        //helper method to show monkey's property for demo purpose
        public override void Eat()
        {
            Console.WriteLine("Since I am Monkey, I like to eat banana");
        }
    }
    public class Kitten : Animal
    {
        public override void Eat()
        {
            Console.WriteLine("Since I am Kitten, I like to eat kitten food");
        }
    }
    public class Kid
    {
        public string Name { get; set; }

        //construct process to build an animal object, 
        //after this process completed, a object 
        //will be consider as a ready to use object.
        public void MakeAnimal(AnimalBuilder aAnimalBuilder)
        {
            aAnimalBuilder.BuildAnimalHeader();
            aAnimalBuilder.BuildAnimalBody();
            aAnimalBuilder.BuildAnimalLeg();
            aAnimalBuilder.BuildAnimalArm();
            aAnimalBuilder.BuildAnimalTail();
        }


    }
}

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


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


يركز نمط البناء على تعقيد إنشاء الكائن (حلها بواسطة "الخطوات")

يؤكد النمط المجرد على "عادل" على "التجريد" من (متعددة ولكن ذات الصلة) الأجسام.


يصف نموذج تصميم الباني كائنًا يعرف كيفية إنشاء كائن آخر من نوع محدد عبر عدة خطوات. وهي تحتفظ بالحالة اللازمة للعنصر المستهدف في كل خطوة وسيطة. فكر في ما يدور حول StringBuilder لإنتاج سلسلة نهائية.

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


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

الميزات البارزة منشئ :

  1. نمط البناء يبني كائنًا معقدًا باستخدام كائنات بسيطة ويستخدم طريقة خطوة بخطوة
  2. ينشئ فئة Builder الخطوة الكائن النهائي خطوة بخطوة. هذا المنشئ مستقل عن الكائنات الأخرى
  3. الاستبدال إلى مصنع Factory / Abstract في هذا السيناريو: عدد كبير جدًا من الحجج التي يتم تمريرها من برنامج العميل إلى فئة المصنع والتي يمكن أن تكون عرضة للخطأ
  4. قد تكون بعض المعلمات اختيارية على عكس المصنع الذي يفرض إرسال جميع المعلمات

مصنع ( مصنع بسيط) الميزات البارزة:

  1. نمط إبداعي
  2. على أساس الميراث
  3. إرجاع مصنع Factory Method (واجهة) الذي بدوره إرجاع "كائن ملموسة"
  4. يمكنك استبدال كائنات خرسانية جديدة للواجهة كما يجب ألا يكون العميل (المتصل) على علم بجميع عمليات التنفيذ الفعلية
  5. يقوم العميل دائمًا بالوصول إلى الواجهة فقط ويمكنك إخفاء تفاصيل إنشاء الكائن بطريقة المصنع.

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

إلقاء نظرة على المشاركات ذات الصلة:

الحفاظ على البناء في فصل منفصل (واجهة بطلاقة)

أنماط التصميم: مصنع مقابل مصنع طريقة مقابل مصنع مجردة

يمكنك الرجوع إلى المقالات أدناه لمزيد من التفاصيل:

sourcemaking

journaldev


أولا بعض الأشياء العامة لمتابعة حجتي:

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

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

الإجابة على السؤال:

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

نمط المصنع المجرد: GoF: "توفير واجهة لخلق عائلات من الكائنات ذات الصلة أو التابعة دون تحديد فئات الخرسانة الخاصة بهم."

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

نمط الباني: GoF: "قم بفصل بناء كائن معقد من تمثيله حتى تتمكن عملية الإنشاء نفسها من إنشاء تمثيلات مختلفة."

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

باختصار: إن الغرض من نمط شركة Abstract Abstract هو تبادل مجموعة من المنتجات التي يتم تصنيعها لاستخدامها معًا. الغرض من تصميم Builder هو تغليف الخوارزمية المجردة لإنشاء منتج لإعادة استخدامه لتمثيل مختلف للمنتج.

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


+-------------------------------------------------------------------+---------------------------------------------------+
|                              Builder                              |                      Factory                      |
+-------------------------------------------------------------------+---------------------------------------------------+
| Return only single instance to handle complex object construction | Retrun various instances on multiple constructors |
| No interface required                                             | Interface driven                                  |
| Inner classes is involved (to avoid telescopic constructors)      | Subclasses are involved                           |
+-------------------------------------------------------------------+---------------------------------------------------+  

تصغير نمط منشئ

القياس:

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

Courtesy


  • بناء عنصر كائن معقد خطوة بخطوة: نمط البناء

  • يتم إنشاء كائن بسيط باستخدام طريقة واحدة: نمط طريقة المصنع

  • إنشاء كائن باستخدام طريقة مصنع متعددة: نمط مصنع مجردة







builder-pattern