1.3. Factory Method

1.3.1. تعریف

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

تفاوتی که با دیگر الگوهای Factory داره اینه که کلاس Factory اصلی یک سری زیرکلاس داره که هر کدوم با استراتژی متفاوتی به ایجاد اشیاء میپردازن که البته این اشیاء ایجاد شده هم همگی interface مشابهی رو پیاده سازی می کنن.

1.3.2. اجزاء

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

  1. کلاس abstract یا interface والد Factory.

  2. زیرکلاس های Factory که هر کدوم مسئول ساخت اشیاء از انواع مختلف هستن.

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

  4. و در نهایت زیرکلاس هایی که کلاس مورد سوم رو پیاده سازی می کنن و انواع مختلف اشیاء نهایی رو مشخص می کنن.

Structure of Factory Method

تصویر از وب سایت refactoring.guru

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

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

از مهم ترین دستاوردهای استفاده از این الگوی طراحی در برنامه میشه کمک به رعایت اصول Single Responsibility، Open/Closed Principle و Dependency Inversion از اصول SOLID رو نام برد.

اگر میخواهید از یک الگوی Creational استفاده کنید، اما مطمئن نیستید که کدام یک را انتخاب کنید،‌ میتوانید Factory method را پیاده سازی کنید. به دلیل انعطاف پذیری بالای Factory Method،‌بعدا میتوانید به دیگر الگوهای Creational تغییر دهید.

Caution

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

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

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

Warning

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

پیچیده تر شدن برنامه به علت نیاز به تعریف انواع زیرکلاس های Factory و اشیاء تولیدی

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

فرض کنید در برنامه خودمون بخشی به نام پرداخت داریم که در اون روش های مختلفی هم برای پرداخت وجود داره و هر روش پرداخت هم روش ایجاد شی مستقل خودش رو داره.

در این شرایط بهترین راه استفاده از Factory Method Design Pattern هست.

1.3.5. پیاده سازی

ابتدا interface های مربوط به Factory و متد پرداخت رو ایجاد می کنیم:

1<?php
2
3interface PaymentMethod {
4    public function processPayment(int $amount);
5}
6
7interface PaymentMethodFactory {
8    public function create();
9}

در مرحله ی بعد پیاده سازی های concrete مربوط به Factory ها رو انجام میدیم:

 1<?php
 2
 3class CreditCardFactory implements PaymentMethodFactory {
 4    private string $cardNumber;
 5    private string $cvv;
 6    private string $expiryDate;
 7
 8    public function __construct(string $cardNumber, string $cvv, string $expiryDate) {
 9        $this->cardNumber = $cardNumber;
10        $this->cvv = $cvv;
11        $this->expiryDate = $expiryDate;
12    }
13
14    public function create(): CreditCard
15    {
16        return new CreditCard($this->cardNumber, $this->cvv, $this->expiryDate);
17    }
18}
19
20class PayPalFactory implements PaymentMethodFactory {
21    private string $email;
22    private string $password;
23
24    public function __construct(string $email, string $password) {
25        $this->email = $email;
26        $this->password = $password;
27    }
28
29    public function create(): PayPal
30    {
31        return new PayPal($this->email, $this->password);
32    }
33}

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

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

 1<?php
 2
 3class CreditCard implements PaymentMethod {
 4    private string $cardNumber;
 5    private string $cvv;
 6    private string $expiryDate;
 7
 8    public function __construct(string $cardNumber, string $cvv, string $expiryDate) {
 9        $this->cardNumber = $cardNumber;
10        $this->cvv = $cvv;
11        $this->expiryDate = $expiryDate;
12    }
13
14    public function processPayment(int $amount) {
15        // process payment using credit card details
16    }
17}
18
19class PayPal implements PaymentMethod {
20    private string $email;
21    private string $password;
22
23    public function __construct(string $email, string $password) {
24        $this->email = $email;
25        $this->password = $password;
26    }
27
28    public function processPayment(int $amount) {
29        // process payment using PayPal details
30    }
31}

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

1$creditCardFactory = new CreditCardFactory("1234567890123456", "123", "12/24");
2$creditCard = $creditCardFactory->create();
3$creditCard->processPayment(100);
4
5$payPalFactory = new PayPalFactory("saleh@example.com", "password123");
6$payPal = $payPalFactory->create();
7$payPal->processPayment(100);

نحوه ی ایجاد شی به این صورت هست و موضوع مهم در اینجا این هست که متغیر های $paypal و $creditCard در اینجا هر دو interface با نام PaymentMethod رو پیاده سازی کردن پس می توان هر دو را در برنامه به یک شکل و با فراخوانی متد مشترک processPayment استفاده کرد.

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

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