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

תוכניתנים רבים שנתקלים בקוד שכתבתי שואלים את עצמם (ולבסוף גם אותי) מהו התפקיד של משתנה בשם Pos אשר מופיע כמעט תמיד בקוד שלי ובמיוחד במקרים של קוד מורכב במיוחד. ובכן, בוודאי קרה לך לא פעם שהתוכנית שלך הציגה הודעת שגיאה בנוסח "Object refrerence not set to an instance of an object", לחצת על אישור והתוכנית שלך נעלמה בענן עשן… ואתה נשארת עם שאלות ותהיות מהיכן הופיעה הודעת השגיאה הזו, בדיוק עבור מקרים כאלו החלטתי להציב את המשתנה Pos.

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

//Loading client information
….
//Analyzing client birth dates
//Building list of birthday boys in this classroom
במקום זאת אני מעדיף להשתמש במשתנה Pos באופן הבא:
Pos = "Loading client information";
Pos = "Analyzing client birth dates";
Pos = "Building list of birthday boys in this classroom";
היתרון המשמעותי ביותר באופן עבודה כזה הוא בכך שבמקרה של שגיאה (Exception) ניתן להציג את המשתנה הזה בתוכן הודעת השגיאה ובכך לאפשר לתומך הרלוונטי לקבל מושג הרבה יותר ברור לגבי הגורם לשגיאה (בתנאי שכתבת באופן ברור ומדוייק את הערכים אל משתנה ה-Pos) בנוסף, גם במקרה שהתומך נדרש לערב את התוכניתן בכדי לפתור את הבעיה, עדיין יהיה לתוכניתן מידע הרבה יותר מדוייק לגבי מיקום התקלה, דבר שיאפשר לו לתת תשובה הרבה יותר מדוייקת וממוקדת. כמובן שאפשרי וגם כדאי לכתוב את תוכנו של משתנה זה ל-Trace (כפי שהסברתי בפוסט "Tracing – מה זה ובשביל מה זה טוב?") על מנת לאפשר למערכת לפרט בדיוק אילו שלבים לוגיים היא עברה. להלן דוגמה מפורטת יותר:
string Pos = "UNSTARTED";
try
{
Pos = "Loading client information";
Trace.WriteLine("[" + DateTime.Now.ToString("s") + "] " + Pos);
Pos = "Analyzing client birth dates";
Trace.WriteLine("[" + DateTime.Now.ToString("s") + "] " + Pos);
Pos = "Building list of birthday boys in this classroom";
Trace.WriteLine("[" + DateTime.Now.ToString("s") + "] " + Pos);
}
catch(Exception ex)
{
Trace.WriteLine("[" + DateTime.Now.ToString("s") + "] " + "The following unexpected exception occurred: " + ex.GetType().ToString() + ", Message: " + ex.Message + "\nStack Trace:\n" + ex.StackTrace + "\n\nPos: " + Pos);
throw new Exception("The following unexpected exception occurred: " + ex.GetType().ToString() + ", Message: " + ex.Message + "\nStack Trace:\n" + ex.StackTrace + "\n\nPos: " + Pos);
}
עם זאת, במידה ואתה מעוניין ליישם שימוש דומה, חשוב לשים לב לנושא בעייתי: במידה ואתה נאלץ להגדיר את ערכו של המשתנה Pos כפונקציה של ערך של משתנה אחר, למשל כך:
Pos = "The GetBirthdayBoys() method returned: " + Result;
באופן כזה קיים סיכון ממשי שהערך שחזר הוא Null, דבר שיגרום לתכנית שלך לקרוס רק בגלל שניסית למנוע את הקריסה שלה… ישנן כמובן מספר דרכים לפתור זאת, קיימת כמובן אפשרות להשתמש בתנאי Null המקוצר כך:
Pos = "The GetBirthdayBoys() method returned: " + Result ?? "NULL!!";
או בתנאי המקוצר, כך:
Pos = "The GetBirthdayBoys() method returned: " + ((Result == null) ? "NULL!!" : Result.ToString());
היות ונתקלתי פעמים רבות במקרים בהם הייתי צריך לשנות את העיצוב של הטקסט המוצג, או לפרמט אותו להצגה נוחה לקריאה (כמו במקרה של Collections שונים שנצרכתי למשל) העדפתי בד"כ לבנות מתודה (בדרך כלל בשם IsNull) שמקבלת אובייקט (Object) ו-Enumerator שמגדיר לה כיצד לתפקד ומחזירה String, המתודה הזו מתרגמת כל אובייקט למחרוזת לפי כל מיני קריטריונים ואופנים, למשל, אפשר להחליט שאם שלחו DataSet וגם רמת ה-Tracing (כפי שנקבע ע"י ה-Trace Switch הרלוונטי) היא Verbose המתודה תחזיר את תוכנו של ה-DataSet אבל אם ה-TracingLevel הוא Info המתודה תחזיר את כמות הטבלאות ואת כמות השורות והטורים בכל טבלה, כל זאת בנוסף לכך שהמתודה הזו תחזיר "Null!!" כאשר הערך שנשלח אליה הוא null.
לסיכום, מאמר זה מהווה דוגמא נוספת לכך שפיתוח נכון תוך מתן דגש על היבטי התחזוקה העתידית עשויים לחסוך באופן משמעותי במשך הזמן הדרוש לטיפול בתקלות במערכת ובכך בעצם להשיג חיסכון משמעותי ביותר שכן, כידוע, למעלה מ-90% מהעלויות של מוצרי תוכנה מקורן בעלויות תחזוקת התוכנה במהלך תקופת השימוש בה.

השאר תגובה