2013-06-27

התיאוריה המאוחדת: קוד, תכנון וארכיטקטורה

ניתן לומר שהגדרת ארכיטקטורה מורכבת מ 4 פעולות בסיסיות:
  • חלוקת המערכת למודולים / תתי מערכות
  • ניהול תלויות (בין המודולים)
  • יצירת הפשטות (Abstractions) והגדרת API
  • תיעוד הארכיטקטורה.
כמעט כל העקרונות והטכניקות של הגדרת ארכיטקטורה (למשל Quality Attributes או חלוקה ל Views) הן הנחיות כיצד לבצע פעולות בסיסיות אלו בצורה נכונה יותר.

Quality Attributes היא טכניקה שכתבתי עליה בפוסט הזה והזה.





תכנון מונחה אובייקטים - OOD


תחום ה Object Oriented Design הוא תולדה של רעיונות שהתפרסמו במספר ספרים / מאמרים משפיעים - אך בניגוד למה שניתן לחשוב, אין הגדרה "חד-משמעית וברורה" מהם "עקרונות ה Object Oriented Design".  
2 המודלים המקובלים ביותר להגדרת OOD כיום הם:
חוצמזה ניתן בהחלט להזכיר את תנועת ה Patterns (קרי POSA, PLOP, GOF) שלא ניסחה חוקים אלא "תיעדה" תבניות עיצוב מוצלחות - אבל יש לה השפעה ניכרת על הדרך בה אנו עושים היום Design (ולא תמיד לטובה).


העשור האחרון
זרם ה Agile השפיע גם הוא רבות על OOD וקידם כמה רעיונות:
  • "כשאתה מקודד - אתה בעצם עושה Design" (מקור: TDD) --> ומכאן רעיונות כמו "Design by Tests/Coding" 
  • ההכרה שביצוע Design או הגדרת ארכיטקטורה הם Waste - שיש לנסות ולייעל אותם ("Just Enough Software Architecture")
  • ההבנה שחיזוי העתיד הוא דבר בלתי-מציאותי, גם על ידי אנשים נבונים למדי, במיוחד במוצרים חדשים אך גם במוצרים קיימים. ירידת קרנם של "העיקרון הפתוח-סגור" (מתוך SOLID) ו "(Predictable Variations (PVs" (מתוך GRASP) והצבת סימני שאלה בפני כמה מהעקרונות האחרים...



התאוריה המאוחדת


בכל מקרה ישנה השאלה: בהינתן עקרונות ל"ארכיטקטורה", "תכנון" ו"כתיבת קוד" - היכן בדיוק עוברים הגבולות הללו? מתי יש להשתמש בעקרון ארכיטקטוני ומתי בטכניקת Design?

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

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

ניסיתי למפות את "ארבע הפעולות הבסיסיות של הגדרת ארכיטקטורה" לפעולות תכנון וכתיבת קוד:


אפשר לראות שמות שונים ("גבוהים" ו"נמוכים") לרעיונות דומים - אבל ההקבלה הרעיונית יפה למדי. 

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

הנה שתי דוגמאות:

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

עקרון ארכיטקטוני: Interface Segregation Principle 
עקרון זה אומר שמודול לא אמור להיות תלוי ב interfaces רבים שאינם בשימוש. אם נוצר מצב כזה - יש לפצל את ה Interfaces כך שהמודול יהיה תלוי, עד כמה שאפשר, רק ב Interfaces שהוא משתמש בהם בפועל. העיקרון נכון מאוד גם למתודות בתוך interface יחיד (רמת ה Design) או לפרמטרים בחתימה של פונקציה בתוך הקוד (רמת הקוד).

עוד היבט קוד שאני מאמץ מעקרון ה ISP הוא לנסות ולהימנע משימוש בספריות (כגון "Open Source") שיש לי מעט שימוש בהן. אני אשתדל לא להכליל במערכת ספרייה של אלף שורות קוד - אם אני משתמש רק ב 50 מהן. אני מעדיף למצוא ספרייה אחרת או אפילו לכתוב אותן שורות קוד לבד. מדוע? א. סיכוי לבעיות מ 950 שורות קוד לא רלוונטיות, ב. מסר לא ברור האם נכון "להשתדל" להשתמש במתודות אחרות בספריה או לא. ג. אם צריך לשנות / לדבג - ייתכן וצריך להבין הרבה קוד בדרך שלא רלוונטי למקרה שלנו.


