ConstructiCat Logo
CodeBust.
Browse section ▾

المصنع المجرد (Abstract Factory).

المصنع المجرد (Abstract Factory) هو نمط تصميم إنشائي يتيح لك إنتاج عائلات من الكائنات ذات الصلة دون تحديد فئاتها الملموسة.

##Intent

المصنع المجرد (Abstract Factory) هو نمط تصميم إنشائي يتيح لك إنتاج عائلات من الكائنات ذات الصلة دون تحديد فئاتها الملموسة.

Abstract Factory pattern

##Problem

تخيل أنك تقوم بإنشاء محاكي لمتجر أثاث. يتكون كودك من فئات تمثل:

  1. عائلة من المنتجات ذات الصلة، مثلاً: Chair + Sofa + CoffeeTable.

  2. عدة بدائل لهذه العائلة. على سبيل المثال، المنتجات Chair + Sofa + CoffeeTable متوفرة في هذه البدائل: Modern، Victorian، ArtDeco.

Product families and their variants.

عائلات المنتجات وبدائلها.

أنت بحاجة إلى طريقة لإنشاء كائنات أثاث فردية بحيث تتطابق مع الكائنات الأخرى من نفس العائلة. يغضب العملاء تماماً عندما يتلقون أثاثاً غير متناسق.

الأريكة ذات الطراز الحديث لا تتطابق مع الكراسي ذات الطراز الفيكتوري.

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

##Solution

أول شيء يقترحه نمط المصنع المجرد هو الإعلان صراحةً عن واجهات لكل منتج متميز في عائلة المنتجات (مثل الكرسي أو الأريكة أو طاولة القهوة). بعد ذلك، يمكنك جعل جميع بدائل المنتجات تتبع هذه الواجهات. على سبيل المثال، يمكن لجميع بدائل الكراسي تنفيذ واجهة Chair؛ ويمكن لجميع بدائل طاولة القهوة تنفيذ واجهة CoffeeTable، وهكذا.

The Chairs class hierarchy

يجب نقل جميع بدائل الكائن نفسه إلى تدرج هرمي واحد للفئات.

الخطوة التالية هي التصريح عن المصنع المجرد—واجهة تحتوي على قائمة بدوال الإنشاء لجميع المنتجات التي تعد جزءاً من عائلة المنتجات (على سبيل المثال، createChair و createSofa و createCoffeeTable). يجب أن ترجع هذه الدوال أنواع منتجات مجردة تمثلها الواجهات التي استخرجناها سابقاً: Chair، Sofa، CoffeeTable وما إلى ذلك.

The _Factories_ class hierarchy

يتوافق كل مصنع ملموس مع بديل منتج معين.

الآن، ماذا عن بدائل المنتج؟ لكل بديل من عائلة المنتجات، نقوم بإنشاء فئة مصنع منفصلة بناءً على واجهة AbstractFactory. المصنع هو فئة تعيد منتجات من نوع معين. على سبيل المثال، يمكن لـ ModernFurnitureFactory فقط إنشاء كائنات ModernChair و ModernSofa و ModernCoffeeTable.

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

لا ينبغي للعميل أن يهتم بالفئة الملموسة للمصنع الذي يعمل معه.

لنفترض أن العميل يريد مصنعاً لإنتاج كرسي. لا يجب على العميل أن يكون على دراية بفئة المصنع، ولا يهم نوع الكرسي الذي يحصل عليه. سواء كان طرازاً حديثاً أو كرسياً على الطراز الفيكتوري، يجب على العميل معاملة جميع الكراسي بنفس الطريقة، باستخدام واجهة Chair المجردة. باستخدام هذا النهج، فإن الشيء الوحيد الذي يعرفه العميل عن الكرسي هو أنه ينفذ طريقة sitOn بطريقة ما. أيضاً، أياً كان بديل الكرسي الذي يتم إرجاعه، فإنه سيتطابق دائماً مع نوع الأريكة أو طاولة القهوة التي ينتجها نفس كائن المصنع.

يتبقى شيء واحد آخر يجب توضيحه: إذا كان العميل مكشوفاً فقط للواجهات المجردة، فما الذي ينشئ كائنات المصنع الفعلية؟ عادةً، ينشئ التطبيق كائن مصنع ملموس في مرحلة التهيئة. وقبل ذلك مباشرة، يجب على التطبيق تحديد نوع المصنع اعتماداً على التكوين أو إعدادات البيئة.

##Structure

Abstract Factory design patternAbstract Factory design pattern
  1. تعلن المنتجات المجردة (Abstract Products) عن واجهات لمجموعة من المنتجات المتميزة ولكن ذات الصلة والتي تشكل عائلة منتجات.

  2. المنتجات الملموسة (Concrete Products) هي تطبيقات مختلفة للمنتجات المجردة، مجمعة حسب البدائل. يجب تنفيذ كل منتج مجرد (كرسي/أريكة) في جميع البدائل المعطاة (فيكتوري/حديث).

  3. تعلن واجهة المصنع المجرد (Abstract Factory) عن مجموعة من الدوال لإنشاء كل من المنتجات المجردة.

  4. تقوم المصانع الملموسة (Concrete Factories) بتنفيذ دوال الإنشاء للمصنع المجرد. يتوافق كل مصنع ملموس مع بديل معين من المنتجات ويخلق فقط تلك البدائل للمنتجات.

  5. على الرغم من أن المصانع الملموسة تنشئ منتجات ملموسة، إلا أن تواقيع دوال الإنشاء الخاصة بها يجب أن ترجع المنتجات المجردة المقابلة. بهذه الطريقة لا يرتبط كود العميل الذي يستخدم المصنع بالبديل المحدد للمنتج الذي يحصل عليه من المصنع. يمكن لـ العميل (Client) العمل مع أي مصنع ملموس/بديل منتج، طالما أنه يتواصل مع كائناتها عبر الواجهات المجردة.

##Pseudocode

يوضح هذا المثال كيف يمكن استخدام نمط **المصنع المجرد (Abstract Factory)** لإنشاء عناصر واجهة مستخدم (UI) عابرة للمنصات دون ربط كود العميل بفئات UI ملموسة، مع الحفاظ على اتساق جميع العناصر التي تم إنشاؤها مع نظام التشغيل المحدد.

The class diagram for the Abstract Factory pattern example

مثال على فئات واجهة المستخدم العابرة للمنصات.

من المتوقع أن تتصرف عناصر واجهة المستخدم نفسها في تطبيق عابر للمنصات بشكل مشابه، ولكنها تبدو مختلفة قليلاً في ظل أنظمة تشغيل مختلفة. علاوة على ذلك، فإن مهمتك هي التأكد من أن عناصر واجهة المستخدم تتطابق مع نمط نظام التشغيل الحالي. لن ترغب في أن يقوم برنامجك بتقديم عناصر تحكم macOS عند تنفيذه في Windows.

تعلن واجهة المصنع المجرد عن مجموعة من دوال الإنشاء التي يمكن لكود العميل استخدامها لإنتاج أنواع مختلفة من عناصر واجهة المستخدم. تتوافق المصانع الملموسة مع أنظمة تشغيل محددة وتنشئ عناصر واجهة المستخدم التي تتطابق مع هذا النظام الخاص.

يعمل الأمر على النحو التالي: عند تشغيل التطبيق، فإنه يتحقق من نوع نظام التشغيل الحالي. يستخدم التطبيق هذه المعلومات لإنشاء كائن مصنع من فئة تتطابق مع نظام التشغيل. يستخدم باقي الكود هذا المصنع لإنشاء عناصر واجهة المستخدم. هذا يمنع إنشاء العناصر الخاطئة.

باستخدام هذا النهج، لا يعتمد كود العميل على الفئات الملموسة للمصانع وعناصر واجهة المستخدم طالما أنه يعمل مع هذه الكائنات عبر واجهاتها المجردة. يتيح هذا أيضاً لكود العميل دعم المصانع الأخرى أو عناصر واجهة المستخدم التي قد تضيفها في المستقبل.

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

// تعلن واجهة المصنع المجرد عن مجموعة من الدوال التي
// تعيد منتجات مجردة مختلفة. تسمى هذه المنتجات عائلة
// وترتبط بموضوع أو مفهوم عالي المستوى. عادةً ما تكون منتجات
// عائلة واحدة قادرة على التعاون فيما بينها. قد تحتوي عائلة
// المنتجات على عدة بدائل، ولكن منتجات بديل واحد غير متوافقة
// مع منتجات بديل آخر.
interface GUIFactory is
    method createButton():Button
    method createCheckbox():Checkbox


// تنتج المصانع الملموسة عائلة من المنتجات التي تنتمي
// إلى بديل واحد. يضمن المصنع أن المنتجات الناتجة متوافقة.
// ترجع تواقيع دوال المصنع الملموس منتجاً مجرداً،
// بينما داخل الدالة يتم إنشاء نسخة من منتج ملموس.
class WinFactory implements GUIFactory is
    method createButton():Button is
        return new WinButton()
    method createCheckbox():Checkbox is
        return new WinCheckbox()

// لكل مصنع ملموس بديل منتج مقابل.
class MacFactory implements GUIFactory is
    method createButton():Button is
        return new MacButton()
    method createCheckbox():Checkbox is
        return new MacCheckbox()


// يجب أن يكون لكل منتج متميز من عائلة منتجات واجهة
// أساسية. يجب أن تنفذ جميع بدائل المنتج هذه الواجهة.
interface Button is
    method paint()

// يتم إنشاء المنتجات الملموسة بواسطة المصانع الملموسة المقابلة.
class WinButton implements Button is
    method paint() is
        // تقديم زر بنمط Windows.

class MacButton implements Button is
    method paint() is
        // تقديم زر بنمط macOS.

// هذه هي الواجهة الأساسية لمنتج آخر. يمكن لجميع المنتجات
// التفاعل مع بعضها البعض، ولكن التفاعل المناسب ممكن فقط بين
// منتجات نفس البديل الملموس.
interface Checkbox is
    method paint()

class WinCheckbox implements Checkbox is
    method paint() is
        // تقديم خانة اختيار بنمط Windows.

class MacCheckbox implements Checkbox is
    method paint() is
        // تقديم خانة اختيار بنمط macOS.


// يعمل كود العميل مع المصانع والمنتجات فقط من خلال
// الأنواع المجردة: GUIFactory و Button و Checkbox. يتيح
// هذا تمرير أي فئة فرعية من المصنع أو المنتج إلى كود العميل دون كسرها.
class Application is
    private field factory: GUIFactory
    private field button: Button
    constructor Application(factory: GUIFactory) is
        this.factory = factory
    method createUI() is
        this.button = factory.createButton()
    method paint() is
        button.paint()


// يختار التطبيق نوع المصنع بناءً على التكوين الحالي أو
// إعدادات البيئة وينشئه في وقت التشغيل (عادةً في مرحلة التهيئة).
class ApplicationConfigurator is
    method main() is
        config = readApplicationConfigFile()

        if (config.OS == "Windows") then
            factory = new WinFactory()
        else if (config.OS == "Mac") then
            factory = new MacFactory()
        else
            throw new Exception("Error! Unknown operating system.")

        Application app = new Application(factory)

##Applicability

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

يوفر لك المصنع المجرد واجهة لإنشاء كائنات من كل فئة في عائلة المنتجات. وطالما أن كودك ينشئ كائنات عبر هذه الواجهة، فلا داعي للقلق بشأن إنشاء بديل خاطئ لمنتج ما لا يتطابق مع المنتجات التي تم إنشاؤها بالفعل بواسطة تطبيقك.

