المحتوى:
- أنماط التصميم الإبداعية “Creational patterns”
- ما هو نمط التصميم الإبداعي”Builder design pattern”
- مشكلة يمكن حلها باستخدام نمط التصميم” Builder design pattern”
- حل المشكلة باستخدام نمط التصميم” Builder design pattern”
- بناء نمط التصميم الإبداعي” Builder design pattern”
- تمثيل نمط التصميم ” Builder design pattern” بشكل كود
- قابلية نمط التصميم ” Builder design pattern” للتطبيق
- كيفية تنفيذ نمط التصميم ” Builder design pattern”
- إيجابيات وسلبيات نمط التصميم “Builder design pattern “
- علاقات نمط التصميم “Builder design pattern ” مع الأنماط الأخرى
- الخاتمة
- المراجع
1.أنماط التصميم الإبداعية “Creational patterns“:
تحدثنا في المقالة السابقة عن أنماط التصميم الإبداعي التي كانت تلخص عملية إنشاء مثيل (instantiation process). وكيف أنها تساعد في جعل النظام مستقلاً عن كيفية إنشاء كائناته وتكوينها وتمثيلها. يستخدم نمط إنشاء الفئة الوراثة لتغيير الفئة التي تم إنشاء مثيل لها، في حين أن نمط إنشاء الكائن سوف يقوم بتفويض إنشاء مثيل إلى كائن آخر.
تصبح الأنماط الإبداعية مهمة مع تطور الأنظمة لتعتمد بشكل أكبر على تكوين الكائن بدلاً من وراثة الفئة. وعندما يحدث ذلك، يتحول التركيز بعيدًا عن الترميز الثابت لمجموعة ثابتة من السلوكيات نحو تحديد مجموعة أصغر من السلوكيات الأساسية التي يمكن تجميعها في أي عدد من السلوكيات الأكثر تعقيدًا. وبالتالي فإن إنشاء كائنات ذات سلوكيات معينة يتطلب أكثر من مجرد إنشاء مثيل لها.
توفر أنماط التصميم الإبداعي آليات مختلفة لإنشاء الكائنات، مما يزيد من المرونة وإعادة استخدام التعليمات البرمجية الموجودة.
وقد تحدثنا في المقالة السابقة عن ثاني نمط تصميم إبداعي وهو ” Abstract pattern “
وهو نمط المصنع، نمط تصميم إبداعي يوفر واجهة لإنشاء كائنات في فئة فائقة، ولكنه يسمح للفئات الفرعية بتغيير نوع الكائنات التي سيتم إنشاؤها.
وسنكمل بالتحدث عن تتمة أنواع التصميم الإبداعي وسنبدأ بال “Builder pattern“.
2.ما هو نمط التصميم الإبداعي” Builder pattern”
هو نمط تصميم إبداعي يتيح لنا إنشاء كائنات معقدة خطوة بخطوة. ويتيح لنا النمط إنتاج أنواع وتمثيلات مختلفة لكائن ما باستخدام نفس كود البناء.
ويستخدم في البرمجة كائنية التوجيه. ويهدف إلى تبسيط بناء الأشياء المعقدة عن طريق فصل عملية البناء عن تمثيل الكائن.
في جوهره، يتضمن نمط البناء إنشاء فئة منشئ منفصلة مسؤولة عن بناء الكائن خطوة بخطوة. تحتوي فئة الإنشاء هذه على طرق لتعيين سمات أو معلمات ” parameter”مختلفة للكائن الذي يتم إنشاؤه. بمجرد تعيين كافة السمات، يقوم كائن الباني ببناء الكائن النهائي.
يعد هذا النمط مفيدًا بشكل خاص عند التعامل مع الكائنات التي تحتوي على عدد كبير من المعلمات، أو عندما يكون ترتيب إعداد المعلمة مهمًا. يعمل على تحسين إمكانية قراءة التعليمات البرمجية وصيانتها من خلال توفير واجهة واضحة وسلسة لإنشاء الكائنات.
غالبًا ما يتم استخدام نمط البناء جنبًا إلى جنب مع أنماط أخرى مثل نمط المصنع”Factory pattern“لإنشاء كائنات معقدة بطريقة مرنة وسهلة الإدارة.
3.مشكلة يمكن حلها باستخدام نمط التصميم” Builder pattern”
تخيل كائنًا معقدًا يتطلب تهيئة شاقة خطوة بخطوة للعديد من الحقول والكائنات المتداخلة. عادةً ما يتم وضع رمز التهيئة هذا داخل مُنشئ ضخم يحتوي على الكثير من المعلمات”constructor”
أو ما هو أسوأ من ذلك: منتشرة في جميع أنحاء رمز العميل.
على سبيل المثال، دعونا نفكر في كيفية إنشاء كائن منزلي. لبناء منزل بسيط، نحتاج إلى بناء أربعة جدران وأرضية، وتثبيت باب، وتركيب زوج من النوافذ، وبناء سقف. ولكن ماذا لو كنا نريد منزلًا أكبر وأكثر إشراقًا، به فناء خلفي وأشياء أخرى (مثل نظام التدفئة، والسباكة، والأسلاك الكهربائية)؟
الحل الأبسط هو توسيع فئة House الأساسية وإنشاء مجموعة من الفئات الفرعية لتغطية جميع مجموعات المعلمات. لكن في النهاية سينتهي بنا الأمر بعدد كبير من الفئات الفرعية. أي معلمة جديدة، مثل نمط الشرفة، ستتطلب تنمية هذا التسلسل الهرمي بشكل أكبر.
هناك نهج آخر لا يتضمن تنمية الفئات الفرعية. يمكننا إنشاء منشئ عملاق مباشرة في فئة المنزل الأساسية مع جميع المعلمات الممكنة التي تتحكم في كائن المنزل. في حين أن هذا النهج يلغي بالفعل الحاجة إلى الفئات الفرعية، فإنه يخلق مشكلة أخرى.
في معظم الحالات، ستكون معظم المعلمات “parameters”غير مستخدمة، مما يجعل استدعاءات المنشئ قبيحة جدًا. على سبيل المثال، جزء صغير فقط من المنازل يحتوي على حمامات سباحة، وبالتالي فإن المعلمات المتعلقة بحمامات السباحة ستكون عديمة الفائدة تسع مرات من أصل عشرة.
4.حل المشكلة باستخدام نمط التصميم”Builder pattern”
يقترح النمط أن نقوم باستخراج كود بناء الكائن من فئته الخاصة ونقله إلى كائنات منفصلة تسمى البنّائون”builders“.
ينظم النمط بناء الكائن في مجموعة من الخطوات (buildWalls، buildDoor، وما إلى ذلك). لذلك لإنشاء كائن، علينا تنفيذ سلسلة من هذه الخطوات على كائن منشئ. الجزء المهم هو أننا لسنا بحاجة إلى استدعاء جميع الخطوات. يمكننا استدعاء تلك الخطوات الضرورية لإنتاج تكوين معين للكائن فقط.
قد تتطلب بعض خطوات البناء تنفيذًا مختلفًا عندما نحتاج إلى إنشاء تمثيلات مختلفة للمنتج. على سبيل المثال، يمكن بناء جدران المقصورة من الخشب، لكن جدران القلعة يجب أن تكون مبنية بالحجر.
في هذه الحالة، يمكننا إنشاء العديد من فئات البناء المختلفة التي تنفذ نفس مجموعة خطوات البناء، ولكن بطريقة مختلفة. ومن ثم يمكننا استخدام هؤلاء البنائين في عملية البناء (أي مجموعة مرتبة من الاستدعاءات لخطوات البناء) لإنتاج أنواع مختلفة من الكائنات.
على سبيل المثال، تخيل بناءًا يبني كل شيء من الخشب والزجاج، وثانيًا يبني كل شيء بالحجر والحديد، وثالثًا يستخدم الذهب والألماس. من خلال استدعاء نفس مجموعة الخطوات، نحصل على منزل عادي من البناء الأول، وقلعة صغيرة من الثاني، وقصر من الثالث. ومع ذلك، لن ينجح هذا إلا إذا كان كود العميل الذي يستدعي خطوات البناء قادرًا على التفاعل مع المنشئين باستخدام واجهة مشتركة. ويمكننا المضي قدمًا واستخراج سلسلة من الاستدعاءات لخطوات الإنشاء التي نستخدمها لإنشاء منتج في فئة منفصلة تسمى المخرج. تحدد فئة المخرج الترتيب الذي سيتم به تنفيذ خطوات البناء، بينما يوفر المنشئ تنفيذ تلك الخطوات.
وإن وجود director class للمخرج في برنامجنا ليس ضروريًا تمامًا. فيمكننا دائمًا استدعاء خطوات البناء بترتيب معين مباشرةً من رمز العميل. ومع ذلك، قد تكون فئة المخرج مكانًا جيدًا لوضع إجراءات البناء المختلفة حتى نتمكن من إعادة استخدامها عبر برنامجنا.
بالإضافة إلى ذلك، تخفي فئة المخرج تفاصيل إنشاء المنتج تمامًا من رمز العميل. فيحتاج العميل فقط إلى ربط منشئ بالمدير، وبدء البناء مع المدير، والحصول على النتيجة من المنشئ.
5.بناء نمط التصميم الإبداعي”Builder pattern”
- تعلن واجهة Builder عن خطوات بناء المنتج المشتركة بين جميع أنواع المنشئين
- يقدم Concrete Builders تطبيقات مختلفة لخطوات البناء. قد ينتج صانعو الخرسانة منتجات لا تتبع الواجهة المشتركة.
- Products هي الكائنات الناتجة. لا يجب أن تنتمي المنتجات التي تم إنشاؤها بواسطة منشئين مختلفين إلى نفس التسلسل الهرمي للفئة أو الواجهة.
- يحدد Director class الترتيب الذي يتم من خلاله استدعاء خطوات البناء، بحيث يمكننا إنشاء وإعادة استخدام تكوينات محددة للمنتجات.
- يجب على Client ربط أحد كائنات الإنشاء بالمدير. عادة، يتم ذلك مرة واحدة فقط، عبر معلمات منشئ المخرج. ثم يستخدم المخرج كائن البناء هذا في جميع عمليات البناء الإضافية. ومع ذلك، هناك طريقة بديلة عندما يقوم العميل بتمرير كائن المنشئ إلى طريقة الإنتاج الخاصة بالمخرج. في هذه الحالة، يمكنك استخدام أداة إنشاء مختلفة في كل مرة تقوم فيها بإنتاج شيء ما مع المخرج.
6.تمثيل نمط التصميم “Builder pattern” بشكل كود
يوضح هذا المثال لنمط الباني”builder patter”كيف يمكننا إعادة استخدام نفس كود إنشاء الكائن عند بناء أنواع مختلفة من المنتجات، مثل السيارات، وإنشاء الأدلة المقابلة لها.
السيارة عبارة عن جسم معقد يمكن تصنيعه بمئات الطرق المختلفة. بدلاً من تضخيم فئة السيارة بمُنشئ ضخم، قمنا باستخراج كود تجميع السيارة في فئة مُنشئ سيارة منفصلة. تحتوي هذه الفئة على مجموعة من الطرق لتكوين أجزاء مختلفة من السيارة.
إذا كان كود العميل يحتاج إلى تجميع نموذج خاص ودقيق للسيارة، فيمكنه العمل مع المنشئ مباشرة. من ناحية أخرى، يمكن للعميل تفويض التجميع إلى فئة المدير، التي تعرف كيفية استخدام منشئ لبناء العديد من نماذج السيارات الأكثر شعبية.
قد تصاب بالصدمة، لكن كل سيارة تحتاج إلى دليل (بجدية، من يقرأها؟). يصف الدليل كل ميزة في السيارة، لذا تختلف التفاصيل الواردة في الأدلة عبر الطرازات المختلفة. ولهذا السبب فمن المنطقي إعادة استخدام عملية البناء الحالية لكل من السيارات الحقيقية والأدلة الخاصة بها. بالطبع، بناء الدليل ليس مثل بناء السيارة، ولهذا السبب يجب علينا توفير فئة بناء أخرى متخصصة في تأليف الأدلة. تطبق هذه الفئة نفس أساليب البناء التي يتبعها شقيقها في بناء السيارات،
ولكن بدلاً من تصنيع أجزاء السيارة، فإنها تصفها. من خلال تمرير هؤلاء البنائين إلى نفس كائن المخرج، يمكننا إنشاء سيارة أو دليل.
الجزء الأخير هو جلب الكائن الناتج. السيارة المعدنية والدليل الورقي، على الرغم من ارتباطهما، لا يزالان شيئان مختلفان تمامًا. لا يمكننا وضع طريقة لجلب النتائج في المخرج دون ربط المخرج بفئات المنتجات الملموسة. ومن ثم نحصل على نتيجة البناء من الباني الذي قام بالمهمة.
7.قابلية تطبيق نمط التصميم”Builder pattern“
- استخدام نمط الباني للتخلص من المنشئ المتداخل “telescoping constructor”
لنفترض أن لدينا مُنشئًا يحتوي على عشر معلمات اختيارية. إن استدعاء مثل هذا الوحش أمر غير مريح للغاية؛ لذلك، يمكننا زيادة تحميل المُنشئ وإنشاء عدة إصدارات أقصر بمعلمات أقل. لا تزال هذه المُنشئات تشير إلى المُنشئ الرئيسي، حيث تمرر بعض القيم الافتراضية إلى أي معلمات محذوفة. حيث يتيح لنا نمط Builder إنشاء الكائنات خطوة بخطوة، باستخدام الخطوات التي نحتاجها بالفعل فقط. بعد تنفيذ النمط، لن نضطر إلى حشر العشرات من المعلمات في المُنشئات الخاصة بنا بعد الآن.
- استخدام نمط الباني عندما تريد أن يكون الكود الخاص بنا قادرًا على إنشاء تمثيلات مختلفة لبعض المنتجات (على سبيل المثال، المنازل الحجرية والخشبية).
يمكن تطبيق نمط البناء عندما يتضمن إنشاء تمثيلات مختلفة للمنتج خطوات مماثلة تختلف فقط في التفاصيل.
تحدد واجهة منشئ القاعدة جميع خطوات البناء الممكنة، ويقوم صانعو الخرسانة بتنفيذ هذه الخطوات لإنشاء تمثيلات معينة للمنتج. وفي الوقت نفسه، تقوم فئة المخرج بتوجيه ترتيب البناء.
- استخدام Builder لإنشاء أشجار مركبة أو كائنات معقدة أخرى.
يتيح لنا نمط Builder إنشاء المنتجات خطوة بخطوة. يمكننا تأجيل تنفيذ بعض الخطوات دون كسر المنتج النهائي. يمكننا أيضًا استدعاء الخطوات بشكل متكرر، وهو ما يكون مفيدًا عندما تحتاج إلى إنشاء شجرة كائنات. ولا يقوم البناء بالكشف عن المنتج غير المكتمل أثناء تنفيذ خطوات البناء. وهذا يمنع رمز العميل من جلب نتيجة غير كاملة.
8.كيفية تنفيذ نمط التصميم“Builder pattern”
- التأكد من أنه يمكننا تحديد خطوات الإنشاء الشائعة بوضوح لبناء جميع تمثيلات المنتج المتوفرة. وإلا فلن نتمكن من متابعة تنفيذ النمط.
- القيام بتعريف هذه الخطوات في واجهة المنشئ الأساسي.
- القيام بإنشاء فئة بناء ملموسة لكل تمثيل من المنتجات وتنفيذ خطوات البناء الخاصة بها.
ولا ننسى تنفيذ طريقة لجلب نتيجة البناء. السبب وراء عدم إمكانية الإعلان عن هذه الطريقة داخل واجهة المنشئ هو أن المنشئين المختلفين قد ينشئون منتجات ليس لها واجهة مشتركة. لذلك، نحن لا نعرف ما هو نوع الإرجاع لمثل هذه الطريقة. ومع ذلك، إذا كنا تتعامل مع منتجات من تسلسل هرمي واحد، فيمكن إضافة طريقة الجلب بأمان إلى الواجهة الأساسية. - التفكير في إنشاء فئة مدير. قد يشمل طرقًا مختلفة لإنشاء منتج باستخدام نفس كائن الإنشاء.
- يقوم رمز العميل بإنشاء الكائنات قبل بدء البناء، يجب على العميل تمرير كائن البناء إلى المدير. وعادة يقوم العميل بذلك مرة واحدة فقط، عبر معلمات مُنشئ فئة المخرج. يستخدم المخرج كائن البناء في جميع أعمال البناء الإضافية. هناك نهج بديل، حيث يتم تمرير المنشئ إلى طريقة بناء منتج معين للمخرج.
- لا يمكن الحصول على نتيجة البناء مباشرة من المخرج إلا إذا كانت جميع المنتجات تتبع نفس الواجهة. وبخلاف ذلك، يجب على العميل جلب النتيجة من المنشئ.
9.إيجابيات وسلبيات نمط التصميم “ Builder pattern“
الإيجابيات:
- يمكنك إنشاء الكائنات خطوة بخطوة، أو تأجيل خطوات البناء، أو تشغيل الخطوات بشكل متكرر.
- يمكنك إعادة استخدام نفس كود البناء عند إنشاء تمثيلات مختلفة للمنتجات.
- مبدأ المسؤولية الفردية. يمكنك عزل كود البناء المعقد عن منطق العمل الخاص بالمنتج.
السلبيات:
- يزداد التعقيد العام للكود نظرًا لأن النمط يتطلب إنشاء فئات جديدة متعددة.
10.علاقات نمط التصميم “Abstract pattern” مع الأنماط الأخرى
- تبدأ العديد من التصميمات باستخدام أسلوب المصنع (أقل تعقيدًا وأكثر قابلية للتخصيص عبر الفئات الفرعية) وتتطور نحو Abstract Factory أو Prototype أو Builder (أكثر مرونة، ولكن أكثر تعقيدًا).
- يركز Builder على بناء الكائنات المعقدة خطوة بخطوة. Abstract Factory متخصص في إنشاء عائلات من الكائنات ذات الصلة. يقوم Abstract Factory بإرجاع المنتج على الفور، بينما يتيح لك Builder تشغيل بعض خطوات البناء الإضافية قبل جلب المنتج
- يمكننا استخدام Builder عند إنشاء أشجار مركبة معقدة لأنه يمكنك برمجة خطوات البناء الخاصة بها للعمل بشكل متكرر.
11.الخاتمة
في الختام، يقدم نمط تصميم البناء”Builder design pattern”نهجًا منظمًا ومرنًا لبناء الأشياء المعقدة عن طريق تقسيم عملية البناء إلى خطوات أصغر يمكن التحكم فيها. من خلال توظيف البنائين والمخرجين، فإنه يسمح بإنشاء كائنات ذات تكوينات معقدة مع الحفاظ على الفصل بين عملية البناء والكائن النهائي.
تشمل فوائد نمط Builder تبسيط إنشاء كائنات كبيرة ومعقدة، مما يتيح إمكانية التوسعة بسهولة لاستيعاب المتطلبات الجديدة، وتعزيز إمكانية صيانة التعليمات البرمجية.
بشكل عام، أثبت نمط تصميم Builder أنه أداة قيمة في تطوير البرمجيات، حيث يوفر منهجية واضحة ومنظمة لبناء كائنات متطورة وتسهيل التحسينات والتعديلات المستقبلية بسهولة.
12.المراجع
https://refactoring.guru/design-patterns/builder
https://www.geeksforgeeks.org/builder-design-pattern/