2.1. Adapter

2.1.1. تعریف

یکی از معروف ترین مثال هایی که برای الگوی طراحی Adapter یا Wrapper وجود داره خود آداپتور هست! مثلا آداپتور های 3 به 2 که پریز برق سه شاخه رو به دو شاخه تبدیل می کنن.

Sample of Adapter Design Pattern

Standderivative work: PleaseStand, CC BY 3.0, via Wikimedia Commons

در واقع در چنین حالتی نه سه شاخه تغییری کرده و نه ورودی دو شاخه ی روی دیوار، بلکه این وسط ما یک پل ارتباطی اضافه کردیم که نقش آداپته کننده یا مطابقت دهنده رو بازی می کنه.

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

این موضوع باعث میشه client بتونه با کلاس آداپته شده دقیقا مثل کلاس اصلی ارتباط برقرار کنه بدون اینکه متوجه تفاوت بشه.

2.1.2. اجزاء

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

  1. Target Interface: مشخص کننده ی interface ای که client برای ارتباط با adapter از آن استفاده می کند.

  2. Adaptee: کلاسی که می بایست آداپته شود.

  3. Adapter:کلاسی که interface مربوط به Adaptee را با Target Interface هماهنگ می کند.

4. Client: کلاسی که از طریق Target Interface با آداپتور ارتباط برقرار می کند بدون اینکه از حضور Adaptee اطلاعی داشته باشد.

UML of Adapter Design Pattern

Trashtoy, Public domain, via Wikimedia Commons

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

این الگو رو همونطور که توضیح دادم زمانی استفاده می کنیم که نیاز باشه در برنامه راهی پیدا کنیم برای هماهنگ کردن کلاس های ناهماهنگ با همدیگه.

Caution

✅ مزایای استفاده رعایت اصل تک مسئولیتی از اصول Solid

رعایت اصل Open/Closed از اصول Solid با هماهنگ کردن کلاس های جدید با ساختار فعلی بدون تغییر ساختار کد

جلوگیری از نوشته شدن کدهای زیاد تکرارشونده برای ایجاد اشیاء

Warning

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

به علت افزایش تعداد کلاس ها و interface پیچیدگی برنامه قطعا افزایش پیدا می کنه

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

فرض کنید یک سیستم پرداخت داریم که داره از interface به نام Payment با یک متد processPayment استفاده می کنیم.

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

اینجاست که الگوی Adapter وارد عمل میشه.

2.1.5. پیاده سازی

ساختار interface ای که برنامه در حال حاضر داره ازش استفاده می کنه:

1<?php
2
3// Payment Interface
4interface Payment {
5    public function processPayment($amount);
6}

و کلاسی که در اختیار ما قرار گرفته و با این interface هماهنگ نیست:

 1<?php
 2
 3// Third-Party Payment Gateway API
 4class PaymentGateway {
 5    public function makePayment($amount): void
 6    {
 7        // code to make payment through the third-party payment gateway API
 8        echo "Payment of $amount processed using the third-party payment gateway API\n";
 9    }
10}

اینجاست که با کلاس Adapter میایم تا این هماهنگی رو انجام بدیم:

 1<?php
 2
 3// Adapter Class
 4class PaymentGatewayAdapter implements Payment {
 5    private PaymentGateway $paymentGateway;
 6
 7    public function __construct(PaymentGateway $paymentGateway) {
 8        $this->paymentGateway = $paymentGateway;
 9    }
10
11    public function processPayment($amount) {
12        $this->paymentGateway->makePayment($amount);
13    }
14}

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

 1<?php
 2
 3// Payment Processing System
 4class PaymentProcessor {
 5    private Payment $payment;
 6
 7    public function __construct(Payment $payment) {
 8        $this->payment = $payment;
 9    }
10
11    public function process($amount): void
12    {
13        $this->payment->processPayment($amount);
14    }
15}

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

1<?php
2
3// Client Code
4$paymentGateway = new PaymentGateway();
5$paymentAdapter = new PaymentGatewayAdapter($paymentGateway);
6$paymentProcessor = new PaymentProcessor($paymentAdapter);
7
8$paymentProcessor->process(100); // Payment of 100 processed using the third-party payment gateway API

اصل Open/Closed میبینید که اینجا به خوبی داره رعایت میشه.

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