עיצוב תוכנה - Top-down programming




בפוסט של היום אני רוצה לדבר על קונספט שישמע תחילה בסיסי ומובן מאליו, אבל לאורך הקריאה אני ממליץ מאוד לשאול את עצמינו בשיא הביקורתיות - האם כך אני באמת כותב קוד? ואם לא, אולי כדאי לי להתחיל?
כולנו יודעים שרוב התוכנות, המערכות, ואפילו הסקריפטים שאנחנו כותבים לא מסתכמים בפונקצייה אחת ולא בשתיים. ככל שתוכנה שאנחנו כותבים מורכבת יותר וככל שאנחנו נרצה להפוך את הקוד שלנו לקריא יותר וגנרי יותר כנראה יהיו בו יותר קלאסים ופונקציות.
במקום לדבר הרבה ולנסות להסביר את המונח באופן אבסטרקטי, פשוט אצלול לדוגמה (top-down לא?).
דמיינו שאתם עובדים בצוות בו עובדים עם מכונות וירטואליות על גבי vcenter (פלטפורמת ניהול vms של vmware) והאוטומציה שרצה מייצרת הרבה מכונות. את המכונות הללו אנחנו רוצים לנקות בסוף כל יום עבודה כדי לא לצבור עומס מיותר, בהנחה שאם מישהו יצטרך את אחת המכונות הוא פשוט יזיז אותם לתיקייה אחרת.

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

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

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

קודם כל, מה זה Top-down programming?
כאשר אנחנו מפתחים top-down אנחנו מתחילים קודם ברכיבים של השכבה הגבוהה של התוכנה אותה אנחנו רוצים לכתוב ויורדים שלב אחר שלב בשכבות התוכנה עד שאנחנו מגיעים לשלב עם לוגיקה שאינה ניתנת לפירוק.

לשם לך, נכתוב תוכנית פשוטה שמטרתה לתקשר עם שרת ה vcenter ולמחוק את כל המכונות אשר נמצאות בתיקייה מסויימת יותר מ24 שעות. השפה שבה נכתוב תהיה פייתון.


כשאנחנו כותבים את התוכנה שלנו באמצעות top-down approach אנחנו רוצים להתחיל מהתכנית הראשית, משכבת האבסטרקציה העליונה ביותר. לכן נתחיל מפונקציית ה main.

ניתן לראות שפונקציית ה-main קוראת ל delete_all_old_machines שקוראת שתי פעולות בתוכה.
get_all_old_machines אשר מחזירה רשימה של כל המכונות שנוצרו לפני יותר מיום, ו-delete_machines אשר מקבלת רשימה של מכונות ומוחקת אותם.
אפשר לשים לב בקלות (באמצעות סביבת הפיתוח) שהפונקציות get_all_old_machines ו-delete_machines אינן ממומשות וזו בעצם הדרך בה נעבוד עם שיטת top down נתחיל מכתיבת הקוד ככותרות בשפה העסקית שלנו ומשם נצלול ונכתוב את המימושים שכבה אחר שכבה - כאשר נרצה לספר את תוכן הפעולה כסיפור בכותרות.

לפיכך, השכבה הבאה בקוד שלנו תצטרך להראות כך:

לאחר שמימשנו את השכבה הזו אנחנו צריכים לממש את התוכן שלה: get_all_old_machines, get_all_machines ו- delete_machines.

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



לפועולות הממומשות במחלקה vcenter לא נכנס כעת, אבל ניתן לראות בקטע הקוד האחרון פונקציית private אחת שאינה ממומשת - was_machine_created_today.
בשלב זה אני ממליץ לעצור ולנסות לחשוב - כיצד תראה הפונקציה הזו בגישת top-down?
ואני מזכיר - רצוי שתוכן הפונקציה יכיל כמה שיותר כותרות וכמה שפחות מימוש מהסיבה שאת אותן כותרות יהיה לנו קל הרבה יותר לקרוא, במידה שנצטרך לבצע שינוי בקוד נוכל להתמקד בפונקציה אותה נרצה לשנות, וכאשר נצטרך לבצע שימוש חוזר רק בחלק מהפונקציונאליות - הפונקציות הללו כבר יהיו כתובות ומחולקות.



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

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

הקשר לאוטומציה

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

מה יוצא לנו מ Top-down programming?

קוד קריא יותר - משפט שאני אוהב לחזור עליו שוב ושוב  - "כל אחד יכול לכתוב קוד שמחשב יכול להבין, רק מעטים באמת יכולים לכתוב קוד שאדם אחר מבין". גישת top-down תכפה על הכותב באמצעותה חלוקה לשכבות עסקיות שונות, ולמתודות קטנות - מה שהופך את הקוד שלנו לפחות "קוד" ויותר משפטים באנגלית.
קלות תחזוקה - לא כיף (או אפשרי) לתחזק פרויקטים מסובכים ומלוכלכים. כל שינוי הופך לסיוט וכל הוספה של קוד לאזור מסוים הופכת לסכנה ממשית של פגיעה באותו אזור. באמצעות top-down אנחנו גורמים לכל שכבה לדבר בשפה העסקית שלה ולפנות ל"קהל יעד" אחר. דוגמה טובה תהיה הפרדה מוחלטת בין תשתית האוטומציה לבדיקות האוטומטיות עצמן - בבדיקות אנחנו שואפים לראות אך ורק שפה עסקית ומובנת (אפילו למחלקת ה product) ובתשתית אנחנו נראה יותר מימושים.
קלות בדיקה של הקוד שלנו - הרבה יותר קשה ומסורבל לבדוק מחלקות ופעולות שמערבות תוכן של כמה שכבות עסקיות. גם כאן top-down מגיע לעזרתנו עם חלוקה פשוטה וברורה בין השכבות.

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

היתרונות של Top-down programming מדהימים (וחולקים את מרבית היתרונות עם גישת TDD אגב), באמצעות הגישה אנחנו נרוויח קוד הרבה יותר קריא, מסודר לשכבות הגיוניות יותר, והרבה יותר קל לתחזוקה בשל ההפרדה לפונקציות ולשכבות שלכל אחת יש את הדומיין שלה.
בנוסף, אם היינו מתחילים מהשכבה התחתונה היינו צריכים לעבוד הרבה יותר קשה ולחשוב באופן אקטיבי על איזה קוד לשים איפה, דבר שנפתר לנו באופן כמעט אוטומטי על ידי גישת top-down.

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

תגובות

  1. תודה רבה! אתה מסביר פשוט וברור :) שאלה: האם תפריד את כל השכבות וכל שכבה תכתוב בקובץ .py נפרד? או שתשים את כולן באותו קובץ? אולי יש לך התייחסות לחלוקה כזאת בפוסט אחר (חפרתי בפוסטים ולא מצאתי משהו) ?

    השבמחק

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

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

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

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

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