الگوی طراحی آداپتر (Adapter Design Pattern)

مدتی پیش در یکی از پروژه هایی که اخیرا شروع به همکاری کرده بودم با مشکلی روبرو شدیم که از یک پکیجی برای بخش از پروژه استفاده کرده بودند و همچنین Interface پکیج موردنظر را implement کرده بودند و در بخشهای مختلفی از آن استفاده کرده بودند که با هر بار تغییر در Interface پکیج موردنظر پروژه باید بروزرسانی میشد و مطابق Interface موردنظر قراردادها باید رعایت میشدند و این برای محیط پروداکشن بسیار خطرناک بود. برای اینکار بعد از بررسی و تحقیقی که داشتم بهترین ساختاری که میشه استفاده کرد استفاده از الگوی طراحی Adapter است که در ادامه مقاله در رابطه با این الگوی طراحی صحبت میکنیم.الگوی طراحی Adapter یک الگوی ساختاری است که طبق توضیحات refactoring.guru در شرایطی که ما Interfaceها یا قراردادهای متفاوتی داریم و میخواهیم آنها را همسان سازی کنیم کاربرد دارد.Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.در واقع مفهوم و دلیل اصلی استفاده از Adpater Pattern این است که معمولا وقتی چند کلاس میخواهند از یک Interface پیروی کنند ولی با هم همسان نیستند و Incompatible هستند و در بیشتر مواقع یک کلاس خارجی یا یک پکیج خارجی است که میخواهیم در پروژه از آن استفاده کنیم، در این حالت ما یک کلاس Adapter مینویسیم که از Interface پیروی کنه و متدهایی که موردنیاز ما است را فراخوانی میکنیم. همانطور که در تصویر زیر میبینید ما با استفاده از Adapterی که داریم پکیج خارجی را با کلاسهای داخلی پروژه Adapt کردیم.چه زمانی از الگوی طراحی Adapter استفاده کنیم؟۱. زمانی که میخواهید از کلاسهایی که موجود هستند استفاده کنید ولی Interfaceی که برای آن وجود دارد با ساختار بخشی که میخواهید استفاده کنید همسان نیست. در واقع Adapter به ما این امکان را میدهد که یک لایه میانی بین سرویسی که میخواهیم استفاده کنیم و بخشی که در پروژه ما وجود دارد ایجاد کنیم. این حالت به ویژه در استفاده از پکیجهای خارجی زیاد پیش می‌آید.۲. هنگامی که می خواهید چندین subclass موجود را که فاقد برخی فانکشنالیتی‌های مشترک هستند و نمی توان آنها را به superclass اضافه کرد. در این حالت به جای اینکه متدها و فانکشنالیتی ها را در همه subclassها کپی کنید که کار جالبی نیست میتوانیم با یک راه حل بسیار زیباتر قابلیتها و فانکشنالیتی‌هایی که نداریم را در یک کلاس آداپتور قرار دهیم که این رویکرد بسیار شبیه به الگوی Decorator است.پیاده سازی یک مثال با Adpater Patternفرض کنید که ما یک سرویس پرداخت را در پروژه خود پیاده سازی کرده ایم و متدهای پرداخت مختلفی را در پروژه داریم، برای اینکار یک Interface یا قرارداد برای همه پروایدرها داریم که از آن پیروی کنند و بر این اساس با متدهای مختلف پرداخت را انجام می دهیم. حال تصور کنید برای یک درگاه پرداخت جدید ما میخواهیم از یک پکیج خارجی در داخل پروژه استفاده کنیم و تنها میخواهیم اطلاعات پرداخت را پاس دهیم و مراحل پرداخت به درستی انجام شود. یکی از راههای بسیار بدی که وجود دارد این است که ما از کلاسهای داخل پکیج استفاده کنیم که با هر بار آپدیت پکیج احتمال از کار افتادن آن قسمت از پروژه وجود دارد.بهترین کار این است که ما یک Adapter برای پکیج موردنظر تعریف کنیم و آن را با ساختاری که در پروژه خود داریم همسان سازی کنیم.همانطور که اشاره کردیم قرارداد زیر را برای متدهای پرداخت ایجاد میکنیم.interface PaymentServiceProviderContract { public function doPayment(); }سایر متدها به صورت زیر قرارداد را پیروی میکنند:class InnerPaymentProvider implements PaymentServiceProviderContract { public function doPayment() { // doing payment according to the Inner Payment structure ... } }حالا میخواهیم پکیج خارجی پرداخت را که به صورت زیر است استفاده کنیم:class ThirdPartyPackage { private string $apiKey; public function getIssuers() { // return Issuers ... } public function startToPay() { // doing payment according to the ThirdParty Package class ... } }حالا میخواهیم یک Adpater Class ایجاد کنیم و ساختار پکیج را با ساختار پروژه خود همسان سازی کنیم که به صورت زیر اینکار را انجام میدهیم:class ThirdPartyPackageAdapter implements PaymentServiceProviderContract { protected $thirdPartyPackage; public function __construct(ThirdPartyPackage $thirdPartyPackage) { $this->thirdPartyPackage = $thirdPartyPackage; } public function doPayment() { $this->thirdPartyPackage->startToPay(); } }