תבנית עיצוב היא כלי מדהים. ניתן לכתוב להתאים או לשנות אותה כמה שבא לנו. ממש כמו פלסטלינה שניתן לשחק איתה.
אז לפני שנתחיל עם הבעיות - מה הן תבניות עיצוב?
תבנית עיצוב או באנגלית - Design Pattern היא פתרון לבעיה מוכרת המאפיין איך בדיוק הפתרון הזה ממוממש.
תבנית עיצוב מאוד פופלרית היא התבנית Strategy.
בהינתן אלגוריתם כלשהו, אנחנו יכולים להזריק אלגוריתם אחר כדי לשנות את ההתנהגות.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classAction: def__init__(self, operator): self.Operator = operator defCalculate(self, a, b): returnself.Operator(a,b)
classCalculator: def__init__(self): self.FancyCalculate = Action(lambda a,b: a + b + 5)
c = Calculator() c.FancyCalculate(1,2) c.FancyCalculate = Action(lambda a,b: a + b + 10) c.FancyCalculate(1,2)
תבניות עיצוב הן חלק אינטגרלי מהעבודה שלנו כי אנו לומדים אותן בשלבים הראשונים ונוטים לראות אותם כמעט בכל מקום: Factory, Visitor, StateMachine, Builder וכדו’…
לפיכך זה כלי יעיל וחזק לשימוש חוזר בקוד במיוחד למפתחי ספריות. אך עם גודל האחריות כך גם גודל הכישלון - שימוש בתבנית אוטומטית הופך את הקוד שלנו למורכב יותר.
איזה בעיות מורכבות יכולות להיווצר ואילו טעויות קריטיות אסור לנו לעשות - בואו נצלול.
טעות 1 - להשתמש בתבנית בשביל להשתמש בתבנית
הטעות
להשתמש בתבנית רק בשביל להשתמש בתבנית כלשהי.
דוגמא
לעיתים בעיצוב נרצה להגביל רק למופע אחד בלבד, ולכן נלך ונממש תבנית עיצוב מסוג סינגלטון.
1 2 3 4 5 6 7 8 9 10
classSingleton: _instance = None
def__new__(cls): if cls._instance isNone: cls._instance = super(Singleton, cls).__new__(cls) return cls._instance
def__init__(self): self.value = None
הפתרון
העקרון התומך שעוזר לא להפר את הכלל הוא - KISS - Keep it simple stupid. קודם השתמשו בגרסה הפשוטה של הפתרון ורק אחר כך תחשבו אם אתם רוצים ליצור מורכבות.
בדוגמא הזו אנו יוצרים מופע, ונשתמש בו, במידה ונצטרך סינגלטון לאורך קוד גלובאלי אנחנו נשתמש בו או נחשוב מחדש אם יש בו צורך.
1 2 3 4 5
classMyClass: def__init(self): self.value = None
myInstance = MyClass()
טעות 2 - לממש תבניות בצורה הכי גנרית\כללית
הטעות
לעיתים נתקלתי בקוד שממש תבנית עיצוב בלי התנהגות דומיינית\עסקית בכלל!
דוגמא
מימוש State Machine כפתרון גנרי על מנת להוסיף ניהול מצבים. בדרך כלל נראה פתרון שדומה לזה:
לא ליפול לבור הגנריות - ממשו דברים ברמת אבסטרקציה נמוכה ותבדקו אם הקוד שלכם זה קוד תשתיתי הכרחי או קוד דומייני. חוץ מזה, פתרון ה-state machine בצורת OOP נראה כך:
אך שימו לב שזה נטו דוגמא, אם אתם באמת צריכים את ה-State pattern אז תשתמשו בו. אבל אני לא ממליץ על כזו מורכבות במיוחד בשלבי ההתחלה לעיצוב קוד.
טעות 3 - חוסר הבנה של הבעיה
הטעות
תבנית עיצוב באה לפתור בעיה שחוזרת על עצמה - למשל איך לממש סינגלטון כי זו בעיה פתורה. כאשר אנו לא מבינים היטב את הבעיה אנו נשתמש בפתרון שאיננו מתאים ולפי כך שימוש בתבנית עיצוב יכול לגרוע מאיתנו עוד יותר.
כשחשבנו על הפתרון להצגת הטמפרטורה הבנו שטמפרטורה יכולה להשתנות ולפיכך רצינו לקבל עדכונים על הטמפרטורות. אולם לא הבנו שיש רק צורך אחד לתת-מערכת הזו וזה פשוט להציג את הטמפרטורה הנוכחית.
הפתרון
דוגמת הבעיה גרמה לקוד הרבה יותר מסובך והוספת אלמנטים שיש לתחזק אותם ולבדוק שאין שם באגים. לכן במקרה הזה יש צורך בפישוט הפתרון והורדת תבנית העיצוב:
תוכלו גם לחלוק עלי שהפתרון הזה מהווה בעייה ביצועים כי כל מה שנרצה לקבל את הטמפרטורה אנו נצטרך לתשאל את התת-מערכת שוב. לא לדאוג כי גם לזה יש פתרונות בסגנון זיכרון מטמון זמני או עדכון פנימי מבלי להפיץ את זה החוצה ל-Display.
טעות 4 - להשתמש בתבנית במקום פתרון קיים
הטעות
נפוץ בעיקר אצל שפות עם אקו-סיסטם עשיר כמו C#. כאשר אנו רוצים ליצור תת-מערכת כלשהי אך במקום לחשוב אם זה כבר קיים אנו חושבים על איך לממש משהו משלנו בעזרת תבנית. חשוב להבין - תבנית עיצוב זה צריך להיות מקום 5 או 8 בקו המחשבה שלנו.
דוגמא
בתת-מערכת שחשבנו עליה אנו רוצים לדאוג שמכשירים ייקבלו את העדכונים הרלוונטיים. אז נממש איזה מיני מנגנון שמקבל עדכונים: