להיות סתגלתנים חלק א' – אדפטר

ברווזים באגם

שלום לכולם,
השבוע נחזור ולדבר על 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 הזה, שנקראת MallardDucku

הקוד די פשוט להבנה.

עכשיו נציג את ה-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 הזה ישתנה עם הזמן, האדפטר מאפשר לנו להחביא את השינוי מהקליינט ולמנוע ממנו להשתנות גם

UML של Adapter

הקליינט שלנו רואה רק את ה-target interface, שהאדפטר מממש.

שני סוגים של אדפטרים

למרות שבדיוק סיימנו להגדיר את האדפטר, זה לא כל הסיפור.
בפועל יש לנו שני סוגים של אדפטרים: Object Adapter ו-Class Adapter
עד עכשיו הצגנו את ה-Object Adapter

אז מהו ה-Class Adapter? ולמה לא דיברנו עליו עד עכשיו?
לגבי השאלה השניה – הוא דורש ירושה מרובה וזה לא אפשרי בג'אווה
אבל זה לא אומר שאתם לא עובדים בכלל בשפה אחרת, שבה אתם יכולים להשתמש בה
אז בואו נדבר על Class Adapter

UML של Class Adapter

נראה מוכר נכון? ובכן זה באמת מאוד דומה
ההבדל היחיד הוא שבמקום שהאדפטר יחזיק אובייקט שאותו הוא אמור להמיר, הוא יורש ממנו

סיכום

בפוסט הזה דיברנו על ה-Adapter Design Patter
למדנו מהו בכלל, איך עובדים איתו, ולמה הוא משמש.
אני אישית משתמש הרבה פעמים באדפטר על מנת להמיר אובייקטים אשר אני מקבל מAPI שאני משתמש לאובייקטים שהמערכת שבה אני עובד רוצה.
ואני חושב שזה אחד הpatterns החשובים

את הקוד המלא אתם יכולים למצוא כאן
וכמו תמיד, אני אשמח לקבל כל הערה, הארה או שאלה שיש לכם

השאר תגובה

Scroll to Top