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

نمط 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 عندما تحتاج إلى ضمان عمل الكائنات المرتبطة معاً بشكل متسق.

challenge icon

التحدي

سهل

لنقم ببناء نظام 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 و PDFInvoice
    • HTMLFactory — ينشئ HTMLReport و HTMLInvoice
  • DocumentService.h: أنشئ خدمة تستخدم المصنع المجرد.

    حدد فئة DocumentService تأخذ std::unique_ptr<DocumentFactory> في منشئها (constructor). يجب أن تحتوي على دالة تسمى generateAllDocuments() تستخدم المصنع لإنشاء كل من تقرير وفاتورة، ثم تطبع نتيجة استدعاء generate() على كل منهما، واحدة في كل سطر.

  • main.cpp: استعرض كلا نهجي المصنع.

    اقرأ مدخلين:

    1. نوع المستند (report أو invoice)
    2. التنسيق (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;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming

13أنماط التصميم - الجزء الأول

مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy