
بیشتر در مورد مشکل
ابتدا رفتار سیستم قدیمی را شبیه سازی می کنیم. فرض کنید بهانه هایی برای دیر رسیدن به محل کار یا مدرسه ایجاد می کند.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[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
"the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
"my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};
@Override
public String generateExcuse() { // Randomly select an excuse from the array
String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
apologies[(int) Math.round(Math.random() + 1)];
return result;
}
@Override
public void likeExcuse(String excuse) {
// Duplicate the element in the array so that its chances of being chosen are higher
}
@Override
public void dislikeExcuse(String excuse) {
// Remove the item from the array
}
}
بیایید مثال خود را آزمایش کنیم:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
خروجی:
"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
حالا تصور کنید که یک سرویس بهانه آفرین راه اندازی کرده اید، آمار جمع آوری کرده اید و متوجه شده اید که بیشتر کاربران شما دانشجویان دانشگاه هستند. برای خدمت بهتر به این گروه، از یک توسعه دهنده دیگر خواستید سیستمی ایجاد کند که به طور خاص برای دانشجویان دانشگاه بهانه ایجاد کند. تیم توسعه تحقیقات بازار را انجام داد، بهانهها را رتبهبندی کرد، هوش مصنوعی را به کار گرفت و این سرویس را با گزارشهای ترافیک، گزارشهای آبوهوا و غیره ادغام کرد. اکنون شما یک کتابخانه برای ایجاد بهانه برای دانشجویان دارید، اما رابط کاربری متفاوتی دارد: StudentExcuse
.
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
این رابط دو روش دارد: generateExcuse
, که بهانه ایجاد می کند و dislikeExcuse
, که از ظاهر شدن دوباره بهانه در آینده جلوگیری می کند. کتابخانه شخص ثالث قابل ویرایش نیست، یعنی نمی توانید کد منبع آن را تغییر دهید. چیزی که اکنون داریم یک سیستم با دو کلاس است که اینترفیس را پیاده سازی می کند Excuse
و یک کتابخانه با SuperStudentExcuse
کلاسی که StudentExcuse
رابط را پیاده سازی می کند:
public class SuperStudentExcuse implements StudentExcuse {
@Override
public String generateExcuse() {
// Logic for the new functionality
return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
}
@Override
public void dislikeExcuse(String excuse) {
// Adds the reason to a blacklist
}
}
کد را نمی توان تغییر داد. سلسله مراتب کلاس فعلی به این صورت است: 

