Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط 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، فما عليك سوى تحديث المصنع. وسيحصل كل الكود الذي يستخدم المصنع تلقائياً على إمكانية الوصول إلى النوع الجديد دون أي تعديل.

challenge icon

التحدي

سهل

لنقم ببناء نظام منشئ مستندات باستخدام نمط المصنع (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"

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming