جزئیات بیشتر در مورد مشکل
ابتدا بیایید رفتار سیستم قدیمی را شبیه سازی کنیم. فرض کنید دلایلی برای دیر رسیدن به محل کار یا مدرسه ایجاد می کند. برای انجام این کار، ما یک اینترفیس داریم کهExcuse
شامل متدها generateExcuse()
و likeExcuse()
.dislikeExcuse()
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
این رابط توسط کلاس پیاده سازی می شود WorkExcuse
:
public class WorkExcuse implements Excuse {
private String[] reasonOptions = {"по невероятному стечению обстоятельств у нас в доме закончилась горячая вода и я ждал, пока солнечный свет, сконцентрированный через лупу, нагреет кружку воды, чтобы я мог умыться.",
"искусственный интеллект в моем будильнике подвел меня и разбудил на час раньше обычного. Поскольку сейчас зима, я думал что еще ночь и уснул. Дальше все How в тумане.",
"предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице."};
private String[] sorryOptions = {"Это, конечно, не повторится, мне очень жаль.", "Прошу меня извинить за непрофессиональное поведение.", "Нет оправдания моему поступку. Я недостоин этой должности."};
@Override
public String generateExcuse() { // Случайно выбираем отговорку из массива
String result = "Я сегодня опоздал, потому что " + reasonOptions[(int) Math.round(Math.random() + 1)] + "\n" +
sorryOptions[(int) Math.round(Math.random() + 1)];
return result;
}
@Override
public void likeExcuse(String excuse) {
// Дублируем элемент в массиве, чтобы шанс его выпадения был выше
}
@Override
public void dislikeExcuse(String excuse) {
// Удаляем элемент из массива
}
}
بیایید مثال را آزمایش کنیم:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
نتیجه:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Прошу меня извинить за непрофессиональное поведение.
حالا بیایید تصور کنیم که شما سرویس را راه اندازی کردید، آمار جمع آوری کردید و متوجه شدید که اکثر کاربران این سرویس را دانشجویان دانشگاه تشکیل می دهند. برای بهبود آن برای نیازهای این گروه، شما یک سیستم تولید بهانه را از یک توسعه دهنده دیگر به طور خاص برای او سفارش دادید. تیم توسعه تحقیقاتی انجام داد، رتبه بندی ها را گردآوری کرد، هوش مصنوعی را به هم متصل کرد، ادغام با ترافیک، آب و هوا و غیره را اضافه کرد. اکنون شما یک کتابخانه برای ایجاد بهانه برای دانش آموزان دارید، اما رابط برای تعامل با آن متفاوت است - StudentExcuse
:
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
اینترفیس دارای دو روش است: generateExcuse
, که یک بهانه ایجاد می کند و dislikeExcuse
, که بهانه را مسدود می کند تا در آینده ظاهر نشود. یک کتابخانه شخص ثالث برای ویرایش بسته است - نمی توانید کد منبع آن را تغییر دهید. در نتیجه، در سیستم شما دو کلاس وجود دارد که اینترفیس را پیاده سازی می کند Excuse
و یک کتابخانه با یک کلاس SuperStudentExcuse
که اینترفیس را پیاده سازی می کند StudentExcuse
:
public class SuperStudentExcuse implements StudentExcuse {
@Override
public String generateExcuse() {
// Логика нового функционала
return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
}
@Override
public void dislikeExcuse(String excuse) {
// Добавляет причину в черный список
}
}
کد را نمی توان تغییر داد. طرح فعلی به این صورت خواهد بود: این نسخه از سیستم فقط با رابط Excuse کار می کند. شما نمی توانید کد را بازنویسی کنید: در یک برنامه بزرگ، چنین تغییراتی می تواند مدت زیادی طول بکشد یا منطق برنامه را خراب کند. می توانید معرفی رابط اصلی و افزایش سلسله مراتب را پیشنهاد دهید: برای انجام این کار، باید نام رابط را تغییر دهید Excuse
. اما سلسله مراتب اضافی در کاربردهای جدی نامطلوب است: معرفی یک عنصر ریشه مشترک معماری را می شکند. یک کلاس متوسط باید پیاده سازی شود که امکان استفاده از عملکردهای جدید و قدیمی را با حداقل تلفات فراهم کند. به طور خلاصه، شما به یک آداپتور نیاز دارید .
نحوه عملکرد الگوی آداپتور
آداپتور یک شیء میانی است که فراخوانی متدهای یک شی را برای دیگری قابل درک می کند. بیایید یک آداپتور برای مثال خود پیاده سازی کنیم و آن را فراخوانی کنیمMiddleware
. آداپتور ما باید رابطی را اجرا کند که با یکی از اشیاء سازگار باشد. بگذار باشد Excuse
. به لطف این، Middleware
می تواند متدهای شی اول را فراخوانی کند. Middleware
تماس ها را دریافت می کند و آنها را با فرمت سازگار به شی دوم ارسال می کند. Middleware
اجرای یک متد با متدها generateExcuse
و به این صورت است dislikeExcuse
:
public class Middleware implements Excuse { // 1. Middleware становится совместимым с an objectом WorkExcuse через интерфейс Excuse
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) { // 2. Получаем ссылку на адаптируемый an object
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse(); // 3. Адаптер реализовывает метод интерфейса
}
@Override
public void dislikeExcuse(String excuse) {
// Метод предварительно помещает отговорку в черный список БД,
// Затем передает ее в метод dislikeExcuse an object superStudentExcuse.
}
// Метод likeExcuse появятся позже
}
تست (در کد مشتری):
public class Test {
public static void main(String[] args) {
Excuse excuse = new WorkExcuse(); // Создаются an objectы классов,
StudentExcuse newExcuse = new SuperStudentExcuse(); // Которые должны быть совмещены.
System.out.println("Обычная причина для работника:");
System.out.println(excuse.generateExcuse());
System.out.println("\n");
Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Оборачиваем новый функционал в an object-адаптер
System.out.println("Использование нового функционала с помощью адаптера:");
System.out.println(adaptedStudentExcuse.generateExcuse()); // Адаптер вызывает адаптированный метод
}
}
نتیجه:
Обычная причина для работника:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Нет оправдания моему поступку. Я недостоин этой должности.
Использование нового функционала с помощью адаптера
بهانه ای باورنکردنی، سازگار با شرایط آب و هوایی فعلی، ترافیک یا اختلال در برنامه حمل و نقل عمومی. این روش generateExcuse
به سادگی فراخوانی را به یک شی دیگر منتقل می کند، بدون اینکه تغییر شکل دهد. این روش dislikeExcuse
نیازمند قرار دادن بهانه در لیست سیاه پایگاه داده بود. پردازش داده های میانی اضافی دلیل محبوبیت الگوی آداپتور است. اما در مورد روشی likeExcuse
که در اینترفیس است Excuse
، اما در نه StudentExcuse
؟ این عملیات در عملکرد جدید پشتیبانی نمی شود. برای چنین موردی، آنها با یک استثنا آمدند UnsupportedOperationException
: اگر عملیات درخواستی پشتیبانی نشود، پرتاب می شود. بیایید از این استفاده کنیم. پیاده سازی کلاس جدید به این صورت است Middleware
:
public class Middleware implements Excuse {
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) {
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse();
}
@Override
public void likeExcuse(String excuse) {
throw new UnsupportedOperationException("Метод likeExcuse не поддерживается в новом функционале");
}
@Override
public void dislikeExcuse(String excuse) {
// Метод обращается за дополнительной информацией к БД,
// Затем передает ее в метод dislikeExcuse an object superStudentExcuse.
}
}
در نگاه اول، این راه حل موفق به نظر نمی رسد، اما شبیه سازی عملکرد می تواند به وضعیت پیچیده تری منجر شود. اگر مشتری مراقب باشد و آداپتور به خوبی مستند باشد، این راه حل قابل قبول است.
زمان استفاده از آداپتور
-
اگر نیاز به استفاده از یک کلاس شخص ثالث دارید، اما رابط آن با برنامه اصلی سازگار نیست. مثال بالا نشان می دهد که چگونه یک شی شیم ایجاد می شود که فراخوانی ها را در قالبی قابل درک برای شی هدف قرار می دهد.
-
هنگامی که چندین زیر کلاس موجود باید عملکرد مشترک داشته باشند. به جای زیر کلاس های اضافی (ایجاد آنها منجر به تکرار کد می شود)، بهتر است از یک آداپتور استفاده کنید.
مزایا و معایب
مزیت: آداپتور جزئیات پردازش درخواست ها را از یک شی به شی دیگر از مشتری پنهان می کند. کد کلاینت به قالب بندی داده ها یا مدیریت تماس ها به روش هدف فکر نمی کند. خیلی پیچیده است و برنامه نویسان تنبل هستند :) نقطه ضعف: پایه کد پروژه به دلیل کلاس های اضافی پیچیده است و اگر تعداد زیادی نقاط ناسازگار وجود داشته باشد، تعداد آنها می تواند به اندازه های غیر قابل کنترل افزایش یابد.نباید با نما و دکوراتور اشتباه شود
پس از بررسی سطحی، آداپتور را می توان با الگوهای نما و دکوراتور اشتباه گرفت. تفاوت بین یک آداپتور و یک نما در این است که یک نما یک رابط جدید را معرفی می کند و یک زیر سیستم کامل را می پوشاند. خب، Decorator بر خلاف آداپتور، خود شیء را تغییر می دهد، نه رابط را.الگوریتم پیاده سازی گام به گام
-
ابتدا مطمئن شوید که مشکلی وجود دارد که این الگو می تواند حل کند.
-
یک رابط مشتری تعریف کنید که از طرف آن کلاس دیگری استفاده می شود.
-
یک کلاس آداپتور بر اساس رابط تعریف شده در مرحله قبل پیاده سازی کنید.
-
در کلاس آداپتور، فیلدی ایجاد کنید که ارجاع به شی را ذخیره کند. این مرجع در سازنده ارسال می شود.
-
تمام روش های رابط مشتری را در آداپتور پیاده سازی کنید. روش می تواند:
-
انتقال تماس بدون تغییر؛
-
تغییر داده ها، افزایش/کاهش تعداد تماس ها به روش هدف، گسترش بیشتر ترکیب داده ها و غیره.
-
به عنوان آخرین راه حل، اگر یک روش خاص ناسازگار است، یک UnsupportedOperationException را پرتاب کنید، که به شدت نیاز به مستندسازی دارد.
-
-
اگر برنامه فقط از طریق رابط کلاینت از آداپتور استفاده کند (مانند مثال بالا)، این به آداپتورها اجازه می دهد تا در آینده بدون دردسر گسترش یابند.
GO TO FULL VERSION