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

نمط المصنع (Factory Pattern)

جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 53 من 70.

يُعد نمط المصنع (Factory pattern) نمطاً إنشائياً يفوض عملية إنشاء الكائنات إلى طريقة (method) أو فئة (class) منفصلة. وبدلاً من استخدام new مباشرة في الكود الخاص بك، فإنك تطلب من المصنع إنشاء كائنات لك. هذا يؤدي إلى مركزية منطق الإنشاء ويجعل الكود الخاص بك أكثر مرونة.

تخيل سيناريو تحتاج فيه إلى إنشاء أنواع مختلفة من المستندات. بدون استخدام المصنع (factory)، يصبح الكود الخاص بك مرتبطاً بشكل وثيق بفئات (classes) محددة:

// بدون Factory - منطق إنشاء مبعثر
IDocument doc;
if (type == "pdf")
    doc = new PdfDocument();
else if (type == "word")
    doc = new WordDocument();

يقوم المصنع بتغليف عملية اتخاذ القرار هذه في مكان واحد:

public interface IDocument
{
    void Open();
}

public class PdfDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening PDF");
}

public class WordDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening Word");
}

public class DocumentFactory
{
    public IDocument Create(string type)
    {
        return type switch
        {
            "pdf" => new PdfDocument(),
            "word" => new WordDocument(),
            _ => throw new ArgumentException("Unknown type")
        };
    }
}

الآن، يطلب كود العميل ببساطة ما يحتاجه دون معرفة الأصناف الملموسة (concrete classes):

var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open();  // فتح ملف PDF

يتألق نمط المصنع (Factory pattern) عندما تحتاج إلى إضافة أنواع جديدة لاحقاً. إضافة SpreadsheetDocument تتطلب تغييرات فقط في المصنع، وليس في كل مكان يتم فيه إنشاء المستندات. هذا الفصل يجعل قاعدة الكود الخاصة بك أسهل في الصيانة والاختبار.

challenge icon

التحدي

سهل

لنقم ببناء نظام إشعارات باستخدام نمط المصنع (Factory pattern). بدلاً من إنشاء أنواع إشعارات مختلفة مباشرةً في جميع أنحاء التعليمات البرمجية الخاصة بك، ستقوم بتركيز منطق الإنشاء في فئة مصنع (factory class) تقرر نوع الإشعار الذي سيتم إنشاؤه بناءً على معرف نصي بسيط.

ستقوم بتنظيم التعليمات البرمجية الخاصة بك عبر ثلاثة ملفات:

  • Notification.cs: قم بتعريف واجهة INotification في مساحة الأسماء Notifications مع طريقة واحدة Send(string message) تعيد سلسلة نصية. ثم قم بإنشاء ثلاث فئات تنفذ هذه الواجهة:
    • EmailNotification - تعيد "Email: {message}"
    • SmsNotification - تعيد "SMS: {message}"
    • PushNotification - تعيد "Push: {message}"
  • NotificationFactory.cs: قم بإنشاء فئة NotificationFactory في نفس مساحة الأسماء مع طريقة Create(string type) تعيد كائن INotification المناسب بناءً على سلسلة النوع النصية:
    • "email" تعيد EmailNotification
    • "sms" تعيد SmsNotification
    • "push" تعيد PushNotification
    لأي نوع غير معروف، قم بإعادة EmailNotification كافتراضي.
  • Program.cs: اجمع كل شيء معاً عن طريق إنشاء مثيل للمصنع واستخدامه لإنشاء إشعارات بناءً على المدخلات. يتولى المصنع جميع عمليات اتخاذ القرار بشأن الفئة الملموسة (concrete class) التي سيتم إنشاؤها.

ستتلقى مدخلين:

  • نوع الإشعار (على سبيل المثال، sms)
  • الرسالة المراد إرسالها (على سبيل المثال، Your order has shipped)

استخدم المصنع الخاص بك لإنشاء نوع الإشعار المناسب، ثم استدعِ طريقة Send الخاصة به مع الرسالة واطبع النتيجة.

على سبيل المثال، إذا كانت المدخلات هي push و Meeting in 5 minutes، فيجب أن تكون النتيجة:

Push: Meeting in 5 minutes

لاحظ كيف أن الكود الرئيسي الخاص بك لا يستخدم أبداً new EmailNotification() أو ما شابه - فهو ببساطة يطلب من المصنع ما يحتاجه. إذا قمت لاحقاً بإضافة SlackNotification، فستحتاج فقط إلى تحديث المصنع، وليس كل مكان يتم فيه إنشاء الإشعارات!

ورقة مرجعية

يُعد نمط المصنع (Factory pattern) نمطاً إنشائياً يفوض عملية إنشاء الكائنات إلى طريقة أو فئة منفصلة، مما يركز منطق الإنشاء ويجعل الكود أكثر مرونة.

بدون استخدام المصنع، تكون عملية إنشاء الكائنات مبعثرة ومترابطة بشكل وثيق:

// بدون المصنع - منطق إنشاء مبعثر
IDocument doc;
if (type == "pdf")
    doc = new PdfDocument();
else if (type == "word")
    doc = new WordDocument();

مع استخدام المصنع، يتم تغليف منطق الإنشاء في مكان واحد:

public interface IDocument
{
    void Open();
}

public class PdfDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening PDF");
}

public class WordDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening Word");
}

public class DocumentFactory
{
    public IDocument Create(string type)
    {
        return type switch
        {
            "pdf" => new PdfDocument(),
            "word" => new WordDocument(),
            _ => throw new ArgumentException("Unknown type")
        };
    }
}

يطلب كود العميل الكائنات دون الحاجة لمعرفة الفئات الملموسة (concrete classes):

var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open();  // Opening PDF

يجعل نمط المصنع إضافة أنواع جديدة أسهل - حيث تقتصر التغييرات على المصنع، ولا تكون مبعثرة في كامل قاعدة الكود.

جرّب بنفسك

using System;
using Notifications;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة المدخلات
        string type = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: إنشاء مثيل من NotificationFactory

        // TODO: استخدام المصنع (factory) لإنشاء الإشعار المناسب

        // TODO: استدعاء الطريقة Send وطباعة النتيجة
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming