الگوی طراحی Adapter یک الگوی ساختاری(structural) است که به اشیاء با رابط های ناسازگار امکان همکاری می دهد.
[divider]
مشکل
تصور کنید که شما در حال ایجاد یک برنامه نظارت بر بازار سهام هستید. برنامه داده های سهام را از چندین منبع با فرمت XML بارگیری می کند و سپس نمودارها و دیاگرامهای زیبا را برای کاربر نمایش می دهد.
در بعضی از مواقع ، تصمیم می گیرید با ادغام یک کتابخانه تحلیلی هوشمند شخص ثالث ، برنامه را بهبود بخشید. اما یک واقعیت وجود دارد: کتابخانه تحلیلی فقط با داده ها با فرمت JSON کار می کند.
برای اینکه کتابخانه تحلیلگر بتواند با دادههای XML کار کند، می توانید کتابخانه را به نحوی تغییر دهید که دادههای XML را به عنوان ورودی بپذیرد. با این وجود، این کار ممکن است برخی از کدهای موجود را که به کتابخانه متکی هستند دچار مشکل کند. و بدتر اینکه، ممکن است در وهله اول به کد منبع کتابخانه(Source Code) دسترسی نداشته باشید که در این صورت، تغییر دادن کتابخانه، روشی غیرممکن خواهد شد.
[divider]
راهحل
به عنوان یک مثال در دنیای حقیقی، به شارژر گوشی خود نگاه کنید. بر روی آن نوشته که ورودی ۲۲۰ ولت متناوب و خروجی ۵ یا ۹ ولت مستقیم. شما هیچگاه نمیتوانید گوشی خود را مستقیما و بدون یک واسط(شارژر) به پریز برق متصل کنید؛ زیرا در اینصورت، شاهد نابودی گوشی خود خواهید بود. گوشی شما ولتاژی در حدود ۵ ولت(یا ۹ ولت) را میتواند تحمل کند. اما ولتاژی در حدود ۲۲۰ ولت قطعا منجر به نابودی گوشی موبایلتان خواهد شد. اما چه چیزی از این فاجعه جلوگیری میکند؟ شما هر روز برای شارژ مجدد باتری گوشی از همان برق ۲۲۰ ولت کمک میگیرید! بله، درست است. چیزی که این دو را به هم مرتبط میکند Adapter است. در ایجاد برنامه سهام هم میتوان از همین ایده کمک گرفت.
شما می توانید یک Adapter ایجاد کنید. این Adapter یک شیء خاص است که رابط یک Object را تبدیل می کند تا Object دیگر بتواند آن را درک کند.
در واقع Adapter بر روی رابط یکی از اشیاء روکشی میکشد تا پیچیدگی تبدیل را در پشت صحنه پنهان کند. Object ای که بر روی آن روکش پیچیده شده حتی از وجود Adapter آگاه نیست. به عنوان مثال، اگر یک Object با واحد اندازهگیری کیلومتر کار میکند و Object دیگر با دادههایی از جنس واحد اندازهگیری مایل میتواند کار کند، در چنین شرایطی، می توانید با Adapter تبدیل مقادیر از کیلومتر به مایل را انجام دهید. با اینکار این دو Object بدون آگاهی از حضور Adapter در میانشان، میتوانند با هم تبادل اطلاعات داشته باشند.
همچنین Adapter ها نه تنها می توانند داده ها را به فرمت های مختلف تبدیل کنند بلکه می توانند به همکاری اشیاء با رابط های مختلف و انتقال اطلاعات میان آنها، کمک کنند. در اینجا نحوه عملکرد آن آورده شده است:
- آداپتور می تواند یک رابط ، سازگار با یکی از اشیاء موجود باشد.
- با استفاده از این رابط، شی موجود می تواند با خیال راحت متدهای آداپتور را صدا کند.
- پس از دریافت فراخوانی، آداپتور درخواست را به Object دوم منتقل می کند ، اما با فرمت و نظمی که Object دوم انتظار دارد.
گاهی اوقات حتی می توان یک آداپتور دو طرفه ایجاد کرد که بتواند فراخوانیها را از دو جهت به فرمت مورد نیاز در جهت دیگر تبدیل کند.
بیایید به برنامه بازار سهاممان برگردیم. برای حل معضل قالب های ناسازگار، می توانید آداپتورهای XML-to-JSON برای هر کلاس از کتابخانه تحلیلی که کد شما مستقیماً با آن کار می کند، ایجاد کنید. سپس کد خود را به گونهای تنظیم کنید که فقط از طریق این Adapter ها با کتابخانه ارتباط برقرار کند. هنگامی که Adapter یک فراخوانی دریافت می کند، داده های XML ورودی را به یک ساختار JSON ترجمه می کند و سپس این فراخوانی را به روشی مناسب به کتابخانه تحلیلگر منتقل میکند.
[divider]
ساختار الگوی طراحی Adapter
این پیاده سازی از اصل Object Composition استفاده می کند: آداپتور واسط یک Object را پیاده سازی می کند و دیگری را درون خود دربر میگیرد. این تکنیک قابل اجرا در تمام زبانهای برنامه نویسی محبوب است.
در دیاگرام بالا:
- Client کلاسی است که شامل Bussiness Logic موجود در برنامه است.
- Client Interface پروتکلی را توصیف می کند که سایر کلاسها باید از آن تبعیت کنند تا بتوانند با کد client همکاری کنند.
- این سرویس کلاس مفیدی است(مثلا همان کتابخانه تحلیلگر سهام در مثال این آموزش). client نمی تواند مستقیماً از این کلاس استفاده کند زیرا client دارای رابط ناسازگاری است.
- Adapter کلاسی است که از طریق رابط Client Interface قادر به همکاری با Client بوده و با دربر گرفتن Object ای از جنس Service، قادر به انتقال درخواستهای کاربر به سرویس خواهد بود. البته در این بین، ابتدا دادههای دریافتی از Client را به فرمت مورد نیاز Service تبدیل میکند و سپس با توجه به فرمت قابل قبول برای Service درخواست را به آن منتقل میکند.
- کد Client تا زمانی که از طریق رابط Client Interface درخواستها را ارسال کند، بطور مستقیم به Adapter اتصال نیافته است. این اتصال غیر مستقیم به ما این امکان را میدهد تا بتوانیم انواع Adapter های دیگر را بدون دستکاری کد Client به برنامه اضافه کنیم. از طرفی اگر رابط Service دچار تغییر شود، تنها کافیست تا Adapter را با آن سازگار کنیم؛ Client اصلا از این تغییر حبردار نمیشود.
در این آموزش سعی کردیم تا مفهوم الگوی طراحی Adapter را بیان کنیم و بدانیم که این الگو چه چالشی را برطرف خواهد کرد. برای پیادهسازی عملی الگوی طراحی Adapter، بسته به زبان برنامهنویسی مورد نظر، نمونه کدهایی وجود دارد که اکنون قادر به درک آنها هستید.
همچنین برای آشنایی با مفاهیم دیگر الگوهای طراحی، به دسته مربوطه مراجعه کنید.
موفق و پیروز باشید.