خانه / طراحی نرم‌افزار / الگوی طراحی Adapter در برنامه‌نویسی: مفاهیم، کاربردها و پیاده‌سازی با مثال واقعی

الگوی طراحی Adapter در برنامه‌نویسی: مفاهیم، کاربردها و پیاده‌سازی با مثال واقعی

الگوی طراحی Adapter در برنامه‌نویسی: مفاهیم، کاربردها و پیاده‌سازی با مثال واقعی

نویسنده:

انتشار:

به‌روزرسانی:

تعداد نظرات: 1

زمان مطالعه: 6 دقیقه

الگوهای طراحی، انواع مختلفی دارند که شامل رایج‌ترین الگوها مانند پروتوتایپ، Factory و … می‌شوند. یکی دیگر از الگوهای شناخته‌شده طراحی نرم‌افزار، الگوی طراحی Adapter است. در این مقاله قصد داریم با مثال و قطعه کد، این الگو را بررسی کنیم.

الگوی طراحی آداپتور یا تبدیل‌کننده (Adapter) چیست؟

همانطور که از اسم این الگو مشخص است، زمانی که دو کلاس واسط‌های غیرمرتبط و ناهمگون داشته باشند، این الگو واسط یکی را به دیگری تبدیل می‌کند که بتوانند با یکدیگر ارتباط برقرار کنند.

از این الگو که یک الگوی ساختاری است، زمانی استفاده می‌شود که در یک برنامه، بخواهیم دو کلاس غیرمرتبط با یکدیگر کار کنند. از این الگو در برنامه‌هایی با کلاس‌های آماده و یا از پیش نوشته شده، که طراحان نرم‌افزار اجازه تغییر در این کلاس‌ها را نداده باشند، استفاده می‌شود. بسیاری از مثال‌های الگوی Adapter درگیر ورودی و خروجی هستند؛ زیرا این دامنه‌ای است که همیشه تغییر می‌کند.

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

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

هدف این الگوی طراحی، تبدیل واسط موجود (جدید) به واسط مورد انتظار سیستم است و زمانی کاربرد دارد که واسط موجود قابل تغییر نباشد و امکان تغییر کد نیز وجود نداشته باشد. با کمک این الگو، امکان همکاری بین اشیایی که قبلا بخاطر داشتن رابط ناسازگار نمی‌توانستند با هم کار کنند، فراهم می‌شود.

مثالی از استفاده Adapter Design Pattern

برای مثال، برنامه‌هایی که در دهه ۸۰ میلادی نوشته شده‌اند دارای UI بسیار ضعیف‌تری نسبت به برنامه‌های نوشته شده در قرن ۲۰ و ۲۱ میلادی هستند. اگر بازنویسی برنامه‌های قبلی به‌صرفه نباشد و بخواهیم این برنامه‌ها با سخت‌افزارهای جدید همخوانی و سازگاری داشته باشند، باید برنامه‌ای جدید طراحی کنیم.

این برنامه باید نقش واسط برنامه قدیمی و سخت‌افزارهای جدید باشد و بین این دو برنامه ارتباط برقرار کند. به برنامه‌ای که بین این دو برنامه قرار می‌گیرد، Adapter می‌گوییم.

ضرورت استفاده از الگوی Adapter

شاید اینطور به نظر برسد که استفاده از Adapter ضروری نیست و می‌توانیم با تغییر واسط یکی از کلاس‌ها، برنامه را اجرا کنیم. اما دقت کنید؛ این کار زمانی ممکن است که بتوانیم به کد کلاس‌ها دسترسی داشته باشیم و تغییر کلاس‌ها باعث به وجود آمدن مشکل در برنامه نشود و پیچیدگی برنامه را افزایش ندهد. این کار در اکثر مواقع به‌صرفه و عملی نیست.

الگوی Adapter الگویی است كه در دنیای واقعی نمونه‌های زیادی از آن وجود دارد و به همین خاطر درك این الگو زیاد مشكل نخواهد بود.

چه زمانی از Adapter استفاده می‌کنیم؟

دانستن اینکه Adapter چیست، کافی نیست؛ باید بدانیم چه زمانی و در چه شرایطی این الگو بهترین انتخاب است. این بخش به شما کمک می‌کند کاربردهای واقعی آن را درک کنید.

به طور کلی استفاده از Adapter زمانی مفید است که دو کلاس یا سیستم ناسازگار داریم و نمی‌خواهیم یا نمی‌توانیم کدهای آن‌ها را تغییر دهیم. این الگو مثل پلی بین سیستم‌های قدیمی و مدرن عمل می‌کند و ارتباط بین آن‌ها را برقرار می‌کند. بنابراین: 

  • زمانی که می‌خواهید از کلاسی استفاده کنید، اما رابط آن با آنچه نیاز دارید منطبق نیست.
  • وقتی که دسترسی به کد کلاس اصلی ندارید و نمی‌توانید آن را تغییر دهید.
  • هنگام طراحی سیستمی قابل‌گسترش که باید با کدهای قدیمی یا کتابخانه‌های ناسازگار کار کند.
  • وقتی نیاز دارید بدون تغییر کلاس‌های اصلی، یک لایه میانی برای سازگاری بین آن‌ها ایجاد کنید.

از Adapter استفاده می‌کنیم. 

ویژگی‌ها و اجزای الگوی Adapter

برای درک عمیق‌تر الگوهای طراحی، آشنایی با اجزای تشکیل‌دهنده‌ی آن‌ها و نحوه تعامل آن‌ها بسیار ضروری است. در این بخش ساختار کلاس‌ها و نقش هرکدام از آن‌ها را بررسی می‌کنیم.

Class Diagram

در شکل زیر، کلاس دیاگرام مربوط به این الگو را می‌بینید و در ادامه هرکدام از اجزای الگو معرفی شده‌اند.

بنابر گفته GoF هدف از الگوی Adapter عبارت است از:

تبدیل واسط یک کلاس به واسط دیگری که کاربر توقع دارد. Adapter امکان همکاری بین چند کلاس را که به علت ناهمگونی واسطه‌هایشان به روش دیگری نمی‌توانند باهم کار کنند، فراهم می‌کند.

Class Diagram الگو طراحی adapter

شکل ۱: کلاس دیاگرام الگوی Adapter

نقش کلاس‌‌ها

  • Target:

ارائه واسط که کلاینت از آن استفاده می‌کند.

  • Adapter:

وفق دادن Interface Adaptee به Target Interface

  • Adaptee:

کلاسی با واسط ناهماهنگ که می‌خواهیم در برنامه از آن استفاده کنیم.

  • Client:

ارتباط با شی Target Interface

سایر ویژگی‌های کلیدی الگوی طراحی Adapter

در جدول زیر به صورت خلاصه هدف الگو، نام یا نام‌های دیگری که الگو با آن شناخته می‌شود، مواقعی که می‌توان از این الگو استفاده کرد، نتایج استفاده از این الگو و الگوهای مرتبط به این الگو توضیح داده شده است.

جدول ۱: ویژگی‌های Adapter

عنوان

شرح

نام

Adapter Pattern

هدف

واسط یک کلاس را به واسط دیگری که مورد انتظار یک مشتری است تبدیل می‌کند. این الگو امکان همکاری بین اشیایی را که قبلا به‌خاطر داشتن واسط‌های ناسازگار نمی‌توانستند با هم کار کنند، فراهم می‌کند.

نام‌های دیگر

این الگو با نام Wrapper نیز شناخته می شوند.

کاربرد

زمانی از الگوی Adapter استفاده کنید که :

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

نتایج

  • این الگو امكان استفاده از كلاس‌های از پیش‌ساخته را در قالب كلاس جدید بدون تغییر در ساختار آن‌ها و بدون محدود کردن آن‌ها به واسط، فراهم می‌آورد.
  • الگوی Adapter الگویی است که در دنیای واقعی نمونه‌های زیادی از آن وجود دارد، بنابراین درک آن زیاد مشکل نخواهد بود.
  • به طور کل این الگو بسیار ساده و مفید است.

الگوهای مرتبط

Factory Method: در تطبیق‌دهنده‌های شی‌ بهتر است نمونه‌سازی از Adaptee را به طور مستقیم انجام ندهید و به جای آن تکنیکی مانند Factory Method را به کار ببرید.

یک مثال برای الگوی طراحی Adapter

در زیر مثالی از این الگو پیاده‌سازی شده‌است.

مساله: در یک آزمایشگاه شیمی قصد داریم که ویژگی‌هایی مانند ساختار، وزن، نقطه جوش و انجماد مربوط به آب، بنزن و الکل را به دانشجویان این رشته نشان دهیم. (قابل ذکر است، از قبل سرویسی برای بیان ویژگی‌های مواد تعریف شده که با واسط برنامه ما همخوانی ندارد و ما باید ارتباط بین این دو را برقرار کنیم).

راه‌حل: به دلیل تفاوت در واسطی که از قبل برای ویژگی‌های مواد پیاده‌سازی شده است، با واسط مربوط به نمایش برای کاربر، از الگوی Adapter استفاده می‌کنیم.

Compound: طبق کلاس دیاگرام الگو نقش کلاس Target را دارد، که در آن متغیرهایی برای نام و ویژگی‌های مواد تعریف شده و دارای یک متد Virtual به نام Display است که در آن نام ماده مربوطه را نمایش می‌دهد.

ChemicalDatabank: نقش کلاس Adaptee را دارد. کلاسی با واسط ناهماهنگ، که می‌خواهیم در برنامه خود از آن استفاده کنیم. این کلاس دارای سه متد است که نقطه جوش، انجماد، ساختار و وزن مواد را برمی‌گردانند.

RichCompound: نقش کلاس Adapter را دارد که کلاس Compound را به ارث می‌برد و در متد Display، یک نمونه از کلاس ChemicalDatabank ایجاد کرده و متدهای آن را فراخوانی می‌کند.

پیاده‌سازی:

خروجی :

پیاده‌سازی الگو طراحی Adapter

تفاوت Adapter Pattern، Facade و Bridge

در نگاه اول، این سه الگو ممکن است شبیه به هم به‌نظر برسند، اما کاربردها و اهدافشان متفاوت است:

ویژگی Adapter Facade Bridge
هدف تطبیق رابط ناسازگار ساده‌سازی یک سیستم پیچیده جداسازی abstraction از implementation
سطح استفاده معماری و استفاده مجدد ساده‌سازی API طراحی معماری منعطف
تغییر در ساختار کلاس‌ها ندارد ندارد دارد
مثال واقعی مبدل دوشاخه برق پنل ساده‌ای برای یک سیستم بزرگ پلتفرم جدا از UI

جمع‌بندی

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

به طور معمول از این الگوی طراحی زمانی استفاده می‌کنیم که امکان ایجاد تغییر در برنامه‌ها و واسط‌های فعلی را نداریم.

منابع:

oodesign.com
codeproject.com
dofactory.com

سوالات متداول

Object Adapter از ترکیب (composition) استفاده می‌کند، در حالی‌که Class Adapter بر پایه وراثت (inheritance) ساخته می‌شود. در Object Adapter می‌توان به‌راحتی از چند کلاس Adaptee مختلف استفاده کرد، اما Class Adapter معمولا فقط یک کلاس را به ارث می‌برد و در زبان‌هایی که چندارثی ندارند (مثل Java یا C#)، محدودتر است.

Adapter برای سازگار کردن دو رابط ناسازگار طراحی شده اما Facade برای ساده‌سازی رابط کاربری یک سیستم پیچیده استفاده می‌شود. Adapter به کارکرد جزئی یک کلاس تمرکز دارد، ولی Facade کل سیستم را پنهان می‌کند تا استفاده از آن آسان‌تر شود.

بله. یکی از رایج‌ترین کاربردهای Adapter، سازگار کردن کدهای قدیمی با ساختارهای جدید است. به‌جای تغییر در کد اصلی که ممکن است پرریسک باشد، با ساخت یک Adapter می‌توانید رابط آن را به فرم دلخواه تبدیل کنید.

در تمام زبان‌های شی‌گرا مثل Java، C#، Python و ++C می‌توان Adapter را پیاده‌سازی کرد. در زبان‌هایی که از وراثت چندگانه پشتیبانی می‌کنند (مثل C++)، پیاده‌سازی کلاس Adapter آسان‌تر است. اما در زبان‌هایی که فقط ترکیب دارند (مثل Java)، معمولاً از Object Adapter استفاده می‌شود.

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

بله، هیچ محدودیتی وجود ندارد. اگر با چند کلاس ناسازگار مختلف روبه‌رو هستید، می‌توانید برای هر مورد یک Adapter متفاوت پیاده‌سازی کنید، یا یک Adapter عمومی بسازید که چندین مورد را پوشش دهد، بسته به نیاز سیستم.

فرصت‌های شغلی

ایجاد محیطی با ارزش های انسانی، توسعه محصولات مالی کارامد برای میلیون ها کاربر و استفاده از فناوری های به روز از مواردی هستند که در آسا به آن ها می بالیم. اگر هم مسیرمان هستید، رزومه تان را برایمان ارسال کنید.

دیدگاه‌ها

یک پاسخ به “الگوی طراحی Adapter در برنامه‌نویسی: مفاهیم، کاربردها و پیاده‌سازی با مثال واقعی”

  1. پگاه نیم‌رخ
    پگاه

    بسیار عالی بود.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *