زمان تخمینی مطالعه: 0 دقیقه

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

Adapter design pattern

بیان مسئله: تصور کنید که در حال ایجاد یک برنامه نظارت بر بازار سهام هستید. این برنامه داده‌های سهام را از چندین منبع در قالب XML دانلود می‌کند و سپس گراف‌ها و نمودارهای زیبا را برای کاربر نمایش می‌دهد. در برخی مواقع، تصمیم می‌گیرید با ادغام یک کتابخانه تحلیلی مجزا، برنامه را بهبود ببخشید. اما نکته‌ای که وجود دارد این است که کتابخانه استفاده شده برای تجزیه و تحلیل فقط با داده‌ها موجود که در قالب JSON هستند کار می‌کند.

The structure of the app before integration with the analytics library
در حالت کلی از کتابخانه تجزیه و تحلیل «همانطوری که هستند» استفاده کنید، زیرا انتظار می‌رود داده‌ها در قالبی ناسازگار با برنامه شما باشد.

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

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

آداپتورها نه تنها می‌توانند داده‌ها را به فرمت‌های مختلف تبدیل کنند، بلکه می‌توانند به اشیاء با اینترفیس‌های مختلف کمک کنند. در اینجا نحوه عملکرد آنها آمده است:

  1. آداپتور یک اینترفیس، سازگار با یکی از اشیاء موجود را دریافت می‌کند.
  2. با استفاده از این اینترفیس، شی موجود می‌تواند با خیال راحت متدهای الگوی Adapter را فراخوانی کند.
  3. پس از دریافت فراخوانی، آداپتور درخواست را به شی دوم ارسال می‌کند، اما در قالب و ترتیبی که شی دوم با آن سازگار است و انتظار دارد.

گاهی اوقات حتی می‌توان آداپتوری دو طرفه ایجاد کرد که بتواند فراخوانی‌ها را در هر دو جهت تبدیل کند.

Adapter's solution

اجازه دهید مجددا به برنامه بازار سهام خود برگردیم. برای حل معضل فرمت‌های ناسازگار، می‌توانید آداپتورهای XML-to-JSON را برای هر کلاسی از کتابخانه تحلیلی که کد شما مستقیماً با آن کار می‌کند ایجاد کنید. سپس کد خود را طوری تنظیم می‌کنید که فقط از طریق این آداپتورها با کتابخانه ارتباط برقرار کند. هنگامی که یک آداپتور فراخوانی را دریافت می‌کند، داده‌های XML ورودی را به یک ساختار JSON ترجمه می‌کند و فراخوانی را به روش‌های مناسب یک شی قابل تحلیل پیچیده ارسال می‌کند.

نمونه قابل قیاس در دنیای واقعی

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

The Adapter pattern example
یک چمدان قبل و بعد از سفرهای متعدد به خارج از کشور.

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

ساختار الگوی Adapter

در این بخش به بررسی ساختار الگوی آداپتور می‌پردازیم و پیاده‌سازی آن را به صورت UML خواهیم دید. در حالت کلی الگوی Adapter دارای دو مدل ساختار متفاوت است که در ادامه هر دو مدل را بررسی خواهیم کرد.

– آداپتور شی(Object Adapter)

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

– آداپتور کلاس(Class Adapter)

این پیاده‌سازی از وراثت استفاده می‌کند: آداپتور اینترفیس‌ها را از هر دو شی به طور همزمان به ارث می‌برد. توجه داشته باشید که این رویکرد فقط در زبان‌های برنامه‌نویسی که از وراثت چندگانه پشتیبانی می‌کنند، مانند ++C قابل پیاده‌سازی است.

Adapter design pattern (class adapter)

آداپتور کلاس(Class Adapter) نیازی به پوشش دادن هیچ شیئی ندارد زیرا رفتارها را هم از کلاینت و هم از سرویس به ارث می‌برد. معمولا انطباق(Adaptation) در روش‌های نادیده گرفته شده(Overridden) اتفاق می‌افتد. آداپتور حاصل می‌تواند به جای یک کلاس کلاینت موجود استفاده شود.

شبه کد(Pseudocode)

این مثال از الگوی Adapter مبتنی بر درگیری کلاسیک موجود مابین میخ‎‌های مربعی(square peg) و سوراخ‌های گرد است.

Structure of the Adapter pattern example
تطبیق میخ‌های مربعی با سوراخ‌های گرد.

آداپتور وانمود می‌کند که یک میخ گرد است که شعاع آن برابر با نیمی از قطر مربع است (به عبارت دیگر، شعاع کوچک‌ترین دایره‌ای که می‌تواند میخ مربعی را در خود جای دهد).

// Say you have two classes with compatible interfaces:
// RoundHole and RoundPeg.
class RoundHole is
    constructor RoundHole(radius) { ... }

    method getRadius() is
        // Return the radius of the hole.

    method fits(peg: RoundPeg) is
        return this.getRadius() >= peg.getRadius()

class RoundPeg is
    constructor RoundPeg(radius) { ... }

    method getRadius() is
        // Return the radius of the peg.


// But there's an incompatible class: SquarePeg.
class SquarePeg is
    constructor SquarePeg(width) { ... }

    method getWidth() is
        // Return the square peg width.


// An adapter class lets you fit square pegs into round holes.
// It extends the RoundPeg class to let the adapter objects act
// as round pegs.
class SquarePegAdapter extends RoundPeg is
    // In reality, the adapter contains an instance of the
    // SquarePeg class.
    private field peg: SquarePeg

    constructor SquarePegAdapter(peg: SquarePeg) is
        this.peg = peg

    method getRadius() is
        // The adapter pretends that it's a round peg with a
        // radius that could fit the square peg that the adapter
        // actually wraps.
        return peg.getWidth() * Math.sqrt(2) / 2


// Somewhere in client code.
hole = new RoundHole(5)
rpeg = new RoundPeg(5)
hole.fits(rpeg) // true

small_sqpeg = new SquarePeg(5)
large_sqpeg = new SquarePeg(10)
hole.fits(small_sqpeg) // this won't compile (incompatible types)

small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
hole.fits(small_sqpeg_adapter) // true
hole.fits(large_sqpeg_adapter) // false

قابلیت‌ها و کاربرد‌ها

راه حل بسیار بهتر قرار دادن قابلیت‌های از دست رفته در یک کلاس آداپتور است. سپس اشیاء با ویژگی‌های از دست رفته را در داخل آداپتور قرار می‌دهید و ویژگی‌های مورد نیاز را به صورت پویا به دست می‌آورید. برای این کار، کلاس‌های هدف باید یک اینترفیس مشترک داشته باشند و فیلد آداپتور باید از آن اینترفیس پیروی کند. این رویکرد بسیار شبیه به الگوی Decorator است.

نحوه پیاده‌سازی

  1. مطمئن شوید که حداقل دو کلاس با اینترفیس‌های ناسازگار دارید:
    • یک کلاس سرویس مفید که نمی‌توانید آن را تغییر دهید (اغلب کلاس‌های شخص ثالث، قدیمی یا با وابستگی‌های موجود زیاد).
    • یک یا چند کلاس مشتری که بوسیله استفاده از کلاس سرویس سود می‌برند.
  2. اینترفیس مشتری را اعلان کنید و نحوه ارتباط مشتریان با سرویس را شرح دهید.
  3. کلاس آداپتور را ایجاد کنید و کاری کنید که آن از اینترفیس مشتری پیروی کند. فعلا تمام متدها را خالی بگذارید.
  4. یک فیلد به کلاس آداپتور اضافه کنید تا یک رفرنس در شیء سرویس ذخیره شود. روش معمول این است که این فیلد را از طریق سازنده مقداردهی اولیه کنید، اما گاهی اوقات هنگام فراخوانی متدهای آن، انتقال آن به آداپتور راحت‌تر است.
  5. یکی یکی تمام متدهای اینترفیس کلاینت را در کلاس آداپتور پیاده‌سازی کنید. آداپتور باید بیشتر کار واقعی را به شیء سرویس واگذار کند و فقط اینترفیس یا تبدیل فرمت داده را مدیریت کند.
  6. مشتریان باید از آداپتور بوسیله اینترفیس مشتری استفاده کنند. این موضوع به شما امکان می‌دهد آداپتورها را بدون تأثیر بر روی کد مشتری تغییر دهید یا گسترش دهید.

مزایا و معایب الگوی Adapter

این الگوی طراحی دارای مزایا و معایبی به شرح زیر است:

– مزایا

معایب

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

نمونه کد الگوی Adapter

2 پاسخ

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

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

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

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