نمط المصنع (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 تتطلب تغييرات فقط في المصنع، وليس في كل مكان يتم فيه إنشاء المستندات. هذا الفصل يجعل قاعدة الكود الخاصة بك أسهل في الصيانة والاختبار.
التحدي
سهللنقم ببناء نظام إشعارات باستخدام نمط المصنع (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 وطباعة النتيجة
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List10أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singleton الآمن للخيوط (Thread-Safe)نمط المصنع (Factory Pattern)نمط المراقب (Events)نمط الاستراتيجية (Strategy Pattern)2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection