תבניות עיצוב ואוטומציה | Template Method Pattern



בתור מפתחי אוטומציה (ותוכנה בכלל) אנחנו נתקלים בלא מעט בעיות..

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

מה אנחנו עושים כאשר דבר כזה קורה לנו? לרוב נפנה לדר' גוגל... הרי כל דבר שקרה לנו כבר קרה למישהו אחר בעבר, ובכל בעיה בה נתקלנו, אנחנו כנראה לא הראשונים שנתקלנו בה.
ב-95% מהמקרים גוגל אכן פותר לנו את הבעיה.

מה קורה כאשר אנחנו נתקלים בבעיית Design בקוד שלנו?
אנחנו לא בטוחים כיצד לחבר את ה class-ים או שאנחנו יודעים שמה שעשינו "לא כל כך יפה" אבל אנחנו לא בטוחים כיצד לעשות זאת נכון.

לשם כך נוצרו Design Patterns!
Desing Pattens הן הפתרונות לבעיות שלנו בעיצוב התוכנה - אלו מעין תבניות פתורות וכל מה שנשאר למשתמש זה להתאים אותן למקרה שלו.

במילים אחרות, Design Patterns זה פשוט פתרון לבעיה בעיצוב הקוד.

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

באמצעות template method pattern נוכל לבצע מספר מימושים שונים לאותו שלד של אלגוריתם.

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

אז אמרנו ש design pattern הוא פתרון לבעיה. אבל מה הבעיה שלנו כאן?

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

הקוד יראה כך:



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

ואנחנו כבר יודעים איך להכין תה, אז להכין קפה בכלל לא יהווה בעיה עבורנו.

נעשה בדיוק את מה שעשינו קודם! רק שהפעם נשנה שתי פעולות קטנות:





ניתן לראות שכל מה שהשתנה בין הפעולה המכינה קפה לפעולה המכינה תה זה שני שלבים בתהליך האלגוריתם.

ועכשיו, יש לנו מחלקה שתכין לנו תה ומחלקה שתכין לנו קפה! כל מה שנשאר לעשות זה רק לקרוא לה מהתכנית הראשית של מכונת השתייה החמה שלנו.



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

קיימות שתי בעיות עיקריות בקוד שלנו.
האחת - הקוד שלנו לא עומד בעיקרון open/closed.
למה? משום שבמידה והמכונה תצטרך להוסיף עוד סוגים של משקאות ה if שלנו יכול להפוך לאינסופי, וזה דבר שפחות רצוי לעשות בתכנית הראשית.
הבעיה השניה היא שכפול קוד - אפשר לראות די בבירור שאנחנו משכפלים את הפעולות BoilWater ו PourInCup.

איך נוכל לשפר את הקוד?

נתחיל מלהוציא את המשותף בקוד אל תוך מחלקת אב (HotDrinkMaker), אשר ממנה ירשו סוגי המשקאות השונים.

אם עד עכשיו דיאגרמת ה-UML שלנו נראתה ככה:




נשנה אותה על מנת שתראה ככה:





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

מחלקת האב תראה כך:



אבל משהו עדיין חסר. אם ננסה להתרכז באילו עוד שלבים משותפים קיימים בין הכנת קפה ותה, נראה שגם שתי הפעולות הספציפיות למחלקה עושות פחות או יותר את את אותו הדבר רק בדרך שונה.
בעצם, יכולנו לקרוא לפעולות BrewCoffeeGrinds ו- SteepTeaBag באותו שם, Brew לדוגמה.
ולפעולות AddSugarAndMilk ו- AddLemon יכולנו לקרוא פשוט AddCondiments.

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

מכיוון שמעתה כל הפעולות יהיו קיימות במחלקת האב, המחלקה PrapareRecipe תהיה ממומשת.
וכעת הדברים הופכים להיות פשוטים בהרבה.

דיאגרמת ה-UML תראה כך:


והמחלקה HotDrinkMaker תראה כך:




אם זה עדיין לא ברור, השתמשנו עכשיו ב Template method pattern!

כמו שאמרנו קודם, באמצעות Template Method אנחנו מספרים את אותו הסיפור בדרכים שונות.

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


אבל איך כל זה מתקשר לאוטומציה?

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

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



סיכום

היום דיברנו על Template method pattern, מטרת הDesign Pattern הנ"ל היא למנוע שכפול קוד וליצור קריאות וקלות תחזוקה באיזורים בהם מצאנו שהקוד שלנו יכול להפוך ל template ושחלקו יכול לעבור כאבסטרקטי למחלקת האב.

נתראה בפוסט הבא :)


תגובות

  1. מאוד נהניתי לקרוא! תודה!

    השבמחק
  2. בלוג פצצה!
    אני רואה שהוא עודכן פעם אחרונה באוקטובר 2018, תמשיכו לעדכן!!

    השבמחק
  3. תודה רבה, מאד ענייני ופרקטי

    השבמחק

הוסף רשומת תגובה

פוסטים פופולריים מהבלוג הזה

תכנות מונחה עצמים | Dependency Inversion Principle

מהם קבצי DLL ואיך להשתמש בהם?

מה ההבדל בין אוטומציה לפיתוח רגיל