DCSIMG
Regions From Hell - Liran Chen's Blog

Liran Chen's Blog

.Net Internals, Debugging, Multithreading - and More!

Regions From Hell

אם יש דבר אחד שאני לא אוהב לראות בקוד, זה שימוש מוגזם ב-Region'ים.
הטיעון העיקרי של התומכים בשימוש באותם Region'ים הוא שאפשר להגיע בעזרתם לקוד הרבה יותר "נקי", "מסודר", או חס וחלילה, "קל לתחזוקה". תלוי ביום, ומצב הרוח שלי באותו רגע, אני אוטומטית משיב: הפוך גוטה, הפוך.
הדבר היחיד ש-Region'ים יודעים לעשות זה להחביא קוד. מה שלעצמו מרגיש די אבסורדי מאחר ורובנו בדרך כלל נמצאים במירוץ לא נגמר אחרי מסך גדול יותר, רזולוציה מטורפת יותר, פונט קטן יותר - העיקר להכניס כמה שיותר קוד למסך בודד. ובכל זאת, ברגע שאנחנו מתחילים לתחום אזורים בקוד עם Region, אנחנו למעשה הופכים את הגלגל לאחור, וגורמים לנו להחשף לפחות ופחות שורות קוד.
כשמפתח ניגש בפעם הראשונה לקוד שהוא לא מכיר, הדבר הראשון שעומד בראש מעיניו הוא לענות על השאלה (הכביכול פשוטה)  "מה _____ הולך כאן?!" (הכנס קללה מועדפת). הדרך הזריזה ביותר לענות על השאלה הזאת היא להקליק על Ctrl+M+O ולגרום לכל הקוד שבקובץ להתכנס להצהרות הפונקציות בלבד. לאחר הצעד הזה, אפשר לסרוק תחילה אחרי פונקציות הנחשפות כ-Public, ומשם כבר להתחיל להבין איך ה-Execution Flow עובר דרך המחלקה, והפונקציות השונות. ובכלל, להבין איזו לוגיקה היא מכילה.
אולם, ברגע שאנחנו מחליטים להשתמש ב-Regions, אנחנו מאבדים את כל הנוחות הזאת, והופכים את עבודת התחזוקה למייגת וארוכה עוד יותר.
נקח את הדוגמה הקלאסית הבאה:



עכשיו, תחשבו שאתם מגיעים בפעם הראשונה לטיפוס המאוד-מאוד מסובך הזה, ומנסים להבין מה בכלל קורה שם. אבל, ברגע שאתם מנסים לראות איזשהו תמונה כללית על הקוד, אתם נתקלים בקיר הבטון הזה שהשימוש ב-Regions גורם לו. במבט ראשון יכול אנחנו יכולים לקבל את הרושם שסך הכל מדובר במחלקה פשוטה למדי, בלי יותר מדי שורות קוד. אבל כל זה הוא לא יותר מאחיזת שווא, מאחר ויתכן וכל אותם Region'ים שאנחנו רואים, יכולים למעשה להסתיר מאות, אם לא אלפי שורות קוד שמתחבאות מתחת לעטיפה היפה הזאת.
בדוגמה הזאת יש 2 בעיות עיקריות. הראשונה, היא הניסיון לתחום אזורים בקוד על פי ה-Access Modifier שלהם. לגבי זה, חשוב לזכור שנורא, אבל נורא קשה לתחזק חיה שכזאת. זאת אומרת, את ה-Region עצמם. רק תחשבו שאתם מוסיפים עכשיו פונקציה פרטית, או אולי איזו Utility מסכנה לקוד. מי שבאמת חושב שהוא יזכור תמיד, אבל תמיד, להכניס את אותה פונקציה לבלוק ה-Region המתאים ביותר .. שיחשוב שנית. זה פשוט בלתי אפשרי, ואי אפשר לצפות מאף אחד לתחזק דבר כזה ב-100% מהזמן. והנה, ברגע ש"התפספס" לנו משהו קטן כזה, הלך לנו כל הסדר. עכשיו מבחינתנו אותה פונקציית Utility כבר לא קיימת. הרי כשנרצה לבחון את אוסף הפונקציות הפרטיות, קיימת סבירות לא רעה בכלל שלא נמצא אותה תחת ה-Region שמתאים לה. ואם אנחנו לא יכולים למצוא אותה שם, מה זה בכלל עוזר לנו זה שאנחנו משתמשים ב-Regions מלכתחילה? ברגע אחד של חוסר תשומת לב איבדנו את כל ה"יתרון".
דבר שני שאפשר לשים לב אליו כאן, הוא Region'ים שתוחמים מימושים של ממשקים. כשבנאדם בא לממש ממשק באופן ידני, סביר להניח שהוא לא יחליט על דעת עצמו להוסיף Region כזה עבור כל ממשק שהוא מחליט להוסיף. אבל, בדיוק במקום הזה Visual Studio מחליט "להגדיל ראש" ולהוסיף אותו עבורנו, ברגע שאנחנו נעזרים ביכולת המימוש האוטומטי שלו. למזלנו, ניתן לבטל את התנהגות ברירת המחדל הלא-מועילה-בעליל הזאת, ולקבוע שהוא לא ידחוף את האף שלו במקרים האלה.
כל מה שצריך לעשות, זה לגשת לחלון ה-Options ומשם דרך התפריט: Text Editor->C#->Advanced, ולהוריד את הסימון מהתיבה "Surround generated code with #region".

 
ואחרי כל זה...
אני אגיד את הדבר הבא: Region'ים הם לא בהכרח רעים, אם משתמשים בהם נכון.
ככלל, כדאי להטיל ספק ברגע ששומעים מישהו אומר "_אף פעם_ אל תעשו X. במקום זה, _תמיד_ תעשו Y". כי כמו בכל דבר אחר בחיים, תמיד יש מקרים "יוצאים מן הכלל", ותמיד בכל דבר שנראה רע .. מסתתר קצת טוב.
זה נכון גם לגבי Region'ים. לפי דעתי, וטעמי האישי, שימוש ב-Region'ים יכול להביא תועלת כאשר באמת מה שאנחנו רוצים לעשות זה להחביא קוד. כלומר אנחנו הופכים את החסרון ליתרון שימושי. המקרים היחידים בהם אני רואה לנכון להשתמש ב-Region'ים הם באותם מקרים בודדים, בהם אנחנו באמת פשוט לא רוצים בכלל לראות קוד/מימוש. כשמדובר בקוד סתמי לגמרי, שאפילו מעצם זה שקראנו את הטקסט שמתאר את ה-Region, אנחנו יכולים להבין בדיוק מה הקוד המוסתר עושה - ולאחר מכן להתפנות להמשיך הלאה בסקירה. דוגמה למקרה כזה הוא מימוש פשטני של אופרטורים. אם למשל אנחנו מחליטים להגדיר טיפוס חדש בשם BigInteger, ואנחנו רוצים להוסיף לו כל אופרטור אפשרי של השוואה/חיבור/חיסור וכו'... אז באמת שאין צורך להעמיס על העיניים עם המספר הלא מבוטל הזה של פונקציות שחוזרות על עצמן פעם אחר פעם (כאמור, זאת בהנחה שאין בהן שום לוגיקה "חכמה" מעבר לכל מה שברור מאליו). להבדיל ממקרים אחרים, עטיפה של כל אזור הקוד שמגדיר את אותם אופרטורים ב-Region בודד - לא יגרום לקריסה טוטאלית של היקום בו אנחנו חיים. ועם זאת, מילת המפתח ומוסר ההשכל הכי חשוב שאפשר לקחת מהפוסט הזה, הוא "בזהירות!".

תוכן התגובה

Shlomo כתב/ה:

אני לא כל כך מסכים איתך, אני משתמש בהרבה region ודואג שכל הצוות שלי יכתוב את ה - region בצורה מסודרת.

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

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

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

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

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

# August 27, 2009 7:15 AM

Liran Chen כתב/ה:

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

עם זאת, אני מסבין שיש כאן עניין של טעם אישי .. רק שאני לא מבין את האנשים שזה הטעם שלהם..

# August 27, 2009 8:29 PM

Shlomo כתב/ה:

אז אנחנו שווים, כי אני לא מבין את האנשים שהטעם שלהם שונה :)

# August 27, 2009 8:46 PM

אשר כתב/ה:

שלומו,

אתה מזכיר לי בדיחה: איש אחד הולך לרופא ואומר לו:

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

אומר לו הדורקטור, אז אל תשים את האצבע שם".

מוסר ההשכל:

אל תכתוב class-ים עם 5000 שורות....

# August 27, 2009 9:52 PM

Shlomo כתב/ה:

זה רק בשביל הדוגמא,

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

# August 27, 2009 10:42 PM

עומר כתב/ה:

"...והופכים את עבודת התחזוקה למייגת וארוכה עוד יותר".

למה הכוונה ארוכה עוד יותר?

מייגעת - אני יכול להבין למה אתה חושב ככה... אבל אין ספק שהסיבה העיקרית לבעיה היא ש*אין טיפול אוטומטי* של Visual Studio בהכנסה ל-Regionים סטנדרטיים כמו בדוגמא - אין שום סיבה שה-IDE שלי לא ידע שפונקציה שמתחילה במילה השמורה private הולכת לRegion המתאים, משתנים פרטיים של המחלקה מתחילים במילה private (או בלעדיה) ו"הולכים" ל-region המתאים וכן הלאה.

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

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

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

כמובן שהשימוש בכל המקרים הללו צריך להיות זהיר אבל בהחלט צריך להיות.

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

# August 28, 2009 12:51 AM

Liran Chen כתב/ה:

עומר,

הכנסה אוטומטית ל-Region'ים על פי כל מיני פרמטרים כגון ה-Access Modifier הוא לא בהכרח טוב. באופן אישי, אני מרגיש שיש מצבים בהם נוח יותר לסדר פונקציות על פי הקשר שלהן אחת לשניה. כלומר אם פונקציה X (פומבית) קוראת לפונקציה Y (פרטית), אז יתכן ויהיה נוח יותר להצהיר עליהן באותו הסדר. כך שכשאתה מסתכל על איזשהו Flow בקוד, אתה למעשה רואה הכל "במכה אחת", במקום להתחיל לקפוץ במקומות שונים בקוד. בכל אופן, ההעדפה היא לבחון כל מקרה לגופו.

כשזה מגיע לחיפוש פונקציה ספציפית בקוד, הדבר הכי פחות פרודוקטיבי שאפשר לעשות זה להתחיל לשחק ולנווט עם העכבר על כל מיני Region'ים או אולי אותו DropDown שציינת. במקום זה, אני באופן מוחלט נוהג להשתמש ב-Incremental Search, שיצא לי לדבר עליו בפוסט הזה: blogs.microsoft.co.il/.../visual-studio-using-the-incremental-search.aspx

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

לגבי 5,000 שורות הקוד .. נשמע כמו סממן רע של איזה God Object שהולך אצלך בקוד.. Anti-Pattern קלאסי.

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

# August 28, 2009 1:27 AM

Technicals and Technicalities כתב/ה:

Liran wrote a post ( Hebrew ) about the use of regions and how he hates it. Or them. I’m not sure. I

# August 31, 2009 11:30 AM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 3 and 6 and type the answer here:


Enter the numbers above: