|
|
|
המטרה:
לשנות ממשק של class לממשק אחר אשר קליינט מצפה לו. Adapter מאפשר ל
class ים לעבוד ביחד במקרים שהם לא היו יכולים לעבוד ביחד רק בגלל אי התאמה בממשקים.
ידועה גם בתור: Wrapper
מוטיבציה:
לעתים, class שהוגדר גנרית במטרה שיעשה בו שימוש במקרים רבים, אינו ניתן לשימוש במקרה מסוים רק מכיוון שהממשק שלו לא עונה על דרישות הממשק הספציפיות שהאפליקציה דורשת.
ומה זה אומר? קחו לדוגמה drawing editor שבו המשתמש יכול לצייר ולסדר אלמנטים גרפיים (קוים, פוליגונים, טקסט וכו') וליצור ציורים ודיאגרמות.
האבסטרקציה הבסיסית של ה editor היא אובייקט גרפי, בעל צורה ניתנת לשינוי ואשר יכול לצייר את עצמו. הממשק לאובייקט הגרפי מוגדר על ידי abstract class
שנקרא Shape. ה editor מגדיר subclass של Shape לכל סוג של אלמנט גרפי: LineShape עבור קו, PolygonShape עבור פוליגון, וכן הלאה.
ה subclass ים עבור אלמנטים גיאומטריים בסיסיים כמו קו ופוליגון הם קלים למימוש, מכיוון שהאפשרויות לצייר אותם ולערוך אותם הן מוגבלות מטבען.
אבל ה subclass שמציג טקסט, TextShape, הוא מסובך יותר למימוש באופן משמעותי, מכיוון שאפילו עריכת טקסט בסיסית דורשת ניהול buffer ועדכון מתוחכם של המסך.
במקרה זה נשמח כמובן לקבל class גנרי מוכן עבור טקסט שכבר נכתב על ידי מישהו ומבצע פונקציונליות של הצגת ועריכת טקסט.
נניח שקיים כזה ונקרא לו לצורך העניין TextView. כמובן שהיינו שמחים עוד יותר להשתמש בו ישירות בתור TextShape אבל לצערנו מי שתכנן אותו לא בדיוק חשב על Shapes באותו רגע, ולכן הוא אינו תואם למחלקה Shape כרגע.
אבל לא ניתן לזה לשבור אותנו, כי אנחנו מספיק עצלנים כדי לא לרצות לכתוב הכל מחדש, ועם זאת מספיק חכמים כדי למצוא לכך פתרון!! (-;
איך בכל זאת נוכל להשתמש ב class ים מוכנים כ TextView באפליקציות שמצפות לclass עם ממשק שונה לגמרי?
הפתרון:
"אולי נשנה את TextView כך שיהיה מסוג Shape?" עלתה המחשבה בראשכם... באמת? ומה אם אין לכם את קוד המקור שבו TextView נכתב?
יותר מכך, אם בעוד חודש תצטרכו אותו לשימוש באפליקציה אחרת תשנו אותו שוב? "לא ולא" אתם נבהלים... אם כן, הרשו לי להציע פתרון:
נוכל להגדיר את TextShape כך שהוא יתאים את עצמו לשימוש ב class
המוכן TextView. זאת נוכל לעשות בשתי דרכים:
1) נבצע שימוש בהורשה מרובה ונירש את הממשק שלו (ב public inheritance ) מ Shape ואת המימוש (ב
private inheritance ) שלו מTextView . או ש:
2) נבצע הרכבה (composition) של אובייקט TextView בתוך אובייקט TextShape ונממש את TextShape במונחים של הממשק של TextView (כלומר ע"י קריאות לפעולות של האובייקט TextView שנמצא בתוכו).
שתי גרסאות אלה הם גרסת ה class וגרסת הobject בהתאמה של ה adapter pattern. במקרה זה TextShape הוא ה adapter. נתבונן בדיאגרמה הבאה (למי שאינו זוכר כיצד להסתכל בדיאגרמה זו זה הזמן
להתרענן):
|
לחץ להגדלה - adapter exapmle
|
הדיאגרמה מדגימה את גרסת ה object adapter. בדיאגרמה רואים כיצד בקשות BoundingBox() מומרות לבקשות GetExtent() שמוגדרות ב TextView.
כך ה editor יכול להשתמש ב class המוכן TextView דרך TextShape שממשקו תואם לממשק של Shape.
לעתים קרובות ה adapter גם אחראי לפונקציונליות שה class המותאם (adapted) אינו מספק.
הדיאגרמה מראה כיצד זה מתבצע. למשתמש ישנה אפשרות לגרור (drag) צורה שצייר למיקום חדש. TextView אינו מתוכנן לכך כי הוא יודע רק לטפל בטקסט עצמו.
לכן TextShape מוסיף את הפונקציונליות הזו על ידי מימוש של פעולת CreateManipulator() שמייצרת מופע של ה subclass המתאים של Manipulator אשר יודע לבצע פעולת גרירה.
Manipulator הוא abstract class עבור אובייקטים שיודעים כיצד
לשנות צורה של אלמנט גרפי בהתאם לקלט מהמשתמש, כמו למשל, גרירה למיקום חדש. לכל צורה יש subclass מתאים לה של Manipulator.
כך הוספנו פונקציונליות ש TextView לא מספק אך הeditor דורש אותה. כלומר, בסיכום, ניצלנו מלכתוב עשרות או מאות שורות קוד מחדש!! איזה כיף!! Yes!!
|
|
|
|