نمط Factory
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 91 من 110.
يقوم نمط المصنع (Factory pattern) بتفويض إنشاء الكائنات إلى طريقة (method) أو فئة (class) منفصلة، مما يتيح لك إنشاء كائنات دون تحديد فئتها الدقيقة. يختلف هذا عن منشئ المصنع (factory constructor) الذي تعلمته سابقًا - فنمط المصنع هو نهج تصميم حيث يتم توحيد منطق الإنشاء في مكان مركزي ويمكنه إرجاع أنواع مختلفة بناءً على المدخلات.
فكر في سيناريو تحتاج فيه إلى إنشاء أنواع مختلفة من الإشعارات. فبدلاً من أن يقرر العميل أي فئة (class) سيقوم بإنشائها، يتولى المصنع (factory) معالجة هذا القرار:
abstract class Notification {
void send(String message);
}
class EmailNotification implements Notification {
@override
void send(String message) => print('Email: $message');
}
class SMSNotification implements Notification {
@override
void send(String message) => print('SMS: $message');
}
class NotificationFactory {
static Notification create(String type) {
switch (type) {
case 'email':
return EmailNotification();
case 'sms':
return SMSNotification();
default:
return EmailNotification();
}
}
}
void main() {
var notification = NotificationFactory.create('sms');
notification.send('Hello!'); // SMS: Hello!
}كود العميل لا يحتاج إلى معرفة EmailNotification أو SMSNotification - فهو يطلب فقط من المصنع (factory) إشعاراً من نوع معين. هذا يجعل إضافة أنواع إشعارات جديدة أمراً سهلاً: فأنت تقوم فقط بتعديل المصنع، وليس كل مكان يتم فيه إنشاء الإشعارات.
استخدم نمط Factory عندما يتضمن إنشاء الكائنات منطقاً معقداً، أو عندما تريد إخفاء فئات التنفيذ عن العملاء، أو عندما يعتمد النوع المحدد المراد إنشاؤه على ظروف وقت التشغيل.
التحدي
سهللنقم ببناء مولد مستندات باستخدام نمط المصنع (Factory pattern)! ستقوم بإنشاء نظام ينتج أنواعاً مختلفة من المستندات (التقارير، الفواتير، والرسائل) من خلال مصنع مركزي، بحيث لا يحتاج كود العميل إلى معرفة فئات المستندات المحددة.
ستقوم بتنظيم الكود الخاص بك في ملفين:
document_factory.dart: أنشئ نظام المستندات الخاص بك هنا:- فئة مجردة (abstract class) باسم
Documentمع طريقةgenerate(String content)تعيدString - ثلاث فئات ملموسة (concrete classes) تقوم بتنفيذ
Document:Report- تعيد طريقةgenerateالخاصة بها=== REPORT ===\n[content]\n=============Invoice- تعيد طريقةgenerateالخاصة بهاINVOICE\n--------\n[content]\nTotal DueLetter- تعيد طريقةgenerateالخاصة بهاDear Reader,\n[content]\nSincerely
- فئة
DocumentFactoryمع طريقة ثابتة (static method) باسمcreate(String type)تعيد كائنDocumentالمناسب بناءً على سلسلة النوع:"report"، أو"invoice"، أو"letter". لأي نوع غير معروف، قم بإعادةLetterكافتراضي
- فئة مجردة (abstract class) باسم
main.dart: قم باستيراد مصنع المستندات الخاص بك ووضح كيف يقوم المصنع بإنشاء أنواع مستندات مختلفة دون حاجة العميل لمعرفة الفئات الملموسة:- استخدم المصنع لإنشاء تقرير واطبع نتيجة توليده بالمحتوى
Sales increased by 25% - اطبع سطراً فارغاً
- استخدم المصنع لإنشاء فاتورة واطبع نتيجة توليدها بالمحتوى
Widget x3 - $150 - اطبع سطراً فارغاً
- استخدم المصنع لإنشاء رسالة واطبع نتيجة توليدها بالمحتوى
Thank you for your order
- استخدم المصنع لإنشاء تقرير واطبع نتيجة توليده بالمحتوى
لاحظ كيف يتفاعل ملفك الرئيسي فقط مع المصنع ونوع Document المجرد - فهو لا يقوم أبداً بإنشاء كائنات Report أو Invoice أو Letter بشكل مباشر!
المخرجات المتوقعة:
=== REPORT ===
Sales increased by 25%
=============
INVOICE
--------
Widget x3 - $150
Total Due
Dear Reader,
Thank you for your order
Sincerelyورقة مرجعية
يقوم نمط المصنع (Factory pattern) بتفويض إنشاء الكائنات إلى طريقة أو فئة منفصلة، مما يسمح لك بإنشاء كائنات دون تحديد فئتها الدقيقة. يتم تمركز منطق الإنشاء ويمكنه إرجاع أنواع مختلفة بناءً على المدخلات.
الهيكل الأساسي لنمط المصنع:
abstract class Notification {
void send(String message);
}
class EmailNotification implements Notification {
@override
void send(String message) => print('Email: $message');
}
class SMSNotification implements Notification {
@override
void send(String message) => print('SMS: $message');
}
class NotificationFactory {
static Notification create(String type) {
switch (type) {
case 'email':
return EmailNotification();
case 'sms':
return SMSNotification();
default:
return EmailNotification();
}
}
}
void main() {
var notification = NotificationFactory.create('sms');
notification.send('Hello!'); // SMS: Hello!
}يتفاعل كود العميل فقط مع المصنع والنوع المجرد، وليس مع فئات التنفيذ الملموسة. وهذا يجعل إضافة أنواع جديدة أمراً سهلاً - حيث تقوم فقط بتعديل المصنع.
استخدم نمط المصنع عندما:
- يتضمن إنشاء الكائن منطقاً معقداً
- تريد إخفاء فئات التنفيذ عن العملاء
- يعتمد النوع الدقيق المراد إنشاؤه على ظروف وقت التشغيل
جرّب بنفسك
import 'document_factory.dart';
void main() {
// TODO: استخدم DocumentFactory.create() لإنشاء تقرير (report)
// ثم اطبع نتيجة توليده بالمحتوى: "Sales increased by 25%"
// TODO: اطبع سطرًا فارغًا
// TODO: استخدم DocumentFactory.create() لإنشاء فاتورة (invoice)
// ثم اطبع نتيجة توليده بالمحتوى: "Widget x3 - \$150"
// TODO: اطبع سطرًا فارغًا
// TODO: استخدم DocumentFactory.create() لإنشاء رسالة (letter)
// ثم اطبع نتيجة توليده بالمحتوى: "Thank you for your order"
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع