1.4. Abstract Factory

1.4.1. تعریف

الگوی Abstract Factory یک الگو از نوع الگوهای سازنده یا Creational محسوب میشه که کار اصلی اون ایجاد شی بدون اطلاع سطوح بالاتر از جزئیات نحوه ایجاد شی هست.

تفاوتی که با الگوی Factory Method داره اینه که اینجا کلاس Factory ما مجموعه یا خانواده ای از آبجکت های مرتبط رو تولید می کنه.

1.4.2. اجزاء

الگوی طراحی Abstract Factory از چند بخش اصلی تشکیل میشه:

  1. Abstract Factory: یک کلاس abstract که interface ایجاد مجموعه ای از آبجکت های مرتبط به هم رو مشخص می کنه.

  2. کلاس های concrete که abstract مورد قبل رو پیاده سازی می کنه و مجموعه ای از آبجکت های مرتبط رو ایجاد می کنه.

  3. کلاس های abstract که interface آبجکت های خروجی یا ایجاد شده رو مشخص می کنه.

  4. و در نهایت کلاس های مربوط به آبجکت ایجاد شده که مورد سوم رو پیاده سازی می کنن.

UML of Abstract Factory

Giacomo Ritucci, CC BY-SA 3.0, via Wikimedia Commons

1.4.3. چه زمانی استفاده میشه؟

این الگو زمانی استفاده میشه که قصد داریم مجموعه ای از آبجکت ها از یک خانواده رو تولید کنیم و نمی خوایم سطوح بالاتر برنامه رو درگیر نحوه ی ایجادشون کنیم.

با این الگو مطمئن میشیم آبجکت های ایجاد شده کاملا با هم مطابقت دارن و از یک خانواده مشابه هستن.

../../_images/categories.png

از وب سایت: refactoring.guru

Caution

✅ مزایای استفاده

استفاده از این الگو باعث میشه در سطح بالای برنامه وابستگی به سطوح پایین و انواع پیاده سازی ها از بین بره و کلاس های Factory مسئول آماده کردن تنظیمات و سپس ایجاد اشیاء بشن و این مسئولیت رو از روی دوش سطوح بالاتر برنامه بردارن.

Warning

❌ معایب استفاده

پیچیده تر شدن برنامه به علت نیاز به تعریف انواع interface و concrete در برنامه

1.4.4. کاربرد عملی

تصور کنید در حال پیاده سازی یک فریمورک رابط کاربری هستیم که قراره از سیستم عامل های مختلف پشتیبانی کنه.

در این شرایط ما یک سری المان های UI داریم که باید برای سیستم عامل های مختلف تعریف بشن.

در واقع یک سری المان یا آبجکت هم خانواده که باید توسط Factory های مربوط به هر سیستم عامل تولید بشن.

1.4.5. پیاده سازی

ابتدا برای المان های مورد نظرمون که شامل button و checkbox هستن interface تعریف می کنیم:

 1<?php
 2
 3// Abstract Product interface
 4interface Button {
 5    public function render(): string;
 6}
 7
 8// Abstract Product interface
 9interface Checkbox {
10    public function render(): string;
11}

حالا المان ها رو برای سیستم عامل های مختلف پیاده سازی می کنیم:

 1<?php
 2
 3// Concrete Product class
 4class WindowsButton implements Button {
 5    public function render(): string {
 6        return "Windows style button\n";
 7    }
 8}
 9
10// Concrete Product class
11class MacOSButton implements Button {
12    public function render(): string {
13        return "MacOS style button\n";
14    }
15}
16
17// Concrete Product class
18class WindowsCheckbox implements Checkbox {
19    public function render(): string {
20        return "Windows style checkbox\n";
21    }
22}
23
24// Concrete Product class
25class MacOSCheckbox implements Checkbox {
26    public function render(): string {
27        return "MacOS style checkbox\n";
28    }
29}

یک interface برای Factory های مورد نظر داریم:

1<?php
2
3// Abstract Factory interface
4interface UIFactory {
5    public function createButton(): Button;
6    public function createCheckbox(): Checkbox;
7}

حالا خود Factory ها رو تعریف می کنیم که کارشون برگردوندن المان های مختلف UI هست:

 1<?php
 2
 3// Concrete Factory class
 4class WindowsUIFactory implements UIFactory {
 5    public function createButton(): Button {
 6        return new WindowsButton();
 7    }
 8
 9    public function createCheckbox(): Checkbox {
10        return new WindowsCheckbox();
11    }
12}
13
14// Concrete Factory class
15class MacOSUIFactory implements UIFactory {
16    public function createButton(): Button {
17        return new MacOSButton();
18    }
19
20    public function createCheckbox(): Checkbox {
21        return new MacOSCheckbox();
22    }
23}

کد سطح بالای ما در برنامه که از این ساختار و الگو استفاده می کنه به این شکل هست:

1<?php
2
3// Client code
4function createUI(UIFactory $factory): string {
5    $button = $factory->createButton();
6    $checkbox = $factory->createCheckbox();
7    return $button->render() . $checkbox->render();
8}

1.4.6. نحوه فراخوانی

1// Usage
2echo createUI(new WindowsUIFactory()); // Output: Windows style button Windows style checkbox
3echo createUI(new MacOSUIFactory()); // Output: MacOS style button MacOS style checkbox

اینجا دیگه اگر سیستم عامل جدیدی اضافه بشه نیاز به تغییر ساختار کد سطح بالای ما یعنی createUI نیست و فقط کافیه به عنوان ورودی کلاس سیستم عامل جدید رو بدیم.

و این ما رو یاد چی میندازه؟ درسته Dependency Inversion

برای اطلاعات بیشتر می تونید ویدیوی مربوط به Dependency Inversion رو ببینید:

1.4.7. آموزش ویدیویی