ארכיטקטורה: Quality Attributes (חלק ב\' – כיצד משתמשים)

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

כיצד בוחרים מאפייני איכות?
ראשית, על מנת לעזור לנו להגדיר את מאפייני האיכות של המוצר / רכיב שלנו, יש כמה \"רשימות\" נפוצות של מאפייני איכות – שיוכלו לעזור לנו להתחיל. אני מתבסס על רשימה של קארל ויגרס מספרו העוסק בדרישות תוכנה, אך יש גם רשימות אחרות.

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

Usability – ב\"עידן התפוח\" לא צריך להסביר כבעבר את החשיבות של השימושיות. שימושיות נובעת מה UI, ומהמהירות בו הוא מגיב, אך גם משפיעה על פנים המערכת: דרישה של שאילתות שקשות לביצוע אך מועילות למשתמש, זמינות נתונים וכו\'. השאלה המעניינת, בעידן התפוח, היא מה יותר חשוב במערכת שלכם מ Usability – וסביר שבמערכות רבות יהיו שיקולים שכאלו.

Security (נקרא במקור Integrity) – עד כמה מוגבלת / מאובטחת צריכה להיות הגישה לנתונים / שירותים של המערכת.

Efficiency – עד כמה יעילה המערכת בניצול משאבי החומרה העומדים לרשותה, או בניצול שירותים אחרים (כגון SOA) – והחומרה העומדת לרשותם.

Portability – היכולת של המערכת לרוץ בסביבות ריצה שונות, קרי מערכות הפעלה, סביבות ענן, תצורות רשת וכו\'.

Reusability – היכולת לבצע שימוש חוזר במודול שנכתב. מאפיין איכות זה לא אמור לעסוק ברמת המחלקות הבודדות, כי אם ברמת השירות ה high-level. עוד בנושא זה בהמשך.

Interoperability – היכולת להחליף נתונים בקלות עם מערכת אחרת. באופן אישי לא מצאתי מאפיין זה שימושי. Interoperability נראה לי יותר כמו פיצ\'ר נקודתי מאשר יכולת רוחבית. אני משאיר אזכור לצורך שלמות הרשימה המקורית.

Maintainability – עלות תפעול (Administration / Operations) נמוכה של המערכת. מה שנקרא TCO (Total Cost of Ownership). מאפיין זה יכול להצביע על הוספת \"פ\'יצרים של ניהול המערכת\". עוד נושא שנכנס לקטגוריה זו היא קלות ההתקנה של המערכת, לעתים מתייחסים אליה כמאפיין איכות עצמאי: Installability.
דוגמה: כלי monitoring שיסייעו לנטר בעיות במערכת. השקעה ב Upgrade קל וכו\'.

Developability (נקרא במקור Flexibility) – היכולת לפתח את המערכת בקלות, לבצע בה שינויים או לתקן באגים. מה שנקרא TCD ((Total Cost of Development מאפיין איכות זה עוזר לאפיין את הדילמה שבין איכות שמוסיפה סיבוכיות למערכת (Portability או Efficiency, למשל) למול היכולת לפתח מהר וביתר קלות.

Extensibility – היכולת להרחיב ולהוסיף יכולות חדשות למערכת, בעזרת Plug-in, API וכו\'. כמובן שיכולת ההרחבה היא ליכולות / אזורים ספציפיים במערכת.

Supportability – היכולת לתמוך במוצר בקלות, אם מדובר בגוף המייצר את התוכנה או גוף שלישי. כלי Monitoring, לוגים קריאים, יכולת לבצע Dumps של זיכרון או נתונים, דו\"ח על תצורת המערכת וכו\'.

Scalability – היכולת של המערכת של המערכת לטפל במספר הולך-וגדל של בקשות ו/או היכולת של המערכת לגדול על מנת להמשיך ולהתמודד עם גדילה זו. לרוב עושים הבחנה בין Vertical Scalability – יכולת לצרוך ביעילות חומרה יותר חזקה ו Horizontal Scalability – היכולת של המערכת להתפרס על מספר שרתים פיסיים (cluster).

Testability – היכולת לבצע בדיקות למערכת. באופן אישי, מאפיין זה נראה לי כמו אמצעי להשגת Reliability או Developablity – ולא מטרה בפני עצמה.

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

Availability – מאפיין זה תקף בעיקר לשרתים (או שירותים) והוא מורכב מזמינות (אחוז הזמן שהשרת / שירות זמין ללקוחותיו) או תדירות הכישלונות (מה שנקרא MTBF). שרת יכול להיות זמין 99% מהזמן אך לכשול לשנייה אחת – כל יום, מה שיגרום לתקלות כואבות. (MTBF (Mean Time Between Failures עוזר להשלים את התמונה שנתון הזמינות לבדו לא מספק היטב.

רשימת \"מאפייני איכות\" ממקורות שונים.  מקור: http://www.clarrus.com/documents/Software%20Quality%20Attributes.pdf

כמה הערות על הרשימה:
שימו לב שבהכללה גסה Reusability, Developability ו Testability הם איכויות פנימיות (משפיעות על הפיתוח) בעוד היתר הן איכויות חיצוניות (משפיעות על הלקוח).

Extensibility, Maintainability ו Supportability הם סוג של \"פיצ\'רים\" שאולי מנהל המוצר לא ייזום ולכן על הארכיטקט להציע ולקדם. לכאורה נראה שהם שונים משאר מאפייני האיכות מכיוון שהם \"פיצ\'רים נוספים למערכת\". בפועל – השקעה בכל מאפייני איכות (למשל Availability או Safety) דורשת עבודה וההבדל בין שלושת מאפייני איכות אלו לשאר היא קטנה משנדמה במבט ראשון.

בחירת מאפייני איכות

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

מכיוון שכל מאפייני האיכות הם \"דברים טובים\" האתגר הוא להבחין מה יותר חשוב ממה.

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


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


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

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

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

במשך שנים, נושא חם בשוק ה IT היה בעיית ה Lock-In: בחרת ספק מסוים, לו יש API מסוימים. השתמשת בהם והיית מרוצה, אך ביום בו הספק מעלה מחירים / מחפף בשירות / קמים מתחרים עדיפים – \"אין לכם שום דרך לבצע שינוי\".
האמירה של \"אין שום דרך\" היא אמירה לא מדויקת – אך מכיוון שהסיפור (אלמנטים טרגיים של בגידה?) הוא סיפור מוצלח – הוא הושרש והשאיר חותמו על אנשים רבים.

אחד ה \"Best Practices\" שנבעו מכך הוא לכתוב קוד \"בלתי תלוי בספק (vendor)\" בכלל, ו\"בלתי תלוי בספק בסיס-הנתונים\" בפרט. עבדתם עם מערכת אורקל ומחר אתם רוצים לעבור ל MS-SQL? אין בעיה – מחליפים רק את ה \"connection string\" – והכל עובד.

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

  • באף אחת מהמערכות הללו לא החליפו בסיס נתונים, עד היום (עד כמה שידוע לי).
  • הצוות הקפיד להשתמש ב ANSI SQL – וכך התעלם מרוב היכולות של בסיס הנתונים. אי-השימוש ביכולות הקשה בצורה משמעותית על הפיתוח ועל שיפורי ביצועים.
  • בכמה פעמים, המוצר נזנח / נכתב מחדש – אך בסיס הנתונים נותר. כלומר: בסיס הנתונים היה חלק יציב יותר מהקוד שהשתמש בו.

בפועל, ההחלטה המעשית הייתה לבחור במאפיין האיכות \"no DB vendor lock-in\", על פני \"Developability\" ועל פני \"Scalability\".

רק להבהיר: אני מדבר על מקרה בו אתם מוכרים Appliance או Hosting. כאשר הלקוח צריך לטפל בבסיס נתונים בעצמו, ברור למדי שלחברות רבות יש העדפה ברורה איזה בסיס נתונים הם רוצים להתקין: אולי יש להן DBA מוכשר שמכיר אותו, אולי הן כבר ביצעו השקעה מסוימת בטכנולוגיה (כלי ניהול, אבטחה וכו\') ספציפית לספק בסיס-נתונים זה.
מתן האופציה להתקין כל DB, עבור לקוחות רבים – יכולה להחשב כאיכות של Maintainability – כי אז הם יוכלו להתקין את בסיס הנתונים שהם מעדיפים.
הבחירה של פלטפורמות ג\'אווה וNET. לבצע הפשטה שכזו (בעזרת ODBC ו JDBC) באה לתמוך במי שרוצה להשיג Maintainability.
אם כל מה שאני שומר בבסיס הנתונים היא טבלה או 2 של נתונים פשוטים – אין לי רווח רב משימוש בבסיס נתונים ספציפי.


בכל מקרה – אין פה תשובה נכונה או שגויה. עצם העניין הוא הבחינה המודעת של האלטרנטיבות. ההבנה שכתיבת קוד \"Database Agnostic\" הוא מאפיין איכות אפשרי שמגיע עם תג מחיר, ולא בהכרח \"הנדסת איכות טובה יותר\".
כניסה לפרטים
גפרור הוא מוצר דיי קטן. מערכת ERP היא גדולה יותר וכנראה שלא סביר להגדיר, באופן גורף, ש\"דיוק עדיף על נוחות\" או \"יכולת אחזור עדיפה על מהירות\" לגבי מערכת שכזו. הרי החלטה זו, יכולה להיות נכונה עבור חלק אחד במערכת – ושגויה עבור חלק אחר.


דרך מומלצת 
על מנת להשתמש במאפייני איכות בצורה יעילה כדאי לבחור כמה תסריטים עיקריים במוצר ולהדגיש אותם:
\"בתחום ה X אנו מעדיפים d על c ומעדיפים b על a\".

אפילו כדאי לצרף דוגמאות ספציפיות, מה שנקרא \"תסריט\":

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

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

Reusability

עוד תחום בו סקירת מאפייני האיכות יכול לסייע רבות הוא בהחלטה האם לעשות שימוש חוזר בקוד או \"איחוד\" של 2 מודולים בעלי פונקציונליות דומה.
לעתים רבות יש לנו 2 ספריות / מודולים שרשימות היכולות שלהן, כ checklist היא דומה או אולי זהה. לדוגמה: ספרייה לשליחת הודעות דוא\"ל. שאלה מתבקשת היא \"מדוע אנו מתחזקים 2 ספריות שעושות אותו הדבר? האם זה לא בזבוז?\"
לפני שאתם רצים לאחד את הקוד לספרייה יחידה, כדאי לבחון מהם מאפייני האיכות של כל ספרייה.
אם ספרייה אחת מקפידה על reliability (מכיוון שהיא משמשת לשליחת התרעות מערכת חמורות), בעוד השנייה מתמקדת ה customizability (היכולת להגדיר הודעות דואר יפות ומותאמות-אישית) – ייתכן ואין סתירה וניתן לשלב אותן בהצלחה. כלומר, ספרייה אחת לא תוכל להחליף מייד את השנייה, אך הגיוני לקחת חלקים מכל ספרייה ולאחד אותן. חשוב להבין אלו הנחות נעשו על מנת לאפשר את מאפייני האיכות העיקריים של כ לספרייה ולראות שהם לא מתנגשים.
לעומת זאת, אם אחת מתמקדת ב customizability והשנייה ב performance/scalability (שליחת אלפי הודעות בדקה) – קרוב לוודאי שטוב תעשו אם תשמרו אותן כ2 ספריות נפרדות.
לעתים דיי קרובות, ספריות שאיחודן נראה כ no brainer במבט ראשון, מתגלות במהרה כלא סבירות לאיחוד לאחר שבוחנים את מאפייני האיכות שלהן.

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

נ.ב. – חלק מהספרות הפורמלית בנושא מתאר את \"מאפייני האיכות\" קצת אחרת – אני התייחסתי אליהן בצורה שהייתה שימושית עבורי והוכיחה את עצמה. חפשו בגוגל \"Quality Attributes\" ותוכלו למצוא חומר רב.







[א] הגפרור שאנו מכירים.


[ב] אני מכחיש בתוקף!, אך בפועל ייתכן שפעם אני הוא זה שיזם את המהלך.


[ג] Effective, לא efficient.



ארכיטקטורה: Quality Attributes (חלק א\' – מבוא)

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

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

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

\"באמת ארכיטקטורה זה כ\"כ משעמם?\" – היה קול אחד שניקר בראשי.
אולי אני לא מבין שומדבר (\"you know nothing Jon Snow\") – היה קול ספקני שני.

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

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

אחד הכלים הללו נקרא מאפייני איכות (Quality Attributes) – ועליו נדבר בפוסט זה.

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

מה מאפייני-איכות הם לא?

הנה screenshot ממצגת שראיתי, אשר ניסתה להסביר \"מהם מאפייני איכות\":


המטפורה הזו היא שטותית!

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

לא קיים מוצר ללא מאפייני איכות. לכל היותר קיים מוצר שמאפייני האיכות שלו נקבעו בצורה לא-מודעת.

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

אנסה להסביר מהם מאפייני איכות בעזרת דוגמה: חי-הסוללה של מחשב נייד (Laptop).
על מנת להגיע לחיי סוללה גבוהים של מחשב נייד מנהל המוצר יכול לדרוש \"סוללה חזקה במיוחד\" שזה סוג של \"פ\'יצר\" – אך זה לא יספיק. על מנת שהמכשיר יחזיק זמן רב ללא טעינה יש להשתמש ברכיבים (מעבד, דיסק) שצורכים מעט חשמל ולעתים קרובות יש תוכנה תומכת (כגון כיבוי משדר ה wi-fi או אפילו מחברי ה USB לאחר זמן ללא שימוש).

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

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

אנו רואים ש\"חיי סוללה ארוכים\", עבור מחשב נייד, זו איכות רוחבית של המוצר. \"מאפיין איכות\" (Quality Attribute), בשפה שלנו.

הנה דוגמה לבחירה בין מאפייני איכות שונים:

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

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

ניתן לומר שבנוסף לצרכים בסיסיים מאוד של \"הסעת נוסעים\" – כל אחד מהרכבים מספק צרכים אחרים שמאופיינים באיכויות רוחביות של המוצר.

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

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

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

מצד שני יבואו המפתחים עם שאלות:
אתה רוצה להשקיע 5 ימי פיתוח בהגנה בפני Directory Traversal[ב]? זו כמובן שאלה לא הוגנת – שרוב אנשי המוצר פשוט לא מסוגלים לענות עליה.
\"כמה יהיה \'ציון האבטחה\' של המערכת שלנו עם ובלי Directory Traversal?\" היא שאלת הנגד המעשית של איש המוצר – שהמפתחים מצידם לא יהיו מסוגלים לספק לה תשובה.

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

כדאי מאוד שמישהו ייקח באופן מודע את ההחלטה לגבי מאפייני האיכות, מישהו:

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

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

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

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

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

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

בהצלחה!

—-

[א] ישנם מספר גופים שמגדירים הגדרות לגבי ארכיטקטורה והנדסת תוכנה בכלל. SEI (Software Engineering Institute) היא דוגמה לגוף שכזה. לגופים אלו לרוב יש השפעה רבה על ארגוני-ענק ועל האקדמיה, אך נראה שהשפעתם על כלל התעשייה – מעטה.

[ב] סוג של פגיעות מערכת, בהיבט האבטחה.

סדרה: מבוא מואץ ל JavaScript ו jQuery – עבור מפתחי C# / Java מנוסים

את דרכי המקצועית התחלתי ב #C, ולאחר מכן עברתי את רובה ב Java.
כאשר נדרשתי להבין גם JavaScript ו jQuery מצאתי הרבה חומר טוב למתחילים, אך מעט חומר שהיה יעיל למפתח Java ותיק. כזה שכבר יודע לתכנת, יודע לולאות, מערכים או Exceptions – אבל לא מכיר ספציפית את JavaScript.

בסדרת פוסטים זו ריכזתי מבוא מואץ ל JavaScript ו jQuery שלא אמור לשעמם מפתחים מנוסים.

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

jQuery – מי, מה, מו?

להבין את JavaScript's this. המילה השמורה this בג'אווהסקריפט נשמעת דבר פשוט, אך היא מבלבלת למדי – עבור מי שהגיע מעולם ה Java (או #C או ++C או Pascal או …)

סקירה של יכולת מתקדמת של jQuery (מה שידוע כ Deferred Object), המסייעת לכתוב קוד אסינכרוני מסודר גם כאשר כמות הקוד היא גדולה.
בהצלחה!

מפרשן מול מהדר – מה ההבדל?

.lior_comment { background-color: #fff2cc; border: 1px solid #BFBFBF; margin: 4px 0; padding: 4px; } למי שלא עשה קורס ב\"קומפילציה\" (ואולי גם למי שכן), עולה מדי פעם השאלה: \"מה ההבדל בין מפרשן (interpreter) למהדר (compiler)?\"
– שניהם הרי גורמים לקוד של תוכנה לרוץ על מכונה כלשהי.

התשובה הפשוטה היא כזו:
מהדר (Compiler) – מתרגם תוכנה שנכתבה בשפה \"קריאה לאדם\", כיחידה שלמה, לקוד בשפת מכונה (אפסים ואחדים) שמבוצע ישירות על המעבד.
מפרשן (Interpreter) – מריץ את התוכנה, שורה אחר שורה ו מפרשן כל שורה בנפרד. כל שורה עוברת תרגום \"משפת אדם\" לשפת מכונה – ואז מורצת, וחוזר חלילה.

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

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

מפרשן יכול לספק debugger בהשקעה אפסית כמעט – עוד יתרון. הקלות בכתיבת מפרשן יכולה להיות מתורגמת בקלות לכתיבת שפה cross-platform – יתרון נוסף.

כשעבדתי ב NICE, השקעתי כמה חודשים טובים בכתיבת מפרשן לשפה מוזרה ללא שם. זה היה אתגר מרתק ומעשיר, עד לשלב בו נתבקשתי לממש מצביעים (pointers). זה היה מספיק על מנת שאלחץ שוב, עד שהצלחתי לשכנע את המנהל שלי להשקיע כ 200$ ברכישת רישיון למפרשן JavaScript – שפה שמילאה בקלות אחר כל הצרכים והיה אלגנטית בהרבה (תארו לעצמכם).

לכתוב מפרשן, זה בסה\"כ דיי פשוט. קוראים שורה מקובץ (להלן ה\"קוד\"), מפרסרים מה כתוב שם (הכי פשוט שהפקודה היא המילה הראשונה) ואז מבצעים בשפה שלכם (#C למשל) את מה שרציתם לבצע. הדברים נהיים קצת יותר מורכבים כאשר יש משתנים, פונקציות, Scope ויותר. על המשמעות של הכנסת מצביעים אני לא יודע לספר.

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


אולי כדאי שבעצם נשאל את עצמנו מה היתרון בכתיבת מהדר? מדוע להשקיע מאמץ רב בתוכנה שתתרגם שפה לפלטפורמה יחידה, ושמציבה אתגר משמעותי בכתיבת debugger?

התשובה היא פשוטה: ביצועים. מהדר עובד קשה פעם אחת – אך הוא מספק קוד מכונה שיכול לרוץ בצורה אופטימלית ללא התערבות בזמן ריצה. מפרשן לעומת זאת – היא תוכנה שרצה ומפרשנת שורה אחר שורה. התקורה היא ברורה [א].

נוסטלגיה. כנראה שרוב קורסי ה\"קומפילציה\" בארץ נעשו על בסיס ספר זה מ 1986. מקור: amazon.com

המצב בפועל
בואו נבחן כמה שפות מוכרות: האם הן שפות-מהדר או שפות-מפרשן?

שפת אסמבלי
בואו נתחיל מאסמבלי: טעות נפוצה היא לקרוא לשפת אסמבלי (assembly) בשם \"אסמבלר\". אסמבלר הוא השם של המהדר שלה. האם האסמבלר \"מתרגם תוכנה שנכתבה בשפה \'קריאה לאדם\', כיחידה שלמה, לקוד בשפת מכונה (אפסים ואחדים) שמבוצע ישירות על המעבד\"? כן. 
שפת אסמבלי היא בעצם ייצוג טקסטואלי, בעזרת מילים \"בשפת אדם\" כגון JMP ו MOV, לפקודות מכונה. האסמבלר פשוט עושה מין \"Search and Replace\" של \"JMP\" ל 00010010101101, למשל.

יש לי הסתייגות קלה מהגדרת שפת אסמבלי כ\"קריאה לאדם\". אני חושב שכדאי מאוד לעשות את ההבחנה בין \"Human Readable\" לבין \"Human Parsable\". ברוב הפעמים שמדברים על Human Readable (לדוגמה קובץ XML) רוצים לומר שזה טקסט שאדם יכול \"לפענח\", לאו דווקא \"לקרוא\".

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




שפת JavaScript
ג\'אווהסקריפט הייתה במשך שנים דוגמה קלסית לשפת מפרשן. הסיבה העיקרית הייתה כנראה הרצון להריץ קוד שלה על סביבות שונות: כתוב את הקוד פעם אחת והוא ירוץ על ספארי (מק), FF (לינוקס) או IE (חלונות). אם הקוד קומפל לשפת מכונה אחת – הוא לא יתאים למכונה אחרת ולכן האפשרות היחידה היא לשלוח את קוד המקור למחשב עליו רץ הדפדפן – ושם לפרשן אותו.
בשנים האחרונות, הפכו נפוצים מהדרי JIT [ב] לשפת ג\'אווהסקריפט. דוגמה בולטת היא מהדר ה JIT של V8 – מנוע  הגא\'ווהסקריפט של גוגל. התוצאה: קוד המקור נשלח לדפדפן לפני ההרצה, אך הדפדפן מחליט להדר אותו ואז להריץ אותו בשפת מכונה – כך שבפועל כיום, JavaScript רצה מהודרת. הנה דוגמה ליתרון שבהידור. דמיינו את הקוד הנפוץ הבא:

var a.b.c.d.e.f.g.h.x = 100;
var a.b.c.d.e.f.g.h.y = 200;

כאשר a.b.c.d.e.f.g.h הם שמות namespaces או אובייקטים בג\'אווהסקריפט. כאשר יש מפרשן, עליו לקחת את אובייקט a ואז לחפש בו property בשם b, ואז על b לחפש property בשם c וחוזר חלילה. ללא שיפורי ביצועים מיוחדים יכול להיות שמדובר בעשרות פעולות בזיכרון.
מהדר לעומת זאת קורא את 2 השורות ומבין שיש פה אופציה לייעל את הקוד. הוא יכול להפוך את הקוד ל:

var _p = a.b.c.d.e.f.g.h;
var p.x = 100;
var p.y = 200;

שיהיה יעיל בהרבה. מכיוון שג\'אווהסקריפט היא שפה דינמית, עליו לוודא ש a עד h לא השתנו בעקבות ההשמה של x. כאן שוב יש מין \"מיקרו מפרשן\" שהמהדר מייצר ופועל בזמן ריצה.





שפת Java
Java היא שפת מהדר – נכון?
כן. בג\'אווה יש מהדר שהופך קוד ג\'אווה לקוד bytecode, אבל קוד ה bytecode עובר פרשון (מכיוון שהוא אמור להיות cross-platform). כבר מזמן יש JIT Compilers ל bytecode, כך שבפועל ג\'אווה עוברת הידור ראשון בסביבת הפיתוח והידור שני על פלטפורמת ההרצה – מה שמתגלה כנוסחה יעילה למדי!
גם כאשר מהדרים קוד ++C למעבד מסויים (למשל מעבדי אינטל 64-ביט) יש לדגמים שונים יכולות מעט שונות – יכולות שלא ניתן לנצל בקוד המהודר מכיוון שעל המהדר לפנות למכנה המשותף הנמוך. הידור דו-שלבי כגון זה של ג\'אווה מסוגל לבצע אופטימיזציות ייעודיות לחומרה הקיימת – ולנצל את כל היכולות / התכונות של החומרה.

שפת CoffeeScript
לקופיסקריפט יש מהדר, אך הוא עושה דבר מוזר: הוא מתרגם קופיסקריפט לג\'אווהסקריפט. בשל הבא קוד הג\'אווהסקריפט  (המהודר) נשלח למכונת משתמש-הקצה ועובר הידור ב JIT Compiler – כך שיש לו הידור כפול, אך קצת שונה משל ג\'אווה מכיוון ששפת הביניים (intermediate language) היא ג\'אווהסקריפט – שפה גבוהה בהרבה מ bytecode והיכולת לבצע אופטימיזציה בקוד – פחותה.
בפועל, אתם מבינים, יכולה להיות שרשרת דיי מורכבת של מהדרים ומפרשנים בדרכו של קוד תוכנה להיות מורץ. הדרך לתאר את כל האפשרויות הקיימות היא מורכבת, ויתרה מכך – כנראה לא מעניינת.


(DSL (Domain Specific Languages
סוג חדש של שפות שהופיע לאחרונה[ג] הן שפות מבוססות דומיין, כלומר שפות מתאימות לפתור בעיות מסוג מאוד מסוים: ויזואליזציה של נתונים בתחום מסוים, ביצוע שאילתות על נתונים, קביעת תצורה של Firewall וכו\'.
לרוב לא מדובר בשפה חדשה לגמרי, אלא שפה ש\"מורכבת\" על שפה קיימת. אלו יכולים להיות פונקציות או אובייקטים שמתארים עולם מונחים שמקל על פתרון הבעיה ובעצם מהווה \"שפה חדשה\".

לדוגמה: ניתן לחשוב על \"שפה לחישוב שכר\". מי שמגדיר את החוזה (להלן ה\"קוד\") יכול בעצם לכתוב בשפה גבוהה כמו שפת Scripting או קובץ קונפיגורציה, ולהשתמש במונחים שנכונים לעולם זה. הקוד יהיה קריא למדי למישהו מתחום השכר, גם אם איננו יודע תכנות.

האם שפת השכר שלנו, \"P\" נקרא לה, היא שפת-מהדר או שפת-מפרשן?
אם היא ממומשת מעל Groovy, למשל, אזי אפשר להתייחס אליה כסוג של שפה-מפורשנת (לגרובי).
כלומר: שפה-מפורשנת (ע\"י גרובי), שעוברת קומפלציה (לבייטקוד) ואז מקומפלת (ב JVM JIT Compiler) לשפת מכונה שכוללת מיני-מפרשנים (שגם הם קומפלו בדרך לשפת מכונה). באאאאההה.

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

בפרק הבא בסדרה: \"מה ההבדל בין חומרה לתוכנה\"?
האם חומרה היא \"מה שניתן לגעת בו\", או אולי ייתכן שחיווט של בקרי דיסק (בהחלט ניתן לגעת בחוטים) הוא סוג של תוכנה, בשפת DSL של טכנאי-מחשבים?

עדכון: הנה נתקלתי בבסיס נתונים שמהדר SQL ל ++C ואז לשפת מכונה.

—-

[א] זה כמובן איננו טיעון שפוסל שפות שירוצו על מפרשן. שפות מסוימות (בעיקר שפות gluing) לא זקוקות לפרשון מהיר. קחו לדוגמה שפות כמו Bash או Windows PowerShell – הן מפעילות תוכנות יעילות, אך אם לכל שורה שלהן לוקח עוד כמה מילי-שניות – אין לכך כל חשיבות.

[ב] (JIT (Just In Time הוא מונח של \"ייצור רזה\" שאומר \"עשה את הפעולה רק מתי שצריך – לא לפני כן\". הרעיון הוא שאם עושים פעולה (כגון הזמנה של מלאי חדש למחסן) לפני הרגע האחרון – היעילות היא איננה מרבית. JIT Compiler הוא כזה שמקמפל את הקוד ממש לפני ההרצה – ולא לפני כן. יש בכך יתרונות וחסרונות – מצד אחד יודעים בדיוק את הסביבה בה הקוד עתיד לרוץ וניתן לבצע אופטימיזציות לפלטפורמה הספציפית (לדוגמה דגם המעבד), מצד שני כנראה שהמשתמש לא ימתין לאופטימיזיות קוד שאורכות זמן רב.

[ג] בעצם הוא קיים המון זמן, אבל המודעות לקיים – היא זו שהתפתחה לאחרונה + קיבלה fancy name.


מקביליות עם jQuery (ובכלל)

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

ה\"מקושרים\" שבכם, אלו שקוראים הרבה חדשות טכנולוגיות, בוודאי שמעו כבר על כמה שינויים שהוכרזו ע\"י jQuery לאחרונה:

  • היכולת לארוז רק חלקים מהספרייה (אולי בגלל התחרות מ Zepto.js).
  • הפסקת התמיכה בגרסאות לא-חדשות של Internet Explorer.
  • איחוד מנגנוני הרישום לאירועים: bind, live ו delegate למנגנון ה on (חדשות ישנות יותר).
האמת שמדובר בשינוי שהוצג ב jQuery גרסה 1.5 (עם כמה תוספות ב 1.6 ו 1.7) – ואישית, אני לא זוכר שהוא עשה חצי מהמהומה של השינויים הנ\"ל. בכל זאת זהו שינוי משמעותי וחשוב.
השינוי נוגע באזור ה Ajax ומאפשר גמישות שלא הייתה קיימת קודם לכן.
בואו נדבר עליו.

שייך לסדרה מבוא מואץ ל JavaScript ו jQuery


שינוי בפקודת ה Ajax
למי שלא מכיר, jQuery עוטף את ה XMLHttpRequest (או בקיצור XHR) של הדפדפן בצורה נוחה ומאפשר לקרוא בפשטות ajax.$ על מנת לבצע קריאת ajax. יש גם גרסאות מקוצרות בשם get.$ ו post.$.

עד גרסה 1.4 הדרך לבצע קריאת ajax הייתה כזו:
$.get(\'http://server.com/myurl\', {
  success: onSuccess, // a callback function to be triggered on success
  failure: onFailure, // a callback function to be triggered on failure
  always: onAlways // a callback function that is triggered on either failure of success. Like \"finally\".
});
success, failure ו always הם callbacks שנקראים כאשר הקריאה מסתיימת, הצלחה או כישלון.

מגרסה 1.5 אפשר לכתוב את הקוד בדרך הבאה:
var request = $.get(\'http://server.com/myurl\');
request.done(onSuccess);
request.fail(onFailure);
request.always(onAlways);
כלומר, ניתן לבחור אלו פונקציות (callback) יקראו בעקבות התשובה לאחר שהקריאה התבצעה. אם התשובה כבר חזרה, הקריאה (done, למשל) תפעיל את פונקציית ה callback מיידית.

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

Making Promises
אפתח בכמה מילים גדולות: מה ש jQuery עשו, הוא לממש Design Pattern (יו… וואהו!) שידוע בשמות הבאים: Deferred Object או Promise או Future. יש אפילו הגדרה פורמלית בשם Promises/A שמגדירה כיצד תבנית זו אמורה להתנהג בג\'אווהסקריפט.
לאכזבתם של לא-מעטים, jQuery לא נצמדה ל\"הגדרה הפורמלית\" ומימשה וריאציה קצת שונה של תבנית העיצוב, מימוש שהושפע כנראה מספריית Dojo שסיפקה יכולת דומה. אם אתם אורתודוקסים ל Promises/A, תוכלו למצוא מימושים \"תקניים\" בספריות כגון Q.js או Async.js שניתן להשתמש שהם. לרובנו, כל מה ש jQuery יעשו – מהווה את התקן בפועל.

על מנת להבין את המשמעות של הפעולה של ה Promise, נפרק אותה (Deconstruct) למרכיבים יותר בסיסים ונבחן אותם. הביטו על קוד ה JavaScript/jQuery הבא:

var deferred = new $.Deferred();
deferred.done(function(){ console.log(\'done\'); }); 
deferred.fail(function(){ console.log(\'fail\'); }); 

deferred.done(function(){ console.log(\'donedone\'); }); 

האובייקט Deferred הוא לב העניין, אם כי אינו קשור ל ajax במאומה. הוא מעין handle-עתידי.
בעת קריאה ל ()deferred.resolve – יופעלו הפונקציות (ניתן לרשום מספר לא מוגבל של פונקציות, לכל אחד מהאירועים) שנרשמו ל done. במקרה זה – כתיבה של done ו donedone ללוג.
בעת קריאה ל ()deferred.reject – יופעלו הפונקציות שנרשמו ל fail. במקרה זה כתיבת fail ללוג.

ההתנהגות העצלה (deferred) נובעת, כפי שאתם בוודאי מבינים, מעצם רישום והפעלת פונקציות בג\'אווהסקריפט. האובייקט Deferred הוא פשוט למדי ואינו כולל התנהגויות אסינכרוניות מיוחדות.

ה Promise הוא אובייקט (יחידון = singleton) שחוזר מקריאה ל ()deferred.promise וכל מטרתו היא לאפשר גישה מוגבלת: לאפשר לרשום פונקציות ל done, fail ו always – אבל לא לבצע triggering ל reject או resolve.

Read/Write Pattern – שליטה ב\"הרשאות\" ע\"י הפרדת הפעולות ל interfaces שונים. אפשר להחליף את user ב promise ואת userMaint ב Deferred על מנת לראות את תבנית העיצוב בהקשר לדיון שלנו.

הערה: ל reject או resolve ניתן לקרוא פעם אחת בלבד בחיי אובייקט ה Deferred. מאותו הרגע, כל פונקציית callback שתנסה להירשם – תופעל במקום.

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

Concurrent Pattern: Monitor
השימוש ב Deferred יכול לסייע לנו לבנות מבנה של concurrent programming בשם בקר (monitor). בקר הוא כלי לטיפול באסינכרוניות, הגבוה ברמת ההפשטה שלו מ mutex או semaphore, אך נמוך מ \"synchronized\" של ג\'אווה / #C. אנו עשויים למצוא אותו דיי שימושי בשפת ג\'אווהסקריפט / שימושי ב ajax בהם אסינכרוניות היא נפוצה [ב].

Monitor – מחכה שמספר פעולות אסינכרוניות יגיעו למצב מסוים, ואז יוזם פעולה. סוג של \"יישור קו\".

נאמר שאני מחכה בג\'אוובסקריפט ל2 פעולות שיסתיימו: טעינה של נתונים מהשרת ואנימציית מעבר. רק ש2 אלו יסתיימו אני רוצה להציג dialog עם שאלה למשתמש.
הדרך הנאיבית לעשות זאת היא ש callback א\' ישנה ערך גלובאלי כלשהו, ו callback ב\' ימתין עד שהערך הגלובאלי השתנה. כאשר callback ב\' מצא את הערך לו הוא ציפה – אני יודע ש2 הפעולות הסתיימו.
הבעיה?
הקוד קשה לקריאה: יהיה צורך לעקוב אחרי הלוגיקה של ה callbacks להבין מה הם עושים.
קרוב לוודאי שייוצר קוד מסורבל ומאוד לא נוח לתחזוקה.
הקוד לא יעיל מבחינת ביצועים.

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

var promiseA = $.get(…);
var deferredAnimation = $.Deferred(); // call  deferredAnimation.resolve() when it is done
var promiseB = deferredAnimation.promise();

$.when(promiseA, promiseB).

done(function(promiseAargs, promiseBargs) {
   … // what happens when both are done 
});

תענוג!

מודולריות
השימוש העיקרי, והנפוץ יותר ל Promises הוא עבור callbacks פשוטים.
כפי שציינו קודם לכן, הקוד

$.get(\'http://server.com/myurl\', {
  success: onSuccess, // a callback function to be triggered on success
  failure: onFailure, // a callback function to be triggered on failure
  always: onAlways // a callback function that is triggered on either failure of success. Like \"finally\".
});

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

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

  • ה onSuccess callback יפנה לאזורים שונים במערכת ויבצע שינויים. למשל: העלמת שעון חול, הפיכת כפתור ל enabled, הוספת פריט לרשימה. קרוב לוודאי שפעולות אלו ישברו את ההכמסה של המודולים במערכת.
  • אם ישנה מודעות מספקת להכמסה, קרוב לוודאי שכל אובייקט יכלול פונקציית callback שיודעת להגיב לאירוע ולשנות את הערכים המקומיים למודול. אבל… איך מעבירים את ה callback למקום בה נעשית הצורה האסינכרונית? לעיתים זה יהיה פשוט כי הגיוני שתהיה תלות בין המודולים – ולעיתים קרובות נאלץ להעביר את ה callback בין אובייקטים רבים ולהוסיף אולי תלויות על מנת לבצע את הקישור. מי שראה קוד שמעביר מספר callbacks בין אובייקטים, על מנת לטפל באירועים שונים או פשוט callbacks ממקורות שונים, יודע עד כמה הקוד עלול להיות מסורבל ו\"ללכלך\" את המערכת. התועלת בהכמסה עלולה לפחות – אם המחיר הוא קוד סבוך כל כך! 
פתרון שבמקרים רבים יהיה אלגנטי למדי הוא שימוש ב promise בעת יצירת הקריאה האסינכרונית ותעבירו את ה promise לאובייקטים אחרים. אובייקטים אלו יוכלו להירשם לאירועים שמעניינים אותם, אבל מעבר לזה – להעביר את ה promise הלאה לאובייקטים אחרים על שמודול המקור לא מכיר. מה שנקרא \"הכמסה\". – כך שאובייקטים אחרים שהם מכירים יירשמו עליו.

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

סיכום 
Promise הוא לא Silver Bullet, אבל במקרים רבים הוא יכול להיות כלי רב-עצמה שיתיר סיבוכיות רבה מהמערכת. היכולת לנתק את השליטה (קריאות resolve ו reject של Deferred) מצד אחד ואת התגובה לסיום הפעולה (done, fail ו always של ה promise) מצד שני מאפשרות לשמור על מודולריות גבוהה של המערכת במחיר נמוך.

אם אתם מוצאים את תבנית העיצוב של Deferred ו Promise שימושית, יש עוד פונקציות שניתן לחקור:
Deferred ו Promise יכולים לתקשר בניהם גם אודות progress (שלא כיסיתי בפוסט). לפעולת when יש פעולה \"אחות\" בשם then. חשובה אולי מכולן היא פעולת ה pipe שמתירה לשרשר הצלחות / כישלונות של promise וכך לחסוך קוד.

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

[א] בגרסה 1.6 שונו שמות הפונקציות – אני משתמש בשמות החדשים. בגרסה 1.8 השמות הישנים (complete, success ו error) יוכרזו כמיושנים (deprecated) ולכן אני נמנע משימוש בהם.

[ב] בג\'אווהקריפט בדפדפן יש כמובן thread יחיד ואין מקביליות חישוב. מצד שני יש הרבה פעולות IO אסינכרוניות שקוראות במקביל ויש אתגר תכנותי לשלוט בהן.