الفرق بين المراجعتين لصفحة: «Design Patterns/builder»
أسامه-دمراني (نقاش | مساهمات) 2.1 محتوى |
أسامه-دمراني (نقاش | مساهمات) 2.2 محتوى |
||
سطر 650: | سطر 650: | ||
System.out.println("Car isn't started"); | System.out.println("Car isn't started"); | ||
} | } | ||
} | |||
} | |||
</syntaxhighlight> | |||
==== الموجّه Director ==== | |||
===== director/Director.java: بانيات المتحكمات الخاصة بالموجّه ===== | |||
<syntaxhighlight lang="java"> | |||
package refactoring_guru.builder.example.director; | |||
import refactoring_guru.builder.example.builders.Builder; | |||
import refactoring_guru.builder.example.cars.Type; | |||
import refactoring_guru.builder.example.components.Engine; | |||
import refactoring_guru.builder.example.components.GPSNavigator; | |||
import refactoring_guru.builder.example.components.Transmission; | |||
import refactoring_guru.builder.example.components.TripComputer; | |||
/** | |||
* يحدد الموجِّه ترتيب خطوات البناء، ويعمل مع الكائن الباني من خلال | |||
* واجهة الباني المشتركة، لهذا قد لا يعرف ما المنتج الذي يتم بناؤه. | |||
*/ | |||
public class Director { | |||
public void constructSportsCar(Builder builder) { | |||
builder.setType(Type.SPORTS_CAR); | |||
builder.setSeats(2); | |||
builder.setEngine(new Engine(3.0, 0)); | |||
builder.setTransmission(Transmission.SEMI_AUTOMATIC); | |||
builder.setTripComputer(new TripComputer()); | |||
builder.setGPSNavigator(new GPSNavigator()); | |||
} | |||
public void constructCityCar(Builder builder) { | |||
builder.setType(Type.CITY_CAR); | |||
builder.setSeats(2); | |||
builder.setEngine(new Engine(1.2, 0)); | |||
builder.setTransmission(Transmission.AUTOMATIC); | |||
builder.setTripComputer(new TripComputer()); | |||
builder.setGPSNavigator(new GPSNavigator()); | |||
} | |||
public void constructSUV(Builder builder) { | |||
builder.setType(Type.SUV); | |||
builder.setSeats(4); | |||
builder.setEngine(new Engine(2.5, 0)); | |||
builder.setTransmission(Transmission.MANUAL); | |||
builder.setGPSNavigator(new GPSNavigator()); | |||
} | } | ||
} | } |
مراجعة 15:47، 25 سبتمبر 2019
نمط الباني (Builder) هو نمط تصميم إنشائي يسمح لك ببناء كائنات معقدة خطوة خطوة، كما يمكنك من إنتاج أنواعًا وتمثيلات (representations) مختلفة من الكائن باستخدام نفس شيفرة البناء (Construction code).
المشكلة
إن كان لدينا كائن معقد يتطلب تهيئة مرهقة وتفصيلية للكثير من الحقول والكائنات المتداخلة (nested objects)، فإن شيفرة التهيئة اللازمة لذلك الكائن تُدفن عادة داخل منشئ هائل الحجم به معامِلات (parameters) كثيرة، أو أسوأ من ذلك، أن تتناثر تلك الشيفرة في أرجاء شيفرة العميل.
ضع الصورة.
(ش.1) قد تجعل البرنامج معقدًا للغاية إن أنشأت فئة فرعية لكل تهيئة محتملة للكائن.
فمثلًا، دعنا ننظر في كيفية إنشاء كائن منزل House
: نحتاج أولًا إلى بناء أربع جدران وأرضية، وتركيب الباب، ونافذتين، وكذلك بناء السقف. لكن إن أردت منزلًا أكبر مع فناء خلفي وأنظمة تدفئة وسباكة وكهرباء وغير ذلك، فإن الحل الأبسط لذلك هو توسيع فئة House الأساسية وإنشاء مجموعة من الفئات الفرعية لتغطية جميع تجميعات (combinations) المعامِلات، لكن سيكون لديك في النهاية عدد كبير من الفئات الفرعية، وستزيد الشيفرة والهرمية عند إضافة أي معامِل جديد، مثل إضافة رواق للمنزل.
هناك منظور آخر لا يحتاج إضافة الفئات الفرعية، وهو إنشاء منشئ ضخم داخل فئة House مع كل المعامِلات المحتملة التي تتحكم في كائن المنزل، وسيلغي هذا الحلُ الحاجةَ إلى الفئات الفرعية، لكنه سيحدث مشكلة أخرى.
الصورة.
المنشئ الذي فيه معامِلات كثيرة مشكلته أن كل المعامِلات مطلوبة دائمًا.
ستكون أكثر المعامِلات غير مستخدمة في أغلب الوقت، مما يجعل المنشئ كبيرًا بدون داعي، فمثلًا قد تحتاج نسبة قليلة من المنازل حمامًا للسباحة، لذا ستكون المعامِلات المطلوبة لحمام السباحة غير مستخدمة في 90% من الوقت مثلًا.
الحل
يقترح نمط الباني (Builder) أن تستخرج شيفرة بناء الكائن من فئته وتنقلها إلى كائنات أخرى تسمى builders.
الصورة.
يسمح لك نمط الباني ببناء كائنات معقدة خطوة بخطوة، ولا يسمح في نفس الوقت للكائنات الأخرى بالوصول إلى المنتج أثناء بنائه.
ينظم النمط بناء الكائن في مجموعة خطوات (buildWalls, buildDoor، إلخ)، وستنفذ سلسلة من تلك الخطوات على كائن باني من أجل إنشاء كائن ما، والمهم هنا أنك لا تحتاج إلى استدعاء جميع الخطوات، بل تستدعي تلك المطلوبة لإنتاج تجهيزات معينة للكائن.
قد تحتاج بعض خطوات البناء إلى تطبيقات مختلفة عندما تريد بناء أشكال مختلفة من المنتج، فمثلًا قد يُنشأ حائط غرفة لكابينة صغيرة من الخشب، لكن جدران القلعة يجب أن تكون من الحجارة. لذا ربما تود إنشاء عدة فئات مختلفة من فئات الباني التي تستخدم نفس مجموعة خطوات البناء، لكن بشكل مختلف، ثم تستطيع استخدام تلك البانيات في عملية البناء -كاستدعاءات مرتبة إلى خطوات البناء مثلًا- من أجل إنتاج أنواع مختلفة من الكائنات.
الصورة.
تنفذ كائنات البناء المختلفة نفس المهام بطرق مختلفة.
فمثلًا، تخيل بانيًا يبني كل شيء من الخشب والزجاج، وآخر يبني بالحجارة والحديد، وآخر يستخدم الذهب والماس، فإن استدعينا نفس الخطوات مع كل باني سنحصل على منزل عادي من الأول، وقلعة صغيرة من الثاني، وقصر من الباني الثالث. لكن ذلك الأسلوب يصلح فقط إن كانت شيفرة العميل التي تستدعي خطوات البناء قادرة على التفاعل مع البنائين باستخدام واجهة مشتركة.
الموجِّه Director
يمكنك استخراج سلسلة استدعاءات إلى خطوات الباني الذي تستخدمه من أجل إنشاء المنتج إلى فئة منفصلة تسمى الموجّه، وتحدد فئة الموجه تلك ترتيب تنفيذ خطوات البناء، بينما يوفر الباني التطبيق المناسب لتلك الخطوات.
الصورة.
يحدد الموجه خطوات البناء التي يجب تنفيذها للحصول على منتج يعمل بكفاءة.
ليس من الضروري وجود موجّه في برنامجك، بل تستطيع أن تستدعي خطوات البناء بالترتيب الذي تريد مباشرة من شيفرة العميل، لكن فئة الموجه ستكون مكانًا جيدًا لوضع روتين بناء يمكن إعادة استخدامه في بقية برنامجك. أيضًا فإن فئة الموجه تخفي تفاصيل إنشاء المنتج تمامًا من شيفرة العميل، فلا يحتاج العميل إلا إلى ربط الباني بالموجه وبدء البناء باستخدام الموجه، ثم الحصول على النتيجة من الباني.
البنية
- تصرح واجهة الباني (Builder) عن خطوات إنشاء المنتج المشتركة بين كل أنواع كائنات البناء.
- توفر كائنات البناء الحقيقية (Concrete Builders) استخدامات مختلفة لخطوات الإنشاء، وقد تنتج منتجات لا تتبع الواجهة المشتركة.
- المنتجات (Products) هي كائنات ناتجة تُبنى بواسطة كائنات بناء مختلفة لا تنتمي بالضرورة إلى نفس الهرمية الفئوية أو إلى نفس الواجهة.
- تحدد فئة الموجه Director الترتيب الذي يجب أن تُستدعى به خطوات الإنشاء كي تستطيع إنشاء وإعادة استخدام إعدادات بعينها من المنتجات.
- يجب أن يربط العميل Client أحد كائنات البناء مع الموجّه، عادة يتم الأمر مرة واحدة فقط من خلال معامِلات منشئ الموجّه (Director's Constructor)، ثم يستخدم الموجه كائن البناء ذاك في كل الإنشاءات التالية، لكن هناك طريقة أخرى حين يمرِّر العميل كائن البناء إلى أسلوب الإنتاج الخاص بالموجّه، فعندئذ تستطيع استخدام كائن بناء مختلف في كل مرة تنتج فيها شيئًا بواسطة الموجّه.
مثال توضيحي
يوضح هذا المثال لنمط الباني كيفية إعادة استخدام نفس شيفرة بناء الكائن عند بناء أنواع مختلفة من المنتجات كالسيارات مثلًا، وكذلك إنشاء الأدلة الخاصة بها.
الصورة.
مثال تفصيلي لبناء سيارات وأدلة استخدام تناسب موديلات تلك السيارات.
السيارة كائن معقد يمكن إنشاؤه بمئات الطرق المختلفة، وبدلًا من حشو فئة Car بمنشئ ضخم فإننا استخرجنا شيفرة تجميع السيارة إلى فئة منفصلة لبناء السيارة تحتوي على مجموعة من الأساليب الخاصة بتهيئة أجزاء السيارة المختلفة. فإن احتاجت شيفرة العميل أن تجمع طرازًا خاصًا من السيارة فإنها تعمل مع الباني مباشرة، لكن من الناحية الأخرى فإنها تستطيع أن تفوض عملية التجميع إلى فئة الموجه التي تعرف كيف تستخدم الباني لإنشاء الطرازات المشهورة من السيارات.
ولأن السيارة تحتاج إلى دليل استخدام كل خاصية في السيارة، فإن أدلة الطرز المختلفة من السيارات تختلف كذلك، لهذا من المنطقي إعادة استخدام عملية بناء موجودة مسبقًا من أجل السيارات وأدلة استخدامها. وقد لا يكون بناء الدليل مشابهًا لبناء السيارة، لهذا يجب أن نوفر فئة بنّاء أخرى تتخصص في إنشاء الأدلة، وتستخدم تلك الفئة نفس أساليب البناء كأختها التي تبني السيارات، لكن بدلًا من بناء أجزاء السيارة فإنها تصف تلك الأجزاء وتشرحها، ونستطيع بناء أي منهما -الدليل أو السيارة- بتمرير هذه الكائنات البانية إلى نفس كائن الموجه.
والجزء الأخير هو جلب الكائن الناتج، وهو الدليل الورقي والسيارة المعدنية اللذان يختلفان تمامًا عن بعضهما رغم ارتباطهما بشكل ما. ولا تستطيع وضع أسلوب لجلب النتائج في الموجه دون ربطه بفئات المنتج الحقيقية، لهذا نحصل على نتيجة الإنشاء من الباني الذي نفذ المهمة.
// يكون استخدام نمط الباني منطقيًا حين تكون منتجاتك
// معقدة وتتطلب إعدادات كثيرة. والمنتجان التاليان
// يرتبطان ببعضهما رغم أنهما ليس لهما واجهة مشتركة.
class Car is
// يمكن أن تحتوي السيارة على جهاز ملاحة وحاسوب وبعض
// المقاعد. لكن طرز السيارات المختلفة مثل السيارات
// الرياضية والعائلية وغيرها قد تحتوي على مزايا أخرى
// تركب فيها.
class Manual is
// يجب أن تحتوي كل سيارة على دليل استخدام متوافق مع
// إعدادات السيارة ويصف كل مزاياها.
// تحدد واجهة الباني أساليب إنشاء الأجزاء المختلفة من
// كائنات المنتج.
interface Builder is
method reset()
method setSeats(...)
method setEngine(...)
method setTripComputer(...)
method setGPS(...)
// تتبع فئات الباني الحقيقي واجهة الباني وتوفر استخدامات
// محددة لخطوات البناء، وقد يحتوي برنامجك على صور متعددة
// للبانيات، تُطبق كل واحدة منها بشكل مختلف.
class CarBuilder implements Builder is
private field car:Car
// يجب أن تحتوي نسخة الباني الحديثة على كائن منتج فارغ
// تستخدمه لاحقًا أثناء التجميع.
constructor CarBuilder() is
this.reset()
// الكائن الذي يتم بناؤه reset ينظف أسلوب.
method reset() is
this.car = new Car()
// تعمل جميع خطوات الإنتاج مع نفس نسخة المنتج.
method setSeats(...) is
// ضع عدد المقاعد في السيارة.
method setEngine(...) is
// ركِّب المحرك.
method setTripComputer(...) is
// ركِّب حاسوب الرحلات.
method setGPS(...) is
// GPS ركب جهاز الملاحة.
// يفترض بالبانيات الحقيقية أن توفر أساليبها الخاصة
// بجلب النتائج، لأن الأنواع المختلفة من البانيات قد تنشئ
// منتجات مختلفة تمامًا لا تتبع نفس الواجهة، لهذا لا يمكن
// التصريح عن أساليب كتلك في واجهة الباني، على الأقل
// statically-typed language في لغات البرمجة من النوع الساكن.
// ينبغي أن تكون نسخة الباني جاهزة لبدء إنتاج منتج جديد بعد
// إعادة النتيجة النهائية إلى العميل. لذا من المعتاد استدعاء أسلوب
// getProduct إعادة الضبط في نهاية متن أسلوب.
// لكن هذا السلوك ليس ضروريًا، ويمكنك جعل الباني الخاص بك ينتظر
// استدعاء إعادة ضبط صريح من شيفرة العميل قبل التخلص من النتيجة
// السابقة.
method getProduct():Car is
product = this.car
this.reset()
return product
// يسمح لك الباني بإنشاء منتجات لا تتبع الواجهة المشتركة
// على عكي الأنماط الإنشائية الأخرى.
class CarManualBuilder implements Builder is
private field manual:Manual
constructor CarManualBuilder() is
this.reset()
method reset() is
this.manual = new Manual()
method setSeats(...) is
// وثِّق مزايا مقعد السيارة.
method setEngine(...) is
// أضف إرشادات المحرك.
method setTripComputer(...) is
// أضف إرشادات حاسوب الرحلات.
method setGPS(...) is
// Add GPS instructions.
method getProduct():Manual is
// أعد الدليل وأعد ضبط الباني.
// لا يكون الموجّه مسؤولًا إلا عن تنفيذ خطوات البناء بتسلسل معين.
// ذلك مفيد عندما تنتج منتجات وفقًا لترتيب أو إعدادات معينة.
// وتُعد فئةالباني خيارية بما أن العميل يستطيع التحكم في
// كائنات البناء مباشرة.
class Director is
private field builder:Builder
// يعمل الموجّه مع أي نسخة للباني تمررها شيفرة العميل
// إليه، وبهذه الطريقة قد تغير النوع النهائي للمنتج
// الذي يتم تجميعه.
method setBuilder(builder:Builder)
this.builder = builder
// يستطيع الموجّه إنشاء صور عدة للمنتج باستخدام
// نفس خطوات البناء.
method constructSportsCar(builder: Builder) is
builder.reset()
builder.setSeats(2)
builder.setEngine(new SportEngine())
builder.setTripComputer(true)
builder.setGPS(true)
method constructSUV(builder: Builder) is
// ...
// وتمررها إلى الموجّه ثم تبدأ builder تنشئ شيفرة العميل كائن بانيًا
// عملية الإنشاء، وتُجلب النتيجة النهائية من الكائن الباني.
class Application is
method makeCar() is
director = new Director()
CarBuilder builder = new CarBuilder()
director.constructSportsCar(builder)
Car car = builder.getProduct()
CarManualBuilder builder = new CarManualBuilder()
director.constructSportsCar(builder)
// يُجلب المنتج النهائي من كائنٍ باني بما أن الموجّه
// لا يكون على علم به، ولا يعتمد على بانيات حقيقية
// أو منتجات حقيقية.
Manual manual = builder.getProduct()
قابلية التطبيق
- استخدم نمط الباني للتخلص من المنشئ التليسكوبي (Telescopic Constructor).
لنقل أن لديك منشئًا فيه عشر معامِلات اختيارية، ستجد أن استدعاء منشئًا بهذا الحجم غير عملي، لهذا ستحمل المنشئ فوق طاقته وتنشئ عدة إصدارات أخرى أقصر بمعامِلات أقل. ستشير تلك المنشئات إلى المنشئ الأساسي وتمرر بعض القيم الافتراضية إلى أي معامِلات مهملة.
class Pizza {
Pizza(int size) { ... }
Pizza(int size, boolean cheese) { ... }
Pizza(int size, boolean cheese, boolean pepperoni) { ... }
// ...
يسمح لك نمط الباني ببناء كائنات خطوة بخطوة باستخدام الخطوات التي تحتاجها فقط، ولا تحتاج إلى حشر عشرات المعامِلات داخل منشئاتك بعد تطبيق النمط.
- استخدم نمط الباني حين تريد لشيفرتك أن تكون قادرة على إنشاء أشكال مختلفة من منتج ما (كبناء بيت من الخشب وآخر من الحجارة مثلًا).
يمكن استخدام النمط حين يتطلب إنشاء أشكال مختلفة من المنتج خطوات متشابهة ولا تختلف إلا في التفاصيل. ذلك أن الباني الأساسي (Base Builder) يحدد جميع خطوات الإنشاء المحتملة، وتستخدم البانيات الحقيقية تلك الخطوات لإنشاء أشكال مختلفة من المنتج بينما ترشد فئة الموجّه ترتيب الإنشاء.
- استخدم الباني لإنشاء أشجار نمط المركَّب أو أي كائنات معقدة أخرى.
يسمح لك نمط المركب بإنشاء منتجات خطوة بخطوة، فتستطيع تأجيل تنفيذ بعض الخطوات دون تعطيل المنتج النهائي، بل تستطيع استدعاء الخطوات بشكل عكسي، وذلك مفيد حين تريد بناء شجرة كائنات. أيضًا، لا يكشف الباني المنتج غير المكتمل أثناء تنفيذ خطوات الإنشاء، كي يمنع شيفرة العميل من جلب نتيجة غير مكتملة.
كيفية الاستخدام
- تأكد أنك تحدد بوضوح خطوات الإنشاء لكل أشكال المنتج المتاحة، وإلا فلن تستطيع المضي قدمًا في تطبيق النمط.
- صرح عن هذه الخطوات في واجهة الباني المشتركة.
- أنشئ فئة بنّاء حقيقية لكل شكل من أشكال المنتج وطبق خطوات الإنشاء الخاصة به. لا تنسى تطبيق أسلوب لجلب نتيجة البناء، لا يمكن التصريح عن هذا الأسلوب داخل واجهة الباني بسبب أن البانيات المختلفة قد تبني منتجات ليس لها واجهة مشتركة، لهذا فإنك لا تعرف نوع الإعادة لمثل ذلك الأسلوب، لكن بأي حال، فإن كنت تتعامل مع منتجات من هرمية واحدة، فيمكن إضافة أسلوب الجلب Fetching Method إلى الواجهة المشتركة.
- فكر في إنشاء فئة موجّهDirector Class، ستغلف تلك الفئة طرقًا مختلفة لبناء منتج باستخدام نفس كائن الباني.
- تنشئ شيفرة العميل كائنات الباني والموجّه، ويجب أن يمرِّر العميل كائنًا بانيًا إلى الموجِّه قبل بدء الإنشاء، وعادة ما يفعل العميل ذلك مرة واحدة فقط من خلال معامِلات منشئ الموجّه (Parmeters of the Director's Constructor)، ويستخدم الموجه كائن البنّاء في كل عمليات الإنشاء التالية، يوجد منظور مختلف لهذا يُمرَّر فيه البنّاء مباشرة إلى أسلوب الإنشاء للموجّه.
- يمكن الحصول على نتيجة الإنشاء مباشرة من الموجّه إن كانت كل المنتجات تتبع نفس الواجهة، وإلا فإن العميل يجب أن يجلب النتيجة من البنّاء.
المزايا والعيوب
المزايا
- تستطيع إنشاء الكائنات خطوة بخطوة، وتؤجل خطوات الإنشاء أو تنفذ الخطوات بشكل معكوس.
- تستطيع إعادة استخدام نفس شيفرة الإنشاء أثناء بناء أشكال مختلفة من المنتجات.
- مبدأ المسؤولية الواحدة. تستطيع عزل شيفرة البناء المعقدة عن منطق العمل للمنتج.
العيوب
- يزيد التعقيد الكلي للشيفرة بما أن النمط يتطلب إنشاء عدة فئات جديدة.
العلاقات مع الأنماط الأخرى
- تستخدم تصميمات كثيرة أسلوب المصنع (Factory Method) بما أنه أقل تعقيدًا وأكثر قابلية للتخصيص عبر الفئات الفرعية، ثم تنتقل إلى أسلوب المصنع المجرد أو النموذج الأولي (prototype) أو الباني (Builder)، حيث أنهم أكثر مرونة لكن أكثر تعقيدًا في المقابل.
- يركز نمط الباني (Builder) على إنشاء كائنات معقدة خطوة بخطوة، أما المصنع المجرد فيتخصص في إنشاء عائلات من المنتجات المتعلقة ببعضها، كذلك يعيد المنتج فورًا في حين أن الباني يسمح لك بإجراء بعض خطوات الإنشاء الإضافية قبل الحصول على المنتج.
- تستطيع استخدام نمط الباني أثناء إنشاء أشجار معقدة لنمط المركّب إذ يمكنك برمجة خطوات إنشائه لتعمل بشكل عكسي.
- تستطيع دمج نمطي الباني والجسر، إذ تلعب فئة الموجّه دور التجريد (abstraction) بينما تتصرف بانيات مختلفة كتطبيقات (implementations).
- يمكن استخدام المصانع المجردة والبانيات (Builders) والنماذج الأولية كمفردات Singletons.
الاستخدام في لغة جافا
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة. إليك بعض الأمثلة على نمط الباني في مكتبات جافا:
- (java.lang.StringBuilder#append() (
unsynchronized
. - (java.lang.StringBuffer#append() (
synchronized
. - ()java.nio.ByteBuffer#put (وأيضًا في CharBuffer، ShortBuffer، IntBuffer، LongBuffer، FloatBuffer، DoubleBuffer).
- ()javax.swing.GroupLayout.Group#addComponent.
- جميع استخدامات java.lang.Appendable.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
إنشاء سيارة خطوة بخطوة
يسمح لك نمط الباني في هذا المثال بإنشاء طرازات مختلفة من سيارة ما، ويوضح كيف يُخرج الباني منتجات من أنواع مختلفة (دليل السيارة) باستخدام نفس خطوات الإنشاء.
يتحكم الموجّه في ترتيب الإنشاء ويعرف أي خطوة يجب استدعاؤها لإنتاج هذا الطراز أو ذاك، ويعمل مع الكائنات البانية من خلال واجهتهم المشتركة فقط، مما يسمح بتمرير أنواع مختلفة من البانيات إلى الموجّه.
نحصل على النتيجة النهائية من الكائن الباني إذ لا يستطيع الموجّه أن يعرف نوع الكائن الناتج، بل الباني فقط هو من يعرف.
البنّاؤون Builders
builders/Builder.java: واجهة الباني المشتركة
package refactoring_guru.builder.example.builders;
import refactoring_guru.builder.example.cars.Type;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* تحدد واجهة الباني كل الطرق المحتملة لتهيئة منتج ما.
*/
public interface Builder {
void setType(Type type);
void setSeats(int seats);
void setEngine(Engine engine);
void setTransmission(Transmission transmission);
void setTripComputer(TripComputer tripComputer);
void setGPSNavigator(GPSNavigator gpsNavigator);
}
builders/CarBuilder.java: باني السيارة (Builder of the car)
package refactoring_guru.builder.example.builders;
import refactoring_guru.builder.example.cars.Car;
import refactoring_guru.builder.example.cars.Type;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* تستخدم البانيات الحقيقية خطوات محددة في الواجهة المشتركة.
*/
public class CarBuilder implements Builder {
private Type type;
private int seats;
private Engine engine;
private Transmission transmission;
private TripComputer tripComputer;
private GPSNavigator gpsNavigator;
@Override
public void setType(Type type) {
this.type = type;
}
@Override
public void setSeats(int seats) {
this.seats = seats;
}
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
@Override
public void setTripComputer(TripComputer tripComputer) {
this.tripComputer = tripComputer;
}
@Override
public void setGPSNavigator(GPSNavigator gpsNavigator) {
this.gpsNavigator = gpsNavigator;
}
public Car getResult() {
return new Car(type, seats, engine, transmission, tripComputer, gpsNavigator);
}
}
builders/CarManualBuilder.java: باني دليل السيارة (Builder of the car manual)
package refactoring_guru.builder.example.builders;
import refactoring_guru.builder.example.cars.Manual;
import refactoring_guru.builder.example.cars.Type;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* يستطيع الباني إنشاء منتجات لا علاقة لها ببعضها، على عكس أنماط التصميم
* الإنشائية الأخرى التي ليس لها واجهة مشتركة.
*
* يوضح هذا المثال كيفية بناء دليل استخدام لسيارة باستخدام نفس خطوات
* بناء السيارة نفسها، هذا يسمح بإنتاج أدلة لطرازات محددة من السيارات
* المجهزة بمزايا مختلفة.
*/
public class CarManualBuilder implements Builder{
private Type type;
private int seats;
private Engine engine;
private Transmission transmission;
private TripComputer tripComputer;
private GPSNavigator gpsNavigator;
@Override
public void setType(Type type) {
this.type = type;
}
@Override
public void setSeats(int seats) {
this.seats = seats;
}
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
@Override
public void setTripComputer(TripComputer tripComputer) {
this.tripComputer = tripComputer;
}
@Override
public void setGPSNavigator(GPSNavigator gpsNavigator) {
this.gpsNavigator = gpsNavigator;
}
public Manual getResult() {
return new Manual(type, seats, engine, transmission, tripComputer, gpsNavigator);
}
}
السيارات Cars
cars/Car.java: منتج السيارة
package refactoring_guru.builder.example.cars;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* السيارة تمثل فئة منتج.
*/
public class Car {
private final Type type;
private final int seats;
private final Engine engine;
private final Transmission transmission;
private final TripComputer tripComputer;
private final GPSNavigator gpsNavigator;
private double fuel = 0;
public Car(Type type, int seats, Engine engine, Transmission transmission,
TripComputer tripComputer, GPSNavigator gpsNavigator) {
this.type = type;
this.seats = seats;
this.engine = engine;
this.transmission = transmission;
this.tripComputer = tripComputer;
this.tripComputer.setCar(this);
this.gpsNavigator = gpsNavigator;
}
public Type getType() {
return type;
}
public double getFuel() {
return fuel;
}
public void setFuel(double fuel) {
this.fuel = fuel;
}
public int getSeats() {
return seats;
}
public Engine getEngine() {
return engine;
}
public Transmission getTransmission() {
return transmission;
}
public TripComputer getTripComputer() {
return tripComputer;
}
public GPSNavigator getGpsNavigator() {
return gpsNavigator;
}
}
cars/Manual.java: منتج دليل الاستخدام
package refactoring_guru.builder.example.cars;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* دليل استخدام السيارة هو منتج آخر، ولاحظ أنه لا يشترك مع السيارة
* في الأصل، أي أنهما لا يرتبطان ببعضهما.
*/
public class Manual {
private final Type type;
private final int seats;
private final Engine engine;
private final Transmission transmission;
private final TripComputer tripComputer;
private final GPSNavigator gpsNavigator;
public Manual(Type type, int seats, Engine engine, Transmission transmission,
TripComputer tripComputer, GPSNavigator gpsNavigator) {
this.type = type;
this.seats = seats;
this.engine = engine;
this.transmission = transmission;
this.tripComputer = tripComputer;
this.gpsNavigator = gpsNavigator;
}
public String print() {
String info = "";
info += "Type of car: " + type + "\n";
info += "Count of seats: " + seats + "\n";
info += "Engine: volume - " + engine.getVolume() + "; mileage - " + engine.getMileage() + "\n";
info += "Transmission: " + transmission + "\n";
if (this.tripComputer != null) {
info += "Trip Computer: Functional" + "\n";
} else {
info += "Trip Computer: N/A" + "\n";
}
if (this.gpsNavigator != null) {
info += "GPS Navigator: Functional" + "\n";
} else {
info += "GPS Navigator: N/A" + "\n";
}
return info;
}
}
cars/Type.java
package refactoring_guru.builder.example.cars;
public enum Type {
CITY_CAR, SPORTS_CAR, SUV
}
العناصر Components
components/Engine.java: ميزة 1 للمنتج
package refactoring_guru.builder.example.components;
/**
* مثال على ميزة لسيارة ما.
*/
public class Engine {
private final double volume;
private double mileage;
private boolean started;
public Engine(double volume, double mileage) {
this.volume = volume;
this.mileage = mileage;
}
public void on() {
started = true;
}
public void off() {
started = false;
}
public boolean isStarted() {
return started;
}
public void go(double mileage) {
if (started) {
this.mileage += mileage;
} else {
System.err.println("Cannot go(), you must start engine first!");
}
}
public double getVolume() {
return volume;
}
public double getMileage() {
return mileage;
}
}
package refactoring_guru.builder.example.components;
/**
* مثال على ميزة أخرى للسيارة.
*/
public class GPSNavigator {
private String route;
public GPSNavigator() {
this.route = "221b, Baker Street, London to Scotland Yard, 8-10 Broadway, London";
}
public GPSNavigator(String manualRoute) {
this.route = manualRoute;
}
public String getRoute() {
return route;
}
}
components/Transmission.java: ميزة 3 للمنتج
package refactoring_guru.builder.example.components;
/**
* مثال على ميزة أخرى للسيارة.
*/
public enum Transmission {
SINGLE_SPEED, MANUAL, AUTOMATIC, SEMI_AUTOMATIC
}
components/TripComputer.java: ميزة 4 للمنتج
package refactoring_guru.builder.example.components;
import refactoring_guru.builder.example.cars.Car;
/**
* مثال على ميزة أخرى للسيارة.
*/
public class TripComputer {
private Car car;
public void setCar(Car car) {
this.car = car;
}
public void showFuelLevel() {
System.out.println("Fuel level: " + car.getFuel());
}
public void showStatus() {
if (this.car.getEngine().isStarted()) {
System.out.println("Car is started");
} else {
System.out.println("Car isn't started");
}
}
}
الموجّه Director
director/Director.java: بانيات المتحكمات الخاصة بالموجّه
package refactoring_guru.builder.example.director;
import refactoring_guru.builder.example.builders.Builder;
import refactoring_guru.builder.example.cars.Type;
import refactoring_guru.builder.example.components.Engine;
import refactoring_guru.builder.example.components.GPSNavigator;
import refactoring_guru.builder.example.components.Transmission;
import refactoring_guru.builder.example.components.TripComputer;
/**
* يحدد الموجِّه ترتيب خطوات البناء، ويعمل مع الكائن الباني من خلال
* واجهة الباني المشتركة، لهذا قد لا يعرف ما المنتج الذي يتم بناؤه.
*/
public class Director {
public void constructSportsCar(Builder builder) {
builder.setType(Type.SPORTS_CAR);
builder.setSeats(2);
builder.setEngine(new Engine(3.0, 0));
builder.setTransmission(Transmission.SEMI_AUTOMATIC);
builder.setTripComputer(new TripComputer());
builder.setGPSNavigator(new GPSNavigator());
}
public void constructCityCar(Builder builder) {
builder.setType(Type.CITY_CAR);
builder.setSeats(2);
builder.setEngine(new Engine(1.2, 0));
builder.setTransmission(Transmission.AUTOMATIC);
builder.setTripComputer(new TripComputer());
builder.setGPSNavigator(new GPSNavigator());
}
public void constructSUV(Builder builder) {
builder.setType(Type.SUV);
builder.setSeats(4);
builder.setEngine(new Engine(2.5, 0));
builder.setTransmission(Transmission.MANUAL);
builder.setGPSNavigator(new GPSNavigator());
}
}
الاستخدام في لغة #C
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
مثال: بُنية النمط
يوضح هذا المثال بنية نمط النموذج الأولي، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟
الاستخدام في لغة PHP
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
مثال: بُنية النمط
يوضح هذا المثال بنية نمط النموذج الأولي، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟
الاستخدام في لغة بايثون
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
مثال: بُنية النمط
يوضح هذا المثال بنية نمط النموذج الأولي، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟
الاستخدام في لغة روبي
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
مثال: بُنية النمط
يوضح هذا المثال بنية نمط النموذج الأولي، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟
الاستخدام في لغة Swift
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
مثال: بُنية النمط
يوضح هذا المثال بنية نمط النموذج الأولي، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟
الاستخدام في لغة TypeScript
المستوى: ★ ★ ☆
الانتشار: ★ ★ ☆
أمثلة الاستخدام: ينتشر استخدام نمط الباني في لغة جافا، إذ هو مفيد جدًا حين تريد إنشاء كائن به الكثير من خيارات التهيئة المحتملة.
يمكن ملاحظة النمط داخل الفئة التي لديها أسلوب إنشاء وحيد وعدة أساليب لتهيئة الكائن الناتج، وتدعم أساليب الباني التسلسل (Chaining) في الغالب. (مثال: باني->ضع قيمةأ(1)->ضع قيمةب(2)->أنشئ() ).
مثال: بُنية النمط
يوضح هذا المثال بنية نمط النموذج الأولي، ويركز على إجابة الأسئلة التالية:
- ما الفئات التي يتكون منها؟
- ما الأدوار التي تلعبها هذه الفئات؟
- كيف ترتبط عناصر النمط ببعضها؟