نمط Factory
جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 72 من 91.
إن نمط المصنع (Factory Pattern) هو نمط تصميم إنشائي يفوض عملية إنشاء الكائنات إلى فئة أو طريقة منفصلة. فبدلاً من استخدام new مباشرةً في جميع أنحاء الكود الخاص بك، تطلب من المصنع إنشاء الكائنات لك.
هذا النمط مفيد بشكل خاص عندما يكون لديك فئات متعددة ذات صلة تشترك في واجهة برمجية مشتركة، وتريد تحديد أي منها سيتم إنشاؤه بناءً على شرط معين. لنفترض وجود نظام إشعارات يمكنه إرسال الرسائل عبر قنوات مختلفة:
<?php
interface Notification {
public function send(string $message): string;
}
class EmailNotification implements Notification {
public function send(string $message): string {
return "Email: $message";
}
}
class SmsNotification implements Notification {
public function send(string $message): string {
return "SMS: $message";
}
}
بدون وجود مصنع (factory)، ستقوم بنشر جمل if واستدعاءات new في كل مكان. يقوم المصنع بمركزة هذا المنطق:
<?php
class NotificationFactory {
public static function create(string $type): Notification {
return match($type) {
'email' => new EmailNotification(),
'sms' => new SmsNotification(),
default => throw new InvalidArgumentException("Unknown type: $type")
};
}
}
$notification = NotificationFactory::create('email');
echo $notification->send('Hello!');
المخرجات:
Email: Hello!الفائدة الرئيسية هي أن الكود الخاص بك يعتمد على واجهة Notification، وليس على فئات ملموسة. إذا قمت لاحقاً بإضافة فئة PushNotification، فما عليك سوى تحديث المصنع. وسيحصل كل الكود الذي يستخدم المصنع تلقائياً على إمكانية الوصول إلى النوع الجديد دون أي تعديل.
التحدي
سهللنقم ببناء نظام منشئ مستندات باستخدام نمط المصنع (Factory Pattern). تشترك أنواع المستندات المختلفة (PDF، HTML، Plain Text) في واجهة برمجية (interface) مشتركة ولكنها تنتج مخرجات مختلفة — وهو سيناريو مثالي لمركزية إنشاء الكائنات في مصنع.
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
Document.php— أنشئ واجهةDocumentالتي تحدد العقد الذي يجب أن تتبعه جميع أنواع المستندات. يجب أن تحتوي على وظيفة (method) واحدةrender(string $content): stringتأخذ المحتوى وتعيد مخرجات المستند المنسقة.Documents.php— قم بتضمين واجهة Document وأنشئ ثلاث فئات (classes) تقوم بتنفيذها:PdfDocument— تعيد وظيفةrender()الخاصة بها"PDF: [content]"HtmlDocument— تعيد وظيفةrender()الخاصة بها"<html>[content]</html>"TextDocument— تعيد وظيفةrender()الخاصة بها"TXT: [content]"
DocumentFactory.php— قم بتضمين ملف Documents وأنشئ فئةDocumentFactoryمع وظيفة ثابتة (static method) باسمcreate(string $type): Document. يجب أن تستخدم هذه الوظيفةmatchلإرجاع كائن المستند المناسب بناءً على النوع:"pdf"تعيد كائنPdfDocumentجديد"html"تعيد كائنHtmlDocumentجديد"text"تعيد كائنTextDocumentجديد- أي نوع آخر يجب أن يرمي استثناءً من نوع
InvalidArgumentExceptionمع الرسالة"Unknown document type: [type]"
main.php— قم بتضمين ملف DocumentFactory. ستتلقى مدخلين: نوع المستند والمحتوى المراد عرضه.استخدم المصنع لإنشاء نوع المستند المناسب، ثم استدعِ وظيفة
render()الخاصة به مع المحتوى واطبع النتيجة.في سطر جديد، اطبع
"Factory benefit: Adding new types only requires updating the factory"لتسليط الضوء على سبب أهمية هذا النمط.
يحافظ نمط المصنع (Factory Pattern) على مرونة الكود الخاص بك — عندما تحتاج إلى إضافة MarkdownDocument لاحقاً، ستقوم فقط بتحديث المصنع بينما يستمر كل الكود الموجود في العمل دون تغيير.
ورقة مرجعية
يقوم نمط المصنع (Factory Pattern) بتفويض عملية إنشاء الكائنات إلى فئة أو طريقة منفصلة بدلاً من استخدام new مباشرة في جميع أنحاء الكود الخاص بك.
هذا النمط مفيد عندما يكون لديك عدة فئات مرتبطة تشترك في واجهة (interface) مشتركة، وتحتاج إلى تحديد أي منها سيتم إنشاؤه بناءً على شرط معين.
مثال مع نظام إشعارات:
<?php
interface Notification {
public function send(string $message): string;
}
class EmailNotification implements Notification {
public function send(string $message): string {
return "Email: $message";
}
}
class SmsNotification implements Notification {
public function send(string $message): string {
return "SMS: $message";
}
}
يقوم المصنع بتركيز منطق إنشاء الكائنات في مكان واحد:
<?php
class NotificationFactory {
public static function create(string $type): Notification {
return match($type) {
'email' => new EmailNotification(),
'sms' => new SmsNotification(),
default => throw new InvalidArgumentException("Unknown type: $type")
};
}
}
$notification = NotificationFactory::create('email');
echo $notification->send('Hello!'); // Output: Email: Hello!
الفوائد الرئيسية:
- يعتمد الكود على الواجهة (interface)، وليس على الفئات الملموسة (concrete classes)
- إضافة أنواع جديدة تتطلب فقط تحديث المصنع
- يحصل الكود الحالي تلقائياً على إمكانية الوصول إلى الأنواع الجديدة دون تعديل
جرّب بنفسك
<?php
require_once 'DocumentFactory.php';
// قراءة المدخلات
$type = trim(fgets(STDIN));
$content = trim(fgets(STDIN));
// TODO: استخدم DocumentFactory لإنشاء نوع المستند المناسب
// TODO: استدعِ التابع render() مع المحتوى واطبع النتيجة
// TODO: في سطر جديد، اطبع "Factory benefit: Adding new types only requires updating the factory"
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)