فكر في تطبيق المصنع المجرد عندما يكون لديك فئة تحتوي على مجموعة من دوال المصنع (Factory Methods) التي تشوش على مسؤوليتها الأساسية.

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

##How to Implement

  1. قم برسم مصفوفة توضح أنواع المنتجات المتميزة مقابل بدائل هذه المنتجات.

  2. قم بالتصريح عن واجهات المنتجات المجردة لجميع أنواع المنتجات. ثم اجعل جميع فئات المنتجات الملموسة تنفذ هذه الواجهات.

  3. قم بالتصريح عن واجهة المصنع المجرد مع مجموعة من دوال الإنشاء لجميع المنتجات المجردة.

  4. قم بتنفيذ مجموعة من فئات المصنع الملموسة، واحدة لكل بديل من بدائل المنتج.

  5. قم بإنشاء كود تهيئة المصنع في مكان ما في التطبيق. يجب أن ينشئ نسخة من إحدى فئات المصنع الملموسة، اعتماداً على تكوين التطبيق أو البيئة الحالية. مرر كائن المصنع هذا إلى جميع الفئات التي تبني المنتجات.

  6. ابحث في الكود عن جميع الاستدعاءات المباشرة لمنشئات المنتجات. واستبدلها باستدعاءات لدالة الإنشاء المناسبة في كائن المصنع.

##Pros & Cons

Pros
  • يمكنك التأكد من أن المنتجات التي تحصل عليها من المصنع متوافقة مع بعضها البعض.
  • تتجنب الاقتران الوثيق بين المنتجات الملموسة وكود العميل.
  • مبدأ المسؤولية الفردية. يمكنك نقل كود إنشاء المنتج إلى مكان واحد، مما يسهل صيانة الكود ودعمه.
  • مبدأ المفتوح/المغلق. يمكنك تقديم بدائل جديدة للمنتجات دون كسر كود العميل الحالي.
Cons
  • قد يصبح الكود أكثر تعقيداً مما ينبغي، حيث يتم إدخال الكثير من الواجهات والفئات الجديدة مع هذا النمط.

##Relations with Other Patterns

  • تبدأ العديد من التصميمات باستخدام Factory Method (أقل تعقيداً وأكثر قابلية للتخصيص عبر الفئات الفرعية) وتتطور نحو Abstract Factory، أو Prototype، أو Builder (أكثر مرونة، ولكنها أكثر تعقيداً).

  • Builder يركز على إنشاء كائنات معقدة خطوة بخطوة. Abstract Factory يتخصص في إنشاء عائلات من الكائنات ذات الصلة. يعيد _Abstract Factory_ المنتج على الفور، بينما يسمح لك _Builder_ بتشغيل بعض خطوات البناء الإضافية قبل جلب المنتج.

  • غالباً ما تعتمد فئات Abstract Factory على مجموعة من Factory Methods، ولكن يمكنك أيضاً استخدام Prototype لتركيب الدوال على هذه فئات.

  • Abstract Factory يمكن أن يعمل كبديل لـ Facade عندما تريد فقط إخفاء طريقة إنشاء كائنات النظام الفرعي عن كود العميل.

  • يمكنك استخدام Abstract Factory مع Bridge. هذا الاقتران مفيد عندما لا تعمل بعض التجريدات التي يحددها _الجسر_ إلا مع تطبيقات محددة. في هذه الحالة، يمكن لـ _المصنع المجرد_ تغليف هذه العلاقات وإخفاء التعقيد عن كود العميل.

  • يمكن تنفيذ كل من Abstract Factories و Builders و Prototypes كـ Singletons.

##Extra Content

  • اقرأ مقارنة المصانع لمعرفة المزيد عن الاختلافات بين أنماط ومفاهيم المصانع المختلفة.