Excuse
رابط را تغییر دهیم. اما سلسله مراتب اضافی در کاربردهای جدی نامطلوب است: معرفی یک عنصر ریشه مشترک معماری را می شکند. شما باید یک کلاس متوسط را پیاده سازی کنید که به ما امکان می دهد از عملکرد جدید و قدیمی با حداقل ضرر استفاده کنیم. به طور خلاصه، شما به یک آداپتور نیاز دارید .
اصل پشت الگوی آداپتور
آداپتور یک شیء میانی است که امکان درک متد فراخوانی یک شی توسط شی دیگر را فراهم می کند. بیایید یک آداپتور برای مثال خود پیاده سازی کنیم و آن را فراخوانی کنیمMiddleware
. آداپتور ما باید رابطی را اجرا کند که با یکی از اشیاء سازگار باشد. بگذار باشد Excuse
. این اجازه می دهد Middleware
تا متدهای شی اول را فراخوانی کنید. Middleware
تماس ها را دریافت می کند و آنها را به روشی سازگار به شی دوم ارسال می کند. Middleware
در اینجا پیاده سازی با generateExcuse
و متدها آمده است dislikeExcuse
:
public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
}
@Override
public void dislikeExcuse(String excuse) {
// The method first adds the excuse to the blacklist,
// Then passes it to the dislikeExcuse method of the superStudentExcuse object.
}
// The likeExcuse method will appear later
}
تست (در کد مشتری):
public class Test {
public static void main(String[] args) {
Excuse excuse = new WorkExcuse(); // We create objects of the classes
StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
System.out.println("An ordinary excuse for an employee:");
System.out.println(excuse.generateExcuse());
System.out.println("\n");
Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
System.out.println("Using new functionality with the adapter:");
System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
}
}
خروجی:
An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position.
Using new functionality with the adapter:
بهانه ای باورنکردنی که با شرایط آب و هوایی فعلی، ترافیک یا تاخیر در برنامه های حمل و نقل عمومی سازگار است. این 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("The likeExcuse method is not supported by the new functionality");
}
@Override
public void dislikeExcuse(String excuse) {
// The method accesses a database to fetch additional information,
// and then passes it to the superStudentExcuse object's dislikeExcuse method.
}
}
در نگاه اول، این راه حل چندان خوب به نظر نمی رسد، اما تقلید از عملکرد می تواند شرایط را پیچیده کند. اگر مشتری توجه کند و آداپتور به خوبی مستند باشد، چنین راه حلی قابل قبول است.
زمان استفاده از آداپتور
-
زمانی که نیاز به استفاده از یک کلاس شخص ثالث دارید، اما رابط آن با برنامه اصلی ناسازگار است. مثال بالا نشان می دهد که چگونه می توان یک شی آداپتور ایجاد کرد که تماس ها را در قالبی قرار می دهد که یک شی هدف قابل درک باشد.
-
هنگامی که چندین زیر کلاس موجود به برخی عملکردهای مشترک نیاز دارند. به جای ایجاد زیر کلاس های اضافی (که منجر به تکرار کد می شود)، بهتر است از یک آداپتور استفاده کنید.
مزایا و معایب
مزیت: آداپتور جزئیات پردازش درخواست ها را از یک شی به شی دیگر از مشتری پنهان می کند. کد سرویس گیرنده به قالب بندی داده ها یا مدیریت تماس ها به روش هدف فکر نمی کند. خیلی پیچیده است و برنامه نویسان تنبل هستند :) نقطه ضعف: پایه کد پروژه با کلاس های اضافی پیچیده است. اگر تعداد زیادی رابط های ناسازگار دارید، تعداد کلاس های اضافی می تواند غیرقابل مدیریت شود.آداپتور را با نما یا دکوراتور اشتباه نگیرید
تنها با یک بازرسی سطحی، یک آداپتور می تواند با الگوهای نما و دکوراتور اشتباه گرفته شود. تفاوت بین آداپتور و نما در این است که نما یک رابط جدید را معرفی می کند و کل زیر سیستم را در بر می گیرد. و دکوراتور، بر خلاف آداپتور، خود شیء را به جای رابط تغییر می دهد.الگوریتم گام به گام
-
ابتدا مطمئن شوید که مشکلی دارید که این الگو می تواند آن را حل کند.
-
رابط مشتری را تعریف کنید که برای تعامل غیر مستقیم با اشیاء ناسازگار استفاده می شود.
-
کاری کنید که کلاس آداپتور رابط تعریف شده در مرحله قبل را به ارث ببرد.
-
در کلاس adapter، یک فیلد برای ذخیره ارجاع به شیء adaptee ایجاد کنید. این مرجع به سازنده منتقل می شود.
-
تمام روش های رابط مشتری را در آداپتور پیاده سازی کنید. یک روش ممکن است:
-
بدون ایجاد هیچ تغییری تماس ها را ارسال کنید
-
تغییر یا تکمیل داده ها، افزایش/کاهش تعداد تماس ها به روش هدف و غیره.
-
در موارد شدید، اگر یک روش خاص ناسازگار باقی بماند، یک UnsupportedOperationException پرتاب کنید. عملیات پشتیبانی نشده باید کاملاً مستند باشد.
-
-
اگر برنامه فقط از کلاس آداپتور از طریق رابط مشتری استفاده کند (مانند مثال بالا)، آداپتور را می توان بدون دردسر در آینده گسترش داد.
GO TO FULL VERSION