نمط Factory و Abstract Factory
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 92 من 104.
يقوم نمط المصنع (Factory pattern) بتفويض إنشاء الكائنات إلى طريقة أو فئة منفصلة، مما يتيح لك إنشاء كائنات دون تحديد فئتها الدقيقة. هذا مفيد عندما يكون منطق الإنشاء معقداً أو عندما تريد فصل كود العميل عن التنفيذات الملموسة.
يستخدم المصنع البسيط (Simple Factory) طريقة تُرجع أنواعاً مشتقة مختلفة بناءً على المدخلات:
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// دالة المصنع
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}يأخذ نمط المصنع المجرد (Abstract Factory) هذا المفهوم إلى أبعد من ذلك من خلال إنشاء عائلات من الكائنات المرتبطة. فبدلاً من وجود طريقة مصنع واحدة، يكون لديك واجهة مصنع تحتوي على طرق إنشاء متعددة:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// Abstract Factory
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};استخدم Factory عندما تحتاج إلى مرونة في إنشاء الكائنات، وAbstract Factory عندما تحتاج إلى ضمان عمل الكائنات المرتبطة معاً بشكل متسق.
التحدي
سهللنقم ببناء نظام Document Generator (مولد مستندات) باستخدام نمطي Factory و Abstract Factory. ستقوم بإنشاء نظام يمكنه إنشاء أنواع مختلفة من المستندات (تقارير وفواتير) لتنسيقات إخراج مختلفة (PDF و HTML)، مما يوضح كيف تفصل المصانع (factories) عملية إنشاء الكائنات عن كود العميل.
ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:
Document.h: حدد التسلسل الهرمي لمنتجات المستندات الخاصة بك.أنشئ فئة أساسية مجردة (abstract base class) باسم
Documentمع دالة افتراضية بحتة (pure virtual method) تسمىgenerate()تعيد سلسلة نصية تصف المستند الذي تم إنشاؤه، ومُهدم افتراضي (virtual destructor).ثم أنشئ أربع فئات مستندات ملموسة (concrete document classes):
PDFReport— تعيدgenerate()القيمة"Generating PDF Report"HTMLReport— تعيدgenerate()القيمة"Generating HTML Report"PDFInvoice— تعيدgenerate()القيمة"Generating PDF Invoice"HTMLInvoice— تعيدgenerate()القيمة"Generating HTML Invoice"
DocumentFactory.h: قم بتنفيذ كل من دالة مصنع بسيطة (simple factory function) ومصنع مجرد (abstract factory).أولاً، أنشئ دالة مصنع بسيطة تسمى
createDocumentتأخذ سلسلتين نصيتين: نوع المستند ("report"أو"invoice") والتنسيق ("pdf"أو"html"). يجب أن تعيدstd::unique_ptr<Document>يشير إلى المستند الملموس المناسب. أعدnullptrللمجموعات غير الصالحة.ثم أنشئ فئة مصنع مجردة تسمى
DocumentFactoryمع دالتين افتراضيتين بحتتين:createReport()— تعيدstd::unique_ptr<Document>createInvoice()— تعيدstd::unique_ptr<Document>
قم بتنفيذ مصنعين ملموسين:
PDFFactory— ينشئPDFReportوPDFInvoiceHTMLFactory— ينشئHTMLReportوHTMLInvoice
DocumentService.h: أنشئ خدمة تستخدم المصنع المجرد.حدد فئة
DocumentServiceتأخذstd::unique_ptr<DocumentFactory>في منشئها (constructor). يجب أن تحتوي على دالة تسمىgenerateAllDocuments()تستخدم المصنع لإنشاء كل من تقرير وفاتورة، ثم تطبع نتيجة استدعاءgenerate()على كل منهما، واحدة في كل سطر.main.cpp: استعرض كلا نهجي المصنع.اقرأ مدخلين:
- نوع المستند (
reportأوinvoice) - التنسيق (
pdfأوhtml)
أولاً، استخدم دالة المصنع البسيطة لإنشاء مستند بناءً على المدخلات. اطبع نتيجة استدعاء
generate()عليه.ثم اطبع سطراً فارغاً.
بعد ذلك، أنشئ
DocumentServiceمع المصنع المناسب بناءً على مدخل التنسيق (استخدمPDFFactoryلـpdf، وHTMLFactoryلـhtml). استدعِgenerateAllDocuments()لتوضيح كيف ينشئ المصنع المجرد عائلة من المستندات ذات الصلة.- نوع المستند (
على سبيل المثال، مع المدخلات report و pdf:
Generating PDF Report
Generating PDF Report
Generating PDF Invoiceمع المدخلات invoice و html:
Generating HTML Invoice
Generating HTML Report
Generating HTML Invoiceلاحظ كيف يقوم المصنع البسيط بإنشاء مستند واحد بناءً على كلا المعاملين، بينما يضمن المصنع المجرد أن جميع المستندات التي تنشئها الخدمة تشترك في نفس التنسيق — وهذه هي الفائدة الرئيسية لـ Abstract Factory عندما تحتاج إلى عائلات من الكائنات ذات الصلة التي تعمل معاً بشكل متسق.
ورقة مرجعية
يقوم نمط المصنع (Factory pattern) بتفويض إنشاء الكائنات إلى طريقة أو فئة منفصلة، مما يسمح لك بإنشاء كائنات دون تحديد فئتها الدقيقة. يؤدي هذا إلى فصل كود العميل عن التنفيذات الملموسة.
يستخدم المصنع البسيط (simple factory) دالة تُرجع أنواعًا مشتقة مختلفة بناءً على المدخلات:
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// Factory function
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}يقوم نمط المصنع المجرد (Abstract Factory pattern) بإنشاء عائلات من الكائنات المرتبطة. بدلاً من طريقة مصنع واحدة، يكون لديك واجهة مصنع تحتوي على طرق إنشاء متعددة:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// Abstract Factory
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};استخدم المصنع (Factory) عندما تحتاج إلى إنشاء كائنات بمرونة. استخدم المصنع المجرد (Abstract Factory) عندما تحتاج إلى التأكد من أن الكائنات المرتبطة تعمل معًا بشكل متسق كعائلة.
جرّب بنفسك
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// قراءة المدخلات
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: استخدم دالة المصنع البسيطة (simple factory function) لإنشاء مستند
// بناءً على مدخلات documentType و format
// اطبع نتيجة استدعاء ()generate عليه
// اطبع سطراً فارغاً
cout << endl;
// TODO: قم بإنشاء DocumentService مع المصنع المناسب
// بناءً على مدخل format (استخدم PDFFactory لـ "pdf"، و HTMLFactory لـ "html")
// استدعِ ()generateAllDocuments لاستعراض المصنع المجرد (abstract factory)
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةبناء وتجميع (Compilation) لغة C++ملفات الـ Header وملفات الـ Sourceالـ Namespaces والـ Scopeمقدمة في الـ OOP في C++الـ Classes مقابل الـ Objectsالمؤشر 'this'الـ Methods (الدوال الأعضاء)الـ Attributes (بيانات الأعضاء)أساسيات الـ Ctors والـ Dtorsمراجعة - آلة حاسبة بسيطة4خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticدوال الـ Getters والـ Settersدوال الأعضاء الثابتة (Const)الكلمة المفتاحية Mutableالدوال والمتغيرات الساكنة (Static)الدوال والفئات الصديقة (Friend)مراجعة - مدير الحساب البنكي7الوراثة
الوراثة الأساسيةمستويات الوصول في الوراثةترتيب استدعاء الـ Ctor والـ Dtorإعادة تعريف الدوال (Method Overriding)الدوال الافتراضية والـ VTableالوراثة المتعددةالوراثة الافتراضيةمراجعة - هيكلية الموظفين10نظرة عامة على STL
نظرة عامة وفلسفة STLحاويات STLالـ Iteratorsخوارزميات STLالـ Functors وتعبيرات Lambdaمراجعة - تكرار الكلمات13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy2إدارة الذاكرة
ذاكرة Stack مقابل Heapالمؤشرات والمراجعالذاكرة الديناميكية (new/delete)المؤشرات الذكية في C++RAII في C++مراجعة - مدير المصفوفات الديناميكية5التغليف (Encapsulation)
محددات الوصول في C++محددات الوصول بشكل متعمقإخفاء المعلوماتStruct مقابل Classالأصناف المتداخلة والداخليةمراجعة - نظام سجلات الطلاب8تعدد الأشكال (Polymorphism)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالتحميل الزائد للدوال (Function Overloading)مراجعة الدوال الافتراضية (Virtual Functions)الدوال الافتراضية البحتة (Pure Virtual Functions)الأصناف المجردة (Abstract Classes)تصميم الواجهات (Interface) في C++التحويل الديناميكي (Dynamic Casting) و RTTIملخص - حاسبة الأشكال11مفاهيم OOP المتقدمة
التركيب مقابل الوراثةالـ Mixins عبر CRTPنمط Pimplمحو النوع (Type Erasure)أصناف Enum والأنواع القويةمعالجة الاستثناءات في OOPتسلسلات الاستثناءات المخصصة14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط قالب الطريقةنمط الحالةنمط التركيبRAII كنمط3المنشئات والموادم
المنشئ الافتراضيالمنشئ ذو المعاملاتمنشئ النسخمنشئ النقلقوائم تهيئة المنشئالمنشئات المفوضةتعمق في الموادمقاعدة الثلاثة / الخمسة / الصفرمراجعة - فئة String6تحميل العوامل (Operator Overloading)
مقدمة في تحميل العواملتحميل العوامل الحسابيةتحميل عوامل المقارنةعوامل Streamتحميل عامل التعيينتحميل العوامل [] و ()عوامل تحويل النوعمراجعة - Matrix Class9القوالب
قوالب الدوالقوالب الأصنافتخصيص القوالبالقوالب المتغيرةأساسيات SFINAE و Type Traitsمراجعة - الحاويات العامة