نمط Builder
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 67 من 87.
يُعد نمط الباني (Builder Pattern) نمط تصميم إنشائي يحل مشكلة شائعة: وهي بناء كائنات معقدة تحتوي على العديد من المعاملات الاختيارية. فبدلاً من إنشاء عدة منشئات (constructors) أو تمرير null للمعاملات غير المستخدمة، يتيح لك نمط الباني بناء الكائنات خطوة بخطوة.
فكر في إنشاء كائن User بحقول مطلوبة (name, email) وأخرى اختيارية (age, phone, address). بدون نمط Builder، ستحتاج إلى منشئات متداخلة (telescoping constructors) أو منشئ فوضوي يحتوي على العديد من المعاملات (parameters). يوفر نمط Builder نهجاً أكثر تنظيماً:
public class User {
private final String name; // مطلوب
private final String email; // مطلوب
private final int age; // اختياري
private final String phone; // اختياري
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}تُرجع دوال الـ Builder القيمة this، مما يسمح بـ تسلسل الدوال (method chaining)—وهي طريقة سلسة وقابلة للقراءة لإنشاء الكائنات:
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();لاحظ كيف أن المُنشئ User خاص (private)—لا يمكن إنشاء الكائنات إلا من خلال الـ Builder. يتألق هذا النمط عندما تحتوي الكائنات على العديد من الحقول الاختيارية، مما يجعل الكود الخاص بك أكثر قابلية للقراءة وأقل عرضة للأخطاء من قوائم المعاملات الطويلة.
التحدي
سهللنقم ببناء نظام لطلب البيتزا باستخدام نمط البناء (Builder Pattern)! بدلاً من التعامل مع منشئ (constructor) يأخذ عشرات المعاملات لجميع الإضافات والخيارات الممكنة، ستقوم بإنشاء باني سلس (fluent builder) يجعل تخصيص البيتزا نظيفاً وسهل القراءة.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
Pizza.java: قم بإنشاء فئةPizzaتستخدم نمط Builder للإنشاء. يجب أن تحتوي البيتزا الخاصة بك على الحقول التالية:size(String) - مطلوب، يمثل حجم البيتزاcheese(boolean) - اختياري، القيمة الافتراضية falsepepperoni(boolean) - اختياري، القيمة الافتراضية falsemushrooms(boolean) - اختياري، القيمة الافتراضية falseيجب أن يكون منشئ
Pizzaخاصاً (private) ويقبل فقطPizzaBuilderكمعامل له، مع نسخ جميع القيم من الباني إلى حقول البيتزا.قم بتضمين طريقة
getDescription()التي تعيد سلسلة نصية بهذا التنسيق:[size] Pizza with: [toppings]حيث toppings هي قائمة مفصولة بفاصلة للإضافات التي قيمتها true وهي (Cheese، Pepperoni، Mushrooms). إذا لم يتم اختيار أي إضافات، يجب أن يقول جزء الإضافات ببساطةno toppings.قم بإنشاء فئة داخلية ثابتة (static inner class) باسم
PizzaBuilderتحتوي على:- منشئ (constructor) يأخذ معامل
sizeالمطلوب - طرق
cheese()، وpepperoni()، وmushrooms()حيث تقوم كل منها بضبط القيمة المنطقية الخاصة بها إلى true وتعيدthisللسماح بالتسلسل (chaining) - طريقة
build()تعيد كائنPizzaجديداً
- منشئ (constructor) يأخذ معامل
Main.java: استعرض باني البيتزا الخاص بك أثناء العمل! ستتلقى أربعة مدخلات: حجم البيتزا (String)، وثلاث قيم yes/no (Strings) تشير إلى ما إذا كان سيتم إضافة الجبن، والببروني، والفطر على التوالي.قم بإنشاء
PizzaBuilderبالحجم المعطى. ثم، بناءً على كل مدخل yes/no، قم بتسلسل طرق الإضافات المناسبة. إذا كان الإدخال"yes"، فاستدعِ طريقة الإضافة تلك؛ وإذا كان"no"، فتخطاها. أخيراً، استدعِbuild()لإنشاء البيتزا الخاصة بك.اطبع نتيجة استدعاء
getDescription()على البيتزا المكتملة.
ستتلقى أربعة مدخلات بالترتيب: الحجم (String)، إضافة الجبن (String: "yes" أو "no")، إضافة الببروني (String: "yes" أو "no")، إضافة الفطر (String: "yes" أو "no").
على سبيل المثال، مع المدخلات Large، و yes، و no، و yes، سيكون مخرجك: Large Pizza with: Cheese, Mushrooms
لاحظ كيف يسمح لك نمط Builder بإنشاء بيتزا بأي مزيج من الإضافات دون الحاجة إلى منشئات منفصلة لكل احتمال!
ورقة مرجعية
يُعد نمط البناء (Builder Pattern) نمط تصميم إنشائي لبناء كائنات معقدة تحتوي على العديد من المعلمات الاختيارية. فهو يتجنب المنشئات المتداخلة (telescoping constructors) وقوائم المعلمات الطويلة من خلال بناء الكائنات خطوة بخطوة.
الهيكل
يتكون النمط من:
- فئة (class) تحتوي على منشئ خاص (private constructor) لا يقبل سوى كائن بناء (builder)
- فئة بناء داخلية ثابتة (static inner Builder class) تقوم بإنشاء الكائن
- طرق بناء (Builder methods) تُرجع
thisمن أجل تسلسل الطرق (method chaining) - طريقة
build()تقوم بإنشاء الكائن النهائي
مثال على التنفيذ
public class User {
private final String name; // مطلوب
private final String email; // مطلوب
private final int age; // اختياري
private final String phone; // اختياري
private User(UserBuilder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
}
public static class UserBuilder {
private final String name;
private final String email;
private int age;
private String phone;
public UserBuilder(String name, String email) {
this.name = name;
this.email = email;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
}الاستخدام مع تسلسل الطرق (Method Chaining)
User user = new User.UserBuilder("Alice", "alice@email.com")
.age(25)
.phone("555-1234")
.build();يُعد نمط البناء مثاليًا عندما تحتوي الكائنات على العديد من الحقول الاختيارية، مما يوفر كودًا قابلًا للقراءة والصيانة مقارنة بالمنشئات التي تحتوي على معلمات عديدة.
جرّب بنفسك
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات
String size = scanner.nextLine();
String addCheese = scanner.nextLine();
String addPepperoni = scanner.nextLine();
String addMushrooms = scanner.nextLine();
// TODO: إنشاء PizzaBuilder بالحجم المحدد
// TODO: ربط طرق الإضافات (topping methods) بناءً على مدخلات yes/no
// TODO: بناء البيتزا وطباعة وصفها
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر