Observer Design Pattern

שלום לכולם,
היום אנחנו נמשיך במסע שלנו ברחבי ה design patterns
והפעם יש לי design pattern שדואג שהאובייקטים שלכם ידעו כאשר משהו חשוב להם עומד לקרות.
קוראים לו Observer Design Pattern.
הוא אחד ה-design pattern שנמצאים הכי הרבה בשימוש והוא מאוד שימושי.
בפוסט הזה אנחנו נסתכל על כל מיני אספקטים מעניינים של Observer, למשל קשר של אחד לרבים ו loose coupeling.
אז יאללה בואו נתחיל

Subscription, אבל איך?

כולנו מכירים את האופציה להירשם לעדכונים בבלוגים/עתונים/כותבי תוכן (subscription)
אפילו לבלוג הזה יש (חפשו בתחתית העמוד)
מה המאפיינים של subscription?

  • יוצר תוכן כלשהו מתחיל לפרסם
  • אנחנו שרוצים להשאר מעודכנים, נרשמים לרשימת תפוצה שלו
  • אפשר לבטל את ההרשמה אם אנחנו כבר לא רוצים לקבל עדכונים
  • כל הזמן שיוצר התוכן קיים (מבחינה עסקית כמובן) אנשים ממשיכים להרשם/לבטל הרשמה

אז Observer מתנהג בצורה מאוד מאוד דומה.
בואו נסתכל על דוגמה כדי להבין את זה יותר טוב

הכירו את המערכת מזג האוויר שלנו

תרשים זרימה של מערכת מזג האוויר שלנו
תרשים זרימה של מערכת מזג האוויר שלנו

במערכת מזג האוויר שלנו יש כמה מרכיבים
מצד שמאל אתם יכולים לראות את כל המדדים שלנו,
כאשר תחנת מזג האוויר מודדת את הנתונים
יש קטע במערכת שמושך את המידע מתוך התחנה
והמשימה שלנו היא לכתוב קוד אשר ידע להמיר את המידע הזה לתצוגות שונות באפליקציה:

  • מזג אוויר עכשיו
  • סטטיסטיקות
  • תחזית

בעצם אנחנו מקבלים אובייקט שנקרא WeatherData שאותו אנחנו צריכים לממש.
זה נראה ככה

אפייון של האובייקט WeatherData
אפייון של האובייקט WeatherData

את כל המתודות של הget אנחנו יכולים להניח שכבר מימשו לנו.
אנחנו צריכים לממש את measurementsChanged
אנחנו גם יודעים שהפונקציה הזאת נקראת כל פעם שהמדידות משתנות
המשימה שלנו היא לבנות את המידע ולעדכן את התצוגות באפליקיציה

אבל בואוו נחשוב על העתיד
זוכרים על מה דיברתי במבוא ל design patterns?
הדבר היחיד שקבוע בתוכנה, זה שהיא משתנה
אז אנחנו רוצים שהתוכנה שלנו תהיה פתוחה להרחבות –
בעתיד אולי יהיו לנו עוד תצוגות לתמוך בהן. או אולי ימחקו כמה

בואו נסתכל על מימוש ראשוני של הפתרון

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

  • אנחנו עובדים עם concrete classes ולא עם interfaces
  • כאשר תגיע תצוגה חדשה אנחנו נצטרך לשנות את הקוד הקיים שלנו (פוגע בעקרון ה open close)
  • אין לנו יכולת להוסיף או להוריד תצוגות בזמן ריצה
  • ולא עשינו אנקפסולציה לקוד שמשתנה (פוגע בעיקרון code to interface)

אז בסופו של דבר, אנחנו לא פתוחים לשינויים
וזה, כמו שאתם כבר יודעים יגרור כאבים בעתיד.

הכירו את observer design pattern

כמו שהסברתי בהקדמה, ה-observer עוזר לנו לדחוף מידע לכל מי שמעוניין
אם נגדיר אותו רגע בצורה רשמית:

Observer design pattern מגדיר יחס של אחד לרבים בין אובייקטים
כך כאשר אחד מהם משתנה כל התלויים שלו מקבלים הודעה על השינוי בצורה אוטומטית

אז בעצם יש לנו subject שהוא אחראי לעדכן את כולם
ויש לנו observer, כלומר צופה, שהוא מעוניין לקבל עדכוני מידע מה-subject.

בואו נסתכל על הUML

UML של Observer design pattern
UML של Observer design pattern

נתחיל ב-Subject.
אובייקטים משתמשים ב-interface הזה על מנת להירשם כ-observers
וגם כדי למחוק את עצמם מהרשימה

לכל subject יש הרבה observers
ולכל observer יש מתודה בשם update, שה-subject שלנו צריך לקרוא לך כאשר הוא רוצה להודיע על עדכונים

ה-concreteSubject תמיד מממש את ה-subject interface.
בנוסף להוספה והסרה של observers, ה-concreteSubject מש את הפונקציה notify
שבה הוא צריך לקרוא ל-update עבור כל observer
ל-concreteSubject יכולות להיות עוד מתודות משל עצמו כמובן

ה-concreteObserver יכול להיות כל מחלקה שמממשת את ה-observer interface
כלומר שיש לה מתודה בשם update.
כל observer נרשם אצל subject שהוא רוצה לקבל ממנו עדכונים

הכוח ב-loose coupling

אני רוצה לעצור פה רגע, ולהסביר למה הכוונה ב-loose coupling
ולמה אנחנו בכלל מדברים על זה בפוסט של observer

כאשר אנחנו מגדירים שני אובייקטים כ-loose coupling, אז הכוונה שלנו היא ששניהם יכולים לבצע אינטרקציה
אבל הם יודעים מעט מאוד אחד על השני
וכאשר יש לנו loosely coupling design אז זה נותן לנו גמישות רבה מאוד
בואו נראה איך design pattern יכול להיות loose coupling:

קודם כל, הדבר היחידי שאפשר לדעת על observer הוא שמממש interface מסויים.
כל מחלקה שתדבר איתו, לא יודעת מה הוא עושה כאשר קוראים ל-update
או כל דבר אחר לגביו

אנחנו יכולים להוסיף observers חדשים בכל רגע נתון
כיוון שה-subject שלנו תלוי רק במתודה update.
אז אין לנו מניעה להוסיף חדשים, ואנחנו לא צריכים לעדכן את subject או את ה-observers הישנים כאשר אנחנו מוסיפים אחד חדש
אנחנו יכולים להשתמש ב-observer וב-subject לשימושים אחרים מעבר לעדכון המידע אם יש לנו צורך

ואולי הכי חשוב, שינוי ב-subject וב-observer לא משפיע אחד על השני.
כל עוד שניהם לא שוברים את ה-interface שהם התחייבו

שימוש ב observer design pattern באלפיקציה שלנו

אז אחרי שהבנו מה זה בכלל observer design pattern, ומה היתרון שלו
בואו נשמש בו כדי לעצב את המערכת שלנו מחדש.
ככה היא תראה

אפליקציית מזג אוויר ממודלת באמצעות observer
אפליקציית מזג אוויר ממודלת באמצעות observer

השראנו את ה-subject interface כמו שהוא היה
אבל הפעם מי שמממש אותו זה ה-WeatherData. הוא מממש גם את ה-interface, אבל גם מוסיף מתודות משלו
כל המחלקות אשר מעדכנות את התצוגות של האפליקציה שלנו מממשות שני interfaces
הראשון כמובן הוא ה-objserver, כדי שהן יוכלו לקבל את המידע
השני הוא הDisplayElement, כדי שנוכל להשתמש במה שלמדנו במבוא ל-design pattern.
באמצעות ה-interface החדש, אנחנו מאפשרים הרחבה בקלות של המערכת שלנו.

הלאה אל הקוד

אז בואו נראה איך הכל מתחבר לנו דרך הקוד. קדימה
נתחיל עם ה-interfaces שלנו

כאן אין לנו משהו חדש. בדיוק כמו בUML
רק נפתח פה נקודה קטנה למחשבה.
האם אתם חושבים שאיך שכתבתי את update היא הדרך הנכונה?
האם יש דרך יותר נכונה לעשות את זה?
מה יקרה אם נצטרך להוסיף עוד קריאות? איזה קוד נצטרך לשנות?


אז בואו נעבור למשהו יותר מעניין כמו ה-weatherData

כאן הדברים הרבה יותר מעניינים
כמו שכבר אמרנו, ה-WeatherData הוא המימוש של Subject
אז הוא מממש את כל המתודות שלו
שימו לב שהוא משתמש בList כדי לנהל את ה-Observers שהוא צריך לעדכן.
דבר ראשון שקופץ לעין הוא למה צריך גם את setMeasurements ואגם את measurmentsChanged וגם את notifyObservers
ובכן, צריך לזכור ששתי הראשונות הן ירושה שקיבלנו ממי שהעביר לנו את כל המערכת.

עכשיו נציג את הdisplays עצמם

הם די strait forward ואין פה לוגיקה מאוד מסובכת
אני רק אגיד שמאוד קל להוסיף תצוגות חדשות, רק לכתוב class חדש שיממש את שני הinterface ולרשום אותו לweatherData וסגרנו פינה

ועכשיו למה שמחבר הכל ביחד

והפלט שלנו יהיה

Current conditions: 80.0F degrees and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forecast: Improving weather on the way!
Current conditions: 82.0F degrees and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forecast: Watch out for cooler, rainy weather
Current conditions: 78.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forecast: More of the same
Current conditions: 62.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 75.5/82.0/62.0

סיכום

זהו להפעם. למדנו הרבה על איך עובד ה-observer design pattern
ראינו ש-observer design pattern מגדיר יחס של אחד לרבים בין אובייקטים
ה-subject הוא זה שמעדכן את ה-observers שלו
כל סוג של אובייקט יכול להיות observer כל עוד יש לו פונקציה של update
ה-observers הם loosely coupled מה-subject, והבנו גם למה זה מאוד חשוב
למי שמעוניין, אפשר למצוא את ה-pattern הזה גם ב Swing או בRxJava ובעוד ספריות אחרות

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

השאר תגובה

Scroll to Top