שלום לכולם,
השבוע נחזור ולדבר על design patterns, והפעם אנחנו נדבר על שניים ביחד, על ה-Adpater.
זוכרים שדיברנו על ה-Decorator? שם עטפנו אובייקטים על מנת לתת להם אחריות חדשה. עכשיו אנחנו רוצים לעטוף אובייקטים במטרה אחרת, כדי לגרום ל-interface להראות כמו משהו שהוא לא.
למה שנרצה לעשות דבר כזה?
כדי שנוכל להתאים design קיים שמצפה לקבל interface מסויים למחלקה אשר מממשת interface אחר. זה יהיה ה-Adpater Design Pattern
בואו נתחיל
כמו כל הפוסטים בסדרה הזאת, גם הפוסט הזה מבוסס על הספר הנהדר Head First Design Patterns.
ה-Adapter הוא מסביבנו, כל הזמן
אני לא חושב שתהיה לכם בעיה להבין מה זה Adapter
, כיוון שבעולם האמיתי מסביבנו, יש מלא כאלה.
לדוגמה, קניתם פעם מכשיר חשמלי מארצות הברית או אנגליה? הוא הגיע עם שקע אחר ממה שיש לכם בבית נכון?
במקרה הזה הייתם צריכים להביא מתאם לשקע. ולכן אתם כבר יודעים מה ה-Adapter
עושה: הוא יושב בין השקע לתקע, והמטרה שלו היא לאפשר לחבר את המכשיר חשמל לשקע.
אפשר להסתכל על זה גם בכיוון הזה: הוא משנה את ה-interface של המכשיר החשמלי שלכם כדי שיתאים למערכת החשמל הישראלית.
יש מתאמים שהם מאוד פשוטים, הם רק משנים את הצורה של השקע ומעבירים את המתח, אבל יש לנו מתאמים יותר מתוחכמים, שיודעים להמיר את המתח שיתאים בין המערכת חשמל בבית לבין המכשיר החשמלי.
אוקי זה בעולם האמיתי; אבל מה לגבי object-oriented?
ובכן ה-object oriented adapters משחקים את אותו התפקיד כמו מקביליהם בעולם האמיתי: הם לוקחים interface ומתאימים אותו ל-interface שהלקוח מצפה לקבל.
אם זה הולך, נשמע ונראה כמו ברווז, זה יכול להיות תרנגול הודו?
בואו נסתכל על Adapter
בפעולה. קודם כל נציג את ה-interface של Duck
אנחנו מגדירים לברווז שתי מתודות. הראשונה היא quack()
והשניה היא fly()
.
ויש לנו גם מחלקה שמממשת את ה-interface הזה, שנקראת MallardDuck
u
הקוד די פשוט להבנה.
עכשיו נציג את ה-interface של Turkey
. הוא מי שאנחנו רוצים להמיר ל-Duck
עכשיו בואו נסתכל על מימוש של ה-interface הזה.
גם כאן הקוד די פשוט ולא ממש דורש הסבר לדעתי (אם אתם חושבים אחרת תרגישו חופשי לשאול בתגובות)
עכשיו בוא נגיד שאנחנו רוצים להשתמש ב-Turkey
שלנו בתור ברווז, איך נוכל לעשות את זה?
הרי הוא לא ממש את ה-interface של Duck
אז בואו נשתמש ב-Adapter
אוקי בואו נפרק את זה קצת
קודם כל אנחנו צריכים לממש את ה-interface אליו אנחנו עושים אדפטציה.
במקרה שלנו אנחנו רוצים לממש את ה-interface של Duck
, על מנת שנוכל ליצור Turkey
שמתנהג כמו Duck
.
את זה אתם יכולים לראות בשורה הראשונה – implements Duck
אחר כך אנחנו צריכים לקבל את האובייקט אותו אנחנו ממירים. במקרה שלנו מדובר על Turkey
.
כאן החלטתי לעשות את זה בבנאי כמו בשורה מספר 4, אבל יש כמובן דרכים אחרות.
עכשיו אנחנו צריכים לממש את שתי המתודות של Duck
.
במקרה של quack()
השתמשנו במתודה gobble()
של Turkey
ואילו במקרה של fly()
השתמשתי בלולאה שקוראת 5 פעמים ל-fly()
של Turkey
עכשיו בואו נסתכל על קוד שבודק את הקוד שכתבנו
בהתחלה אנחנו יוצרים MallardDuck
ו-WildTurkey
.
ולאכן מכן אנחנו יוצרים TurkeyAdapter
אשר מקבל את ה-WildTurkey
.
וכאן אנחנו נכנסים לבדיקה.
בהתחלה אנחנו מדפיסים את ההודעות של Turkey
, והפלט הואThe Turkey says…
Gobble gobble
I'm flying a short distance
לאחר מכן אנחנו מדפיסים את הפלט של Duck
The Duck says…
Quack
I'm flying
ובסופו של דבר אנחנו מדפיסים את הפלט של האדפטר שלנוThe TurkeyAdapter says…
Gobble gobble
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
חלקי האדפטר
אז אחרי שהבנו איך האדפטר עובר כרעיון כללי, עכשיו אנחנו נבין יותר לעומק איך ה-design patter הזה עובד.
אז איך הקליינט שלנו משתמש ב-Adapter
?
- הקליינט שולח בקשה לאדפטר על ידי לקרוא לאחת המתודות של ה-interface שלו
- האדפטר ממיר את הבקשה לאחת המתודות של ה-interface שהוא קיבל כדי להמיר
- האדפטר מחזיר את התוצאה חזרה לקליינט
הגדרה רשמית ל-Adapter
אוקי, מספיק לדבר על ברווזים ותרנגולי הודו, ורשתות חשמל.
בואו נגדיר בצורה רשמית את ה-Adapter Design Patter
ה-Adapter
ממיר interface של מחלקה אחת ל-interface שהקליינט שלנו מצפה לו.
האדפטר מאפשר למחלקות אשר לא יכולו לעבוד ביחד עקב אי תאימות ב-interfaces לעובד ביחד
זה מאפשר לנו לבצע decoupeling בין הקליינט ל-interface.
ובמיוחד אם אנחנו מצפים שה-interface הזה ישתנה עם הזמן, האדפטר מאפשר לנו להחביא את השינוי מהקליינט ולמנוע ממנו להשתנות גם
הקליינט שלנו רואה רק את ה-target interface, שהאדפטר מממש.
שני סוגים של אדפטרים
למרות שבדיוק סיימנו להגדיר את האדפטר, זה לא כל הסיפור.
בפועל יש לנו שני סוגים של אדפטרים: Object Adapter
ו-Class Adapter
עד עכשיו הצגנו את ה-Object Adapter
אז מהו ה-Class Adapter
? ולמה לא דיברנו עליו עד עכשיו?
לגבי השאלה השניה – הוא דורש ירושה מרובה וזה לא אפשרי בג'אווה
אבל זה לא אומר שאתם לא עובדים בכלל בשפה אחרת, שבה אתם יכולים להשתמש בה
אז בואו נדבר על Class Adapter
נראה מוכר נכון? ובכן זה באמת מאוד דומה
ההבדל היחיד הוא שבמקום שהאדפטר יחזיק אובייקט שאותו הוא אמור להמיר, הוא יורש ממנו
סיכום
בפוסט הזה דיברנו על ה-Adapter Design Patter
למדנו מהו בכלל, איך עובדים איתו, ולמה הוא משמש.
אני אישית משתמש הרבה פעמים באדפטר על מנת להמיר אובייקטים אשר אני מקבל מAPI שאני משתמש לאובייקטים שהמערכת שבה אני עובד רוצה.
ואני חושב שזה אחד הpatterns החשובים
את הקוד המלא אתם יכולים למצוא כאן
וכמו תמיד, אני אשמח לקבל כל הערה, הארה או שאלה שיש לכם