פרטים נוספים על הבעיה
ראשית, בואו נדמה את ההתנהגות של המערכת הישנה. נניח שזה מייצר סיבות לאיחור לעבודה או ללימודים. לשם כך, יש לנו ממשק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.
}
}
במבט ראשון, פתרון זה לא נראה מוצלח, אך הדמיית הפונקציונליות יכולה להוביל למצב מורכב יותר. אם הלקוח קשוב והמתאם מתועד היטב, פתרון זה מקובל.
מתי להשתמש במתאם
-
אם אתה צריך להשתמש בכיתה של צד שלישי, אבל הממשק שלה אינו תואם ליישום הראשי. הדוגמה שלמעלה מראה כיצד נוצר אובייקט shim שעוטף שיחות בפורמט המובן לאובייקט היעד.
-
כאשר למספר תת-מחלקות קיימות חייבות להיות פונקציונליות משותפת. במקום תת מחלקות נוספות (יצירתן תוביל לשכפול קוד), עדיף להשתמש במתאם.
יתרונות וחסרונות
יתרון: המתאם מסתיר מהלקוח את פרטי עיבוד הבקשות מאובייקט אחד למשנהו. קוד הלקוח לא חושב על עיצוב הנתונים או טיפול בקריאות לשיטת היעד. זה מסובך מדי, ומתכנתים עצלנים :) חסרון: בסיס הקוד של הפרויקט מסובך על ידי מחלקות נוספות, ואם יש מספר רב של נקודות לא תואמות, מספרן יכול לגדול לגדלים בלתי נשלטים.לא להתבלבל עם חזית ודקורטור
בבדיקה שטחית, ניתן לבלבל את המתאם עם דפוסי החזית והדקורטור. ההבדל בין מתאם ל- Facade הוא ש- Facade מציגה ממשק חדש ועוטפת תת-מערכת שלמה. ובכן, הדקורטור, שלא כמו המתאם, משנה את האובייקט עצמו, לא את הממשק.אלגוריתם יישום שלב אחר שלב
-
ראשית, ודא שיש בעיה שדפוס זה יכול לפתור.
-
הגדירו ממשק לקוח שעבורו ישמש מחלקה אחרת.
-
הטמע מחלקת מתאם המבוססת על הממשק שהוגדר בשלב הקודם.
-
במחלקת המתאם, צור שדה המאחסן הפניה לאובייקט. הפניה זו מועברת בקונסטרוקטור.
-
הטמע את כל שיטות ממשק הלקוח במתאם. השיטה יכולה:
-
העבר את השיחה ללא שינוי;
-
שינוי נתונים, הגדלת/הקטנת מספר הקריאות לשיטת היעד, הרחב עוד יותר את הרכב הנתונים וכו'.
-
כמוצא אחרון, אם שיטה מסוימת אינה תואמת, זרוק UnsupportedOperationException, אשר בהחלט צריך להיות מתועד.
-
-
אם האפליקציה משתמשת במתאם רק דרך ממשק הלקוח (כמו בדוגמה למעלה), זה יאפשר להרחיב מתאמים ללא כאב בעתיד.
GO TO FULL VERSION