אפשר להראות כמה דוגמאות של עקרונות ש"עוברים פחות נקי":
  • מקבילה ל"ניהול תלויות" ברמת הקוד - לא מצאתי בדיוק. הסתפקתי בעקרון של הימנעות ממשתנים גלובליים.
  • לרעיון של חלוקת מודולים ע"פ "Unit Of Work" (כך שקבוצות פיתוח שונות יוכלו לעבוד במקביל עם מינימום תלות) - אני לא חושב שיש הקבלה אמיתית ברמת קוד.
  • העיקרון האלמותי של (DRY (Do Not Repeat Yourself הוא "No Brainer" בקוד, אבל הופך לנושא מורכב ולא חד-משמעי ברמת הארכיטקטורה.


בסופו של דבר - יש הרבה מאוד חפיפה בין עקרונות "ארכיטקטורה" לעקרונות "קוד", כך שאין כ"כ חדש ללמוד. הרעיונות חוזרים על עצמם בשינוי אדרת. חלק מהעקרונות (למשל KISS = Keep It Simple Stupid) הם פשוט אוניברסליים.


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

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


הרי שזו חריגה ברורה מהעיקרון. הפונקציה drawItems עוסקת בפרטים: כיצד לצייר פריט אחר פריט. מה פתאום היא מבצעת שמירה לבסיס הנתונים?! (פעולה ברמת הפשטה גבוהה יותר - OMG!)

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

קושי מסוים בשימוש ב SLAP הוא שאין "מד רמת-הפשטה", כזה שנכוון אותו לשורה בקוד והוא יגיד לנו "רמה 6!" או "רמה 7!". זה הכל בראש שלנו כמפתחים ובני-אדם אינטליגנטים. לפעמים יהיו "סתירות" כך שיוחלט שפעולה X תהיה פעם אחת ברמה n ופעם אחרת ברמה n+1. אני אומר: זה אנושי. זהו עקרון חשוב - פשוט קחו אותו בפרופורציה ("We figured they were more actual guidelines").


סיכום

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


שיהיה לנו בהצלחה!



פ.ס. : הערות, מחשבות, ביקורות - יתקבלו בהחלט בשמחה!



10 תגובות:

  1. ישראל28/6/13 00:10

    היי ליאור,

    פוסט מעולה ככל הפוסטים שלך, בזכות ה-feed קראתי אותו מיד שיצא לאוויר העולם ...
    בדרך כלל יש לי טינה כלפי מי שמבקר תכנים במידה ואין לו טיעונים מוצקים.
    כעת הבאת עיקרון Literal Code ששמעתי עליו רבות וקשה לי להסכים עימו, הוא נכון אולי במודולים זניחים שתפקידם לפרסר CSV/XML או לייצא קובץ במבנה אחיד למס הכנסה וכו' בהם אפשר לכתוב קוד שיסביר את עצמו ללא הרבה תיעוד והערות אבל מעבר לכך הוא תאורטי ובלתי אפשרי ליישמו בעולם האמיתי, אני מכיר ומתחזק מנגונים גדולים של בילינג ניהול תקציבים וחישובי הצמדות המייצגים תהליכים עסקיים מורכבים, לא שייך במקרים כאלו לכתוב פונקציה בלי 10-20 שורות הערות וכמובן שלא לפצל אותה כי זה יסבך את הקוד עוד יותר.
    נראה לי שאתה מכיר דברים כאלו ותסכים עם הגישה..

    השבמחק
    תשובות
    1. היי ישראל - תודה על התגובה!

      א. שגיאת כתיב שלי: התכוונתי Literary (ספרותי) ולא Literal (מילולי).
      ב. בהנחה שאנו עדיין מדברים על אותו הדבר, אני דווקא מאמין גדול בקוד עם מינימום הערות, גם ואולי דווקא במערכות גדולות ומורכבות (לנו יש מערכת גדולה למדי, 2.5 מיליון שורות קוד, ויש בה מורכבות).

      אתן דוגמה פשוטה:
      במקום לכתוב בפונקציה את 4 השורות (יש לי מגבלה על כיוון הכתיבה ולכן אכתוב בעברית):
      // הערה: הכן נתונים לחישוב
      data = פונקציה1 (data);
      data = פונקציה2 (data);
      data2 = פונקציה3 (data);

      אני אבצע extract method לפונקציה שנקראת "הכן נתונים לחישוב" (כמו ההערה) שתכיל את 3 השורות האחרות. התיעוד עבר מהטקסט לקוד.

      דוגמה שנייה (קטנה):
      // אם n גדול מ 4 אין מקום ב ABC
      if (x>4) dosomething

      יכול להכתב כ:
      var hasNoRoomAtABC = x > 4
      if (hasNoRoomAtABC) doSomething
      ולוותר על ההערה.

      למה? - הערות (במיוחד ארוכות) אנשים לא קוראים והן יוצאות מסנכרון עם הקוד. קוד קוראים.

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

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

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

      לסיכום: הניסיון שלי דווקא גרם לי להאמין ש Literary Code הוא לא רק אפשרי אלא גם נכון (ברוב המקרים) והאמת... לעשות שינוי גדול בקוד ולראות "לפני" ו"אחרי" - זה באמת כיף. :)

      ליאור

      מחק
    2. Moti Mendelovich28/6/13 10:20

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

      https://gist.github.com/anonymous/3753571

      מחק
    3. ישראל28/6/13 14:32

      היי ליאור,

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

      ישראל.

      מחק
    4. ישראל ג.30/6/13 18:43

      היי ליאור

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

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

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

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

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

      מחק
    5. היי ישראל ג.,

      תודה!

      אני סה"כ מסכים אם מה שאתה אומר (אולי יש לנו פער קטן לגבי המידתיות) - אבל בהחלט יש מקום להערות בקוד. פשוט הייתי מנסה להקפיד ולכתוב רק הערות שלא ניתן / לא יעיל לבטא כקוד.

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

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

      שוב תודה על התגובה,

      ליאור

      מחק
  2. אנונימי29/6/13 21:44

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

    השבמחק
    תשובות
    1. היי,

      נראה לי שאני נוטה להסכים, אם כי עדיין הייתי רוצה לוודא שאנו מדברים על אותו הדבר:

      "אריכטקטורת תהליכים עסקיים" נשמעת לי כמו משהו שאני הייתי קורא לו "בניית תהליכים" (או Business Process Re-engineering). אני מייחס חלק זה ל"הגדרת המוצר" ולא לפעולה של "הגדרת הארכיטקטורה". כמובן שבהגדרת ארכיטקטורה יש שיקולים מוצריים / פרסונליים / ארגוניים - שלא יעשו ע"י אחת מ-4 הפעולות שציינת. עד כאן נראה לי שאנו מסכימים.

      "ארכיטקטורת אינטגרציה" - נשמעת לי כמו עוד אתגר טכנולוגי כיצד לגרום לכמה מערכות לעבוד זו עם זו. עדיין נראה לי שתרצה להגדיר מודולים ואחריויות, abstractions ו APIs, לנהל את התלויות בין המערכות וכו'. אני מפספס משהו?

      ליאור

      מחק
    2. אנונימי30/6/13 03:27

      מה שאתה אומר לגבי אינטגרציה נכון אבל לא שלם. במערכות מורכבות יש דברים נוספים מעבר להגדרות API ותלויות למשל איך לנהל טרנסאקציות מבוזרות או אספקטים של security (סליחה שלא מצאתי את המונח המדויק בעברית) או שימוש בESB - כל אלה אספקטים ארכיטקטוניים שגדולים יותר מאפליקציה אחת (ולכן הם חלק מ enterprise architecture) שאכן מתמפים לדרישות עבור אפליקציה ספציפית. עדיין הם חלק מארכיטקטורת הIT של הארגון

      מחק