پوریا سیفی
پوریا سیفی
خواندن ۵ دقیقه·۱ سال پیش

دیزاین پترن Adapter به زبان ساده ?


حتما واژه ی اداپتور به گوشتون خورده... کار این وسیله اینه که ولتاژ برق شهری رو به اندازه مورد نیاز برای وسیله های مورد نظر ما کاهش بده.

در واقع سازگار کردن یه چیزی با چیز دیگه (به قول خارجی ها ادپته شدن :) )

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

ما برای اینکه لپتاپ خودمون رو شارژ کنیم مجبور نیستیم با پیچ گوشتی پریز رو باز کنیم و ولتاژ رو به اندازه ظرفیت لپتاپ کم کنیم... یا مجبور نیستیم سوکت لپتاپ مون رو باز کنیم و یه کاری کنیم با ولتاژ شهری کار کنه.


برای اینکار یه آداپتور (شارژر) ساخته شده که باعث میشه این برق شهری و لپتاپ بدون مشکل با هم کار کنند.دیزاین پترن Adapter هم دقیقا همین کار رو برای ما انجام میده. در واقع باعث میشه که دو تا کلاسی که با هم سازگار نیستند بتونند با هم کار کنند بدون اینکه مجبور باشیم به سورس کد دست بزنیم. پس اگر برق شهری رو کلاس الف در نظرم بگیریم و لپتاپ رو کلاس ب ، کلاس ادپتور باعث میشه این دو کلاس با هم سازگار بشند. به همین سادگی...

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

فرض کنیم نرم افزار ما قابلیت لاگ گیری داره و ما تا الان لاگ هامون رو توی فایل ذخیره میکردیم :

interface Logger { public function logInfo($message); public function logWarning($message); public function logError($message); } class FileLogger implements Logger { public function logInfo($message) { // Log info message to a file } public function logWarning($message) { // Log warning message to a file } public function logError($message) { // Log error message to a file } } // Usage $fileLogger = new FileLogger(); $fileLogger->logInfo(This is an informational message); $fileLogger->logWarning(This is a warning message); $fileLogger->logError(This is an error message);


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

اینکه مکانیزم ثب لاگ ها به چه صورتی هست تو این مقاله برای ما مهم نیست بنابراین تو کامنت فقط اشاره میکنیم که قراره چه اتفاقی بیفته :

class MongoLogger { public function info($message) { // Log info message using the MongoDB logger } public function warn($message) { // Log warning message using the MongoDB logger } public function error($message) { // Log error message using the MongoDB logger } }


اگر دقت کنید متوجه میشید که MongoLogger با لاگر قبلی ما یه تفاوتی در اسم متد ها داره. در واقع ما تا الان به این صورت لاگ ثبت میکردیم :

$logger->logInfo('info message');

حالا MongoLogger داره به ما تحمیل میکنه که به این صورت ثبت کنیم :

$logger->info('info message');


خب پس یه ناسازگاری تو سیستم به وجود اومده ! برای اینکه این ناسازگاری رو درست کنیم باید یک Adaptor بنویسیم :

class MongoLoggerAdapter extends MongoLogger implements Logger { public function logInfo($message) { $this->info($message); } public function logWarning($message) { $this->warn($message); } public function logError($message) { $this->error($message); } }

کلاس MongoLoggerAdapter به ما این اجازه رو میده که ثبت شدن لاگ های کتابخونه شخص ثالث رو با نحوه ی لاگ گیری اپلیکیشن خودمون سازگار کنیم.

خب تا اینجای کار با کار اصلی ادپتور ها آشنا شدیم.

برای پیاده سازی این دیزاین پترن دو روش مختلف وجود داره :

  • Class Adapter
  • Object Adapter

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

اما قبل از اینکه بریم سراغ پیاده سازی نوع دوم ادپتور، شما رو دعوت میکنم به تفکر و تعمق به یک جمله زیر :

Favor "Object Composition" over "Class Inheritance"

هرچقدر میتونید برای درک این جمله زمان بذارید...

این جمله بیان میکنه که در طراحی سیستم های نرم افزاری تا جایی که میتونید سعی کنید به جای ارث بری از object composition استفاده کنید.در واقع اولویت رو به object composition بدید. این رویکرد اشاره به ساخت ابجکت های پیچیده با استفاده از ابجکت های ساده تر داره که قابلیت استفاده مجدد و انعطاف پذیری بیشتری رو به ما میده و باعث میشه کنترل بیشتری داشته باشیم.

مثلا برای ساخت ادپتر ، میتونیم به جای ارث بری، ابجکت MongoLogger رو تو متد سازنده ادپتر پاس بدیم.

تو کدی که بالا نوشتیم فرض کنید که MongoLogger متدی داره که ما اصلا نیاز نداریم تو ادپتر داشته باشیمش ولی خب ارث بری به ما تحمیل میکنه که تمام متد ها رو ادپتر هم داشته باشه. این یکی از ایرادات ارث بری هست. اساسا در دنیای شی گرایی ارث بری مشکلاتی رو تحمیل میکنه که باید در مقالات دیگه بهش بپردازیم...

حالا با توجه به این توضیحات بریم به یه شکل دیگه ادپتور خودمون رو پیاده سازی کنیم :

class MongoLoggerAdapter implements Logger { private $mongoLogger; public function __construct(MongoLogger $mongoLogger) { $this->mongoLogger = $mongoLogger; } public function logInfo($message) { $this->mongoLogger->info($message); } public function logWarning($message) { $this->mongoLogger->warn($message); } public function logError($message) { $this->mongoLogger->error($message); } } // Usage $mongoLogger = new MongoLogger(); $adapter = new MongoLoggerAdapter($mongoLogger); $adapter->logInfo(This is an informational message); $adapter->logWarning(This is a warning message); $adapter->logError(This is an error message);


خب همونطور که مشخص هست ما بدون استفاده از ارث بری تونستیم ادپتور مورد نظرمون رو بسازیم. در آخر لازمه اشاره کنیم که این دیزاین پترن در دسته بندی دیزاین پترن های Structural قرار میگیره.

امیدوارم مقاله براتون مفید بوده باشه :)


مقالات دیگر در مورد دیزاین پترن ها :

https://virgool.io/@PouriaSeyfi/%D8%AF%DB%8C%D8%B2%D8%A7%DB%8C%D9%86-%D9%BE%D8%AA%D8%B1%D9%86-template-method-%D8%A8%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-%D8%B3%D8%A7%D8%AF%D9%87-vhmsfebmbwyk






دیزاین پترنادپتر دیزاین پترنadapteradapter design patterndesign pattern
شاید از این پست‌ها خوشتان بیاید