نمط المصنع المجرد

من موسوعة حسوب
مراجعة 17:27، 28 ديسمبر 2018 بواسطة أسامه-دمراني (نقاش | مساهمات) (إدخال 2.0: محتوى)

نمط المصنع المجرَّد هو نمط تصميم إنشائي يسمح لك بإنتاج عائلات من الكائنات المرتبطة ببعضها دون تحديد فئاتهم الحقيقية. (انظر ش.1)

المشكلة

تخيل أنك تنشئ محاكيًا لمتجر أثاث، وستتكون شيفرتك من فئات تمثل ما يلي:

  1. عائلة من المنتجات المرتبطة ببعضها، لنقل Chair + Sofa + CoffeeTable.
  2. عدة متغيرات من تلك العائلة، فمثلًا ستكون منتجات Chair + Sofa + CoffeeTable متاحة في المتغيرات التالية: IKEA، VictorianStyle، ArtDeco. (انظر ش.2).

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

الحل

أول ما يقترحه نمط المصنع المجرد هو أن تصرح بوضوح عن واجهات لكل نوع من المنتجات في عائلة المنتجات (مثل الكرسي Chair، أو الأريكة Sofa، أو منضدة القهوة Coffee table)، ثم يمكنك بعدها أن تجعل كل متغيرات المنتجات تتبع تلك الواجهات. فمثلًا تستخدم كل متغيرات الكراسي واجهة Chair، وكل متغيرات مناضد القهوة تستخدم واجهة CoffeeTable، وهكذا. (انظر ش.4)

والخطوة التالية هي التصريح عن واجهة AbstractFactory مع قائمة من أساليب الإنشاء لكل المنتجات الموجودة داخل عائلة المنتجات (مثلًا createChair، و createSofa، وcreateCoffeeTable)، ويجب أن تعيد تلك الأساليب أنواعًا مجردة من المنتجات ممثلة بالواجهات التي استخرجناها من قبل: Chair و Sofa و CoffeeTable، وهكذا. (انظر ش.5). ولكل متغير من عائلة المنتجات ننشئ فئة مصنع منفصلة بناءً على واجهة المصنع المجرد AbstractFactory، والمصنع هو فئة تعيد منتجات من نوع بعينه، ففئة مصنع أيكيا مثلًا IKEAFactory لا تنشئ إلا كائنات IKEAChair و IKEASofa و IKEACoffeeTable.

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

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

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

البُنية

  1. تصرح المنتجات المجردة عن واجهات لمجموعة من المنتجات المستقلة التي ترتبط ببعضها وتكوّن عائلة منتجات. (انظر ش.6)
  2. المنتجات الحقيقية (Concrete Products) هي استخدامات مختلفة للمنتجات المجردة مجموعةً مع بعضها وفقًا للأنواع (Variants)، ويجب أن يُستخدم كل منتج مجرد في كل الأنواع المعطاة (فيكتوري، حديث، ... إلخ.)
  3. تصرح واجهة المصنع المجرد (Abstract factory) عن مجموعة من الأساليب لإنشاء كل منتج من المنتجات المجردة.
  4. تستخدم المصانع الحقيقية (Concrete Factories) أساليب إنشاء من المصنع المجرد، ويطابق كل مصنع حقيقي نوعًا من المنتجات وينشئ الأنواع المختلفة (variants) لذلك المنتج فقط.
  5. رغم أن كل المصانع الحقيقية تمثّل منتجات حقيقية إلا أن توقيعات أساليب إنشائها يجب أن تعيد المنتجات المجردة التي تطابقها، وذلك من أجل ألا تُقرن شيفرة العميل التي تستخدم مصنعًا إلى نوع محدد من أنواع منتج تحصل عليه من ذلك المصنع. ويستطيع العميل أن يعمل مع أي نوع من المصانع / المنتجات الحقيقية طالما أنه يتواصل مع عناصرها من خلال واجهات مجردة.

مثال توضيحي

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

ينبغي أن تتصرف نفس عناصر الواجهة في تطبيق موجه لمنصات مختلفة بنفس الأسلوب وإن ظهرت بشكل مختلف قليلًا على نظم التشغيل المختلفة، وهي مهمتك أن تتأكد أن عناصر الواجهة تطابق نسق النظام الحالي، فلا يُظهر برنامجك أزرار تحكم لنظام ماك في حين أنه يعمل على ويندوز. وتصرح واجهة المصنع المجرد عن مجموعة أساليب إنشائية يمكن لشيفرة العميل أن تستخدمها لإنتاج أنواع مختلفة من عناصر الواجهة، وتتسق المصانع الحقيقية مع أنظمة التشغيل لتنشئ عناصر واجهة تماثل أسلوب الواجهة في تلك الأنظمة. (انظر ش.7)

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

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

// تصرح واجهة المصنع المجرد عن مجموعة أساليب تعيد منتجات مجردة مختلفة.
// يطلق على هذه المنتجات اسم "عائلة" وترتبط مع بعضها بعامل مشترك موجود فيها جميعًا.
// منتجات العائلة الواحدة قادرة على التعاون فيما بينها.
// عائلة المنتجات قد تحتوي بضعة أنواع، لكن منتجات النوع الواحد لا تتوافق مع منتجات نوع آخر.
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()


// (base interface) يجب أن يكون لكل منتج مستقل من عائلة المنتجات واجهةً أساسية.
// ويجب أن تستخدم جميع صور المنتج هذه الواجهة.
interface Button is
    method paint()

// Concrete products are created by corresponding concrete
// factories.
// تُنشأ المنتجات الحقيقية بواسطة المصانع الحقيقية المتوافقة معها.
class WinButton implements Button is
    method paint() is
        // أخرج زرًا لنظام ويندوز.

class MacButton implements Button is
    method paint() is
        // أخرج زرًا لنظام ماك.

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

class WinCheckbox implements Checkbox is
    method paint() is
        // أخرج صندوق اختيار بأسلوب ويندوز.

class MacCheckbox implements Checkbox is
    method paint() is
        // أخرج صندوق اختيار بأسلوب نظام ماك.


// Checkbox و Button و GUIFactory تعمل شيفرة العميل مع المصانع والمنتجات من خلال أنواع مجردة فقط هي.
// يسمح هذا لك بتمرير أي فئة فرعية لمصنع أو منتج إلى شيفرة العميل دون تعطيلها.
class Application is
    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 == "Web") then
            factory = new MacFactory()
        else
            throw new Exception("Error! Unknown operating system.")

        Application app = new Application(factory)

قابلية التطبيق

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

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

  • استخدم المصنع المجرد عندما يكون لديك فئة فيها مجموعة من أساليب المصنع التي تشوش مسؤوليتها الأساسية.

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

كيفية الاستخدام