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