anti patterns של מתודולוגיות עבודה

הפוסט המקורי לא הובן כפי שהתכוונתי אליו, ולכן שכתבתי את הפוסט לגרסה הזו.

הקדמה

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

הדגמות

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

כך שימוש בכפית הוא למעשה anti-pattern, היות והוא לא מוביל לאן שאנו רוצים להגיע בדרך הגיונית.

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

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

הדגמה ראשונה:

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

לעומת זאת יש שפות שבהן מראש זה לא קורה: בשפות כמו פסקל (לא, לא חזרתי אליה) ו go.

בשפות אלו, המעבר הוא חד פעמי בלבד, דבר שלא רק מוריד חלק מבעיות קימפול ש++C למשל חווה, אלא גם ניהול התלויות (שלא לדבר על מהירות קימפול גבוהה יותר), מאפשר להשיג המון כוח. למשל בפסקל וגם בgo נוצר בעצם סוג של namespace. היצירה של namespace מאוד חשובה היות וזו נרשמת פעם אחת בצורה בינארית, ופשוט יש מיפוי לכל המקומות אשר מבקשים להשתמש בקוד עצמו, וכך יש הצבעה לאיזור מוגדר לכל מקום שרוצה להשתמש בקוד שכזה, במקום לכלול אותו בכל פעם מחדש. ועכשיו אין צורך יותר בhack של ifndef ואז define רק בשביל לגרום לקומפיילר לא להוסיף שוב פעם את הקוד. כמו כן, היות וכבר הnamespace נכנס לאיזשהו עץ בקומפיילר, במקום לקרוא שוב את התוכן, פשוט מסמנים שגם בנקודה הזו שהצהרנו שוב על fmt (למשל) צריך להשתמש ב namespace הזה עם שם פונקציה מסויימת, ובכך יש symbol ייחודי עם namespace ושם הפונקציה שנכתבת רק פעם אחת.

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

בשפה PHP הצליחו ליצור עם include עוד סוג של anti-pattern בכך שהם הוסיפו את include_once. למעשה הם זרקו את האחריות על המתכנת, במקום על המפרש, ובכך יצרו מצב שיכול להכניס להרבה מאוד בעיות, בייחוד כאשר לא מבינים עד הסוף מתי יש להשתמש ב include ומתי להשתמש ב include_once.

הדגמה שנייה:

יש החושבים כי פירוש השימוש ב SQL הוא ניהול מידע בעל קשרי גומלין. כלומר, אפשר לנהל קשרי גומלין בין טבלאות שונות בצורה טובה ויעילה.
העניין הוא שזה לא כזה פשוט כמו שזה נשמע.
זו הסיבה ששאילתות join כל כך מסובכות, ויש במסדי נתונים רציניים, גם הרבה סוגים מהם, עם המון משמעויות שונות ומשונות (inner/outer left/right/natural וכיוב'). אם לא מודעים להבדלים הנכונים בין קשרי הגומלין לפי השאילתא (לא לפי כוונת מתכנן הטבלאות, אלא לפי פרשנות מסד הנתונים את השאילתא), המידע לא מגיע בצורה יעילה, אבל יותר חמור, לפעמים הסיבה שהמידע מגיע שגויה, או נראה כאילו אין מידע מתאים, או מגיע מידע, אבל מסיבות לא נכונות, ואז לא תמיד מגלים שזה בעצם שגוי.

זו הסיבה שיש מסדי נתונים אחרים אשר בנויים לגישה נוחה/מתאימה יותר של קשרי גומלין. לא בהכרח no-sql, אבל יכול להיות שכן (אבל זה לא כזה משנה). העניין הוא שהן יוצרות גישה מסוימת המתאימה יותר למה שחשוב לך לבצע.
כך למשל אם יש הרבה מידע היררכי, לפעמים מסד נתונים שהוא בנוי כעץ מתאים יותר.
אם מידע שצריך להישמר פעם אחת אבל אינו שייך לטבלה הראשית, בSQL יהיה צורך ב pivot table, אבל גם כאן, יש מסדי נתונים שבהם עדיין אפשר לשמור את זה כרשומה בודדת ולשנות את התפיסה הזו.

הדגמה שלישית:

לפני מספר שבועות קראתי פוסט מאוד מעניין על כך שבשנות ה70 יצאו 2 ניסויים בעולם התוכנה על הרעיון של איך לבנות עורך טקסט:

בעוד שהלימוד מemacs תפס, הלימוד של vi מעולם לא באמת נלמד כתצורת מימוש של עורכי טקסט.

ברעיון של emacs יש API אשר משמש לביצוע פעולות.
ברעיון של vi, יש הבדל בין תנועה, זיהוי גבולות ופעולות העורך.

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

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

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

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

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

אם נלחץ על dW, הוא יפעל אותו הדבר, אבל ימחק כל תו שהוא לא רווח שנמצא עד המילה הקרובה הבאה.

למחוק אנחנו יודעים כי זו פעולה המתחילה ב"d” (במקרה הזה). אם אנחנו רוצים להעתיק עכשיו מילים, נשתמש באות y (מציינת yank – למשוך). כלומר מה שהשתנה זה במקום שנלחץ על d נלחץ על y, הגבולות של מה להעתיק (במקרה הזה) נשארות לאחר ה "y” בצורה זהה לאותן הגדרות של פעולת המחיקה ועכשיו יצרנו תבנית (pattern) עבודה.

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

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

סיכום

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

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

וכפי שניתן להבין, ניתן למצוא אין סוף anti-patterns, וזיהוי שלהם עוזר לנו להתמקד בעיקר במקום בלמצוא איך להתמודד עם המצב.

כך שאם מתעכבים על התמודדות עם כלי/שפה/מימוש מסויים יותר מידי, סביר להניח כי יש שם anti-pattern, גם אם הכלי עצמו "מצויין".

7 מחשבות על “anti patterns של מתודולוגיות עבודה

  1. levdev

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

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

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

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

    לפעמים, הסיבה ש-"כולם"tm עושים משהו היא לא סתם הרגל, אלא תוצאה של התאמה טובה של המשהו הזה למשתמש…

    1. ik_5 מאת

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

  2. צפריר כהן

    ב־Emacs יש אפשרות להפעיל פקודה "מספר פעמים". האם קרה לכם שלחצתם ב־bash על כמה מקשים והגעתם ל־prompt המוזר: "(arg: 35)" (או כל מספר אחר)? זה אומר שהפעולה הבאה שתורץ תופעל עם הפרמטר המספרי 35, כלומר: כדרך כלל היא תורץ 35 פעמים. כדי לחזור פעמים על פעולה צריך להקיש לפניה M-‎ (לדוגמה: Esc ולאחר מכן המספר בספרות).

    זה עובד ב־bash (במצב Emacs, שהוא ברירת המחדל) וזה עובד גם ב־Emacs עצמו.

    כמוכן, יש ב־Emacs פקודות לא מעטות של מחיקת "אובייקט" כלשהו (מילה, שורה, ועוד) שבדרך כלל מכניסות אותו לחוצץ להדבקה, ממש כמו ב־VI. כל מי שעבד ב־pico/nano אמור להכיר אותן.

    לסיכום:
    אם אני רוצה למחוק עשר מילים אחורה ב־Emacs:‏ Esc 1 0 Ctrl-w
    אם אני רוצה למחוק אחורה 10 מילים ב־vi:‏ Esc 10 d 1 0 b

  3. צפריר כהן

    לפני הרבה שנים, כשלמדתי לתכנת באוניברסיטה, את הקורס במסדי נתונים למדנו על אורקל (שרץ על סולריס).

    אחד הדברים שאני זוכר משם הוא שיש לאורקל שאילתות SQL היררכיות:
    http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm
    מכיוון שלא ראיתי את זה אצל אף אחד אחר, אני מניח שזה לא כל כך מועיל (או מאוד לא נוח למימוש).

    1. ik_5 מאת

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

כתיבת תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s