יום שבת, 13 ביולי 2013

Most important Design Patterns

לאחרונה יצא לי לרענון את הראש עם כמה מה-Design patterns היותר נפוצים.
אני אציג את הרשימה של ה Design pattens שלדעתי כדאי להכיר, וכמובן להשתמש בעת הצורך:
  1. Singleton- אני חושב שאין מתכנת שלא יצא לו להשתמש לפחות פעם אחת בתבנית העיצוב הנ"ל. נשתמש כאשר אנחנו צריכים לייצר רק instance אחד של Class במערכת. יש שני מימושים ליצירת Singleton. האחד יצירה סטטית של Singleton- המימוש המועדף, אך החסרון בו זה שאין שליטה על תזמון יצירת האובייקט. ויש את האפשרות השניה- יצירת Singleton הקלאסי- כאשר המתכמת מחליט מתי האובייקט נוצר, אך במימוש זה יש להיזהר מבעיית ה multy-thrading. לעוד פרטים

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

  3. Factory - יצירה של "בית חרושת" שיוצר עבורנו את האובייקטים. התוכנה שלנו מאוד דינמית. וייתכן שהיום אנחנו נרצה להשתמש עם אובייקט מסוג אחד, ולאחר מכן נשנה לו את המימוש/ או נרצה להשתמש עם אובייקט דומה. במצב כזה נצטרך לחפש את כל המקומות בקוד שיצרנו את האובייקט (new SomeObject). תחשבו את הבעייתיות שנוצרת אם יש לנו אפליקציה עם מספר רב של שורות קוד. כאשר אנחנו משתמשים ב Factory, אנחנו נשנה את מימוש יצירת האובייקט רק במקום אחד בקוד. לעוד פרטים. למימוש Factory באמצעות מספר דרכים

  4. Builder- מחלקה שיוצרת עבור המשתמש את האובייקט (בד"כ מורכב). ההבדל מתבנית Factory זה שב Factory יוצר את האובייקט בקריאה יחידה (מעין מעטפת לבנאי), בעוד ה Builder יודע ליצור את האובייקט עם כל הפרמטרים האפשריים. לדוגמא: אם המשתמש רוצה מכונית חדשה, ה Factory יחזיר "הונדה" או "טויוטה". אך ה Builder יחזיר "הונדה קבריולט עם מנוע 6 צילינדרים" או "טויוטה 4 דלתות ו ארבע צילינדרים". לעוד פרטים בפוסט הנפרד שלי על ה Builder design pattern

  5. Observer- כאשר אנחנו רוצים לקבל נוטיפיקציה כאשר אובייקט כלשהו משנה את המצב שלו. יש כאן יכולת לבצע קשר של יחיד- רבים, כאשר מספר רב של אובייקטים יכולים "להרשם" לשינוי באובייקט היחיד. יש דוגמאות רבות: למשל אפליקצייה בורסאית שרשומה לאובייקט מנייה, וכל שינוי במחיר המניה שולח נוטיפיקציה לכל האפליקציות שרשומות אליה.  עוד דוגמא זה כאשר קוראים נרשמים לקבל נוטיפקציה מבלוג מסויים, וכאשר מתווספת רשומה חדשה לבלוג, כולם מקבלים נוטיפיקציה שהתווספה רשומה חדשה. צריך לשים לב לאופן המימוש: יש את האפשרות הקלאסית של מימוש באמצעות Collection שנמצא באובייקט שנרשמים אליו, ויש את האפשרות שה CLR מאפשר לנו- על ידי שימוש ב event ו delegate. לעוד פרטים

  6. Template method- כאשר יש לנו אלגוריתם שמבצע מספר פעולות. כאשר אנחנו נרצה שפעולה מסויימת (או יותר) יהיו שונות ממימוש למימוש, אנחנו נשתמש ב pattern הזה. להבדיל מ strategy, לא כל האלגוריתם שונה אלא פעולה אחת בו שונה. למשל אלגוריתם שמבצע כמה פעולות, ובסוף שומר את התוצאה. שמירת התוצאה יכולה להיות לקובץ pdf, text או אחר. שמירת התוצאה תהיה שונה ממימוש למימוש, ומי שיחליט על אופן שמירת התוצאה יהיה מי שקורא לאלגוריתם.. לעוד פרטים

  7. Iterator- כאשר אנחנו רוצים לעבור על collection מסויים (למשל לולאת foreach), אנחנו חייבים לממש את ה pattern. למזלנו כל ה collections ב .NET   מממשים אותו. אם יש לנו אובייקט מורכב, שאנחנו רוצים להחליט איך ה iterator יעבוד, אז אנחנו צריכים שהאובייקט יממש את IEnumerable. לעוד פרטים

  8. Adapter- מתאים לעבודה בעיקר במערכות "מדף" שאנחנו לא רוצים לפתוח את הקוד ולשנות את המימוש. נעבוד כאשר נרצה שאובייקט אחד יעבוד מול אובייקט שני, ללא שינוי בקוד של אף אחד מהאובייקטים. ניצור מחלקת Adapter שיורשת מאובייקט אחד, ומממשת interface של אובייקט אחר, וכך יכולה "לחבר" בין שני האובייקטים. לעוד פרטים

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

  10. Dynamic Proxy- כמו הסעיף הקודם, אך באופן דינמי ע"י חיפוש Attribute מתאים.  פירטתי בפוסט הבא: Dynamic Proxy

  11. Decorator- כאשר נרצה להוסיף פונקציונאליות, או מצב חדש לאובייקט קיים, בלי לשנות את הקוד של האובייקט. למשל יש לנו אובייקט עוגה, ובעתי כנראה שנרצה להוסיף לעוגה קצפת, דובדבנים, ריח... אם לא היינו משתמשים בתבנית העיצוב, כנראה שהיה לנו אובייקט מסוג "עוגת דובדבנים", "עוגת קצפת", "עוגת קצפת עם דובדבנים", ותחשבו עוד כמה אופציות...  זהו pattern מאוד נפוץ ושימושי. לעוד פרטים
תהנו!

יום שישי, 12 ביולי 2013

Dynamic Proxy - Design Pattern

האמת, זה אחד ה Pattern שיותר אהבתי.
אז מה זה? כאשר אנחנו כותבים רכיבים תשתיתיים, ה Pattern עוזר לנו, במתן תשתית נוחה למשתמשים לשימוש בקוד שכתבנו כ Proxy לפעולות שונות בדרך מאוד נוחה.
למשל- אנחנו רוצים שכל מי שרוצה שהפונקציה שלו תהיה טרנזקציונית- לא יצטרך לממש את הטרנזקציה, אלא יוסיף מעל הפונקציה שלו Custom Attribute שאנחנו כתבנו (למשל [Transacion]). אפשר לחשוב על עוד מיליון דברים שאנחנו יכולים לממש בתשתית שלנו. למשל כתיבה ללוגים, הצגת משך זמן ביצוע הפונקציה, או כל דבר אחר.
אנחנו כמפתחי תשתיות צריכים לדאוג למשתמשים שלנו, שלא יצטרכו לממש הכל. אנחנו נספק להם תשתית נוחה.

אז איך זה עובד:

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

 [System.AttributeUsage(System.AttributeTargets.Class
                  | System.AttributeTargets.Struct | System.AttributeTargets.Method)]
        public class Benchmarks : Attribute { }


נכתוב את ה Class הדינמי הבא, שמשתמש ב Reflection כדי למצוא את סוג האובייקט שהפעיל את הפונקציה, הוא מפעיל את הפונקציונאליות שקבענו (על פי ה Attribute), ואז עושה Invoke למתודה המקורית שהמשתמש הפעיל.

          public class Wrapper<T> : DynamicObject
        {
            private readonly T _wrappedObject;

            public Wrapper(T obj)
            {
                this._wrappedObject = obj;
            }

            public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
            {           
                //get the method that invoked
                MethodInfo method = _wrappedObject.GetType().GetMethod(binder.Name);

                //create a new instance of the object that invoked
                object o = Activator.CreateInstance(_wrappedObject.GetType());

                // Check if the method has the benchmarks attribute
                if (method.GetCustomAttributes(typeof(Benchmarks)).Count() > 0)
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();

                    result = _wrappedObject.GetType().InvokeMember(binder.Name, System.Reflection.BindingFlags.InvokeMethod, null, o, args);
                  
                    sw.Stop();

                    Console.WriteLine(binder.Name + ": " + sw.ElapsedMilliseconds + "ms");
                }
                else
                {
                    result = _wrappedObject.GetType().InvokeMember(binder.Name, System.Reflection.BindingFlags.InvokeMethod, null, o, args);
                }


                return true;
            }
        }

ניצור Factory שיוצר למשתמש את האובייקט.

   // Factory.cs
        class Factory
        {
            public static Factory Instance = new Factory();

            private Factory() { }

            public dynamic New<T>(params object[] args) where T : class
            {
                T obj = (T)Activator.CreateInstance(typeof(T), args);
                return new Wrapper<T>(obj);
            }
        }

זהו האובייקט של המשתמש

        class MyClass
        {
            [Benchmarks]
            public void doSomething()
            {
                System.Console.WriteLine("My .NET Diary Blog");
            }

            [Benchmarks]
            public void doSomething2(string text)
            {
                System.Console.WriteLine(text);
            }
        }

וככה המשתמש יפעיל את הפונקציה:

      static void Main(string[] args)
        {
            var myClass = Factory.Instance.New();
            myClass.doSomething();
            myClass.doSomething2("By Eyal Chapnik");
        }
והפלט:


שבוע טוב!