Friday, December 19, 2008

Dynamic Javascript Function Invocation

Such dynamic JavaScript call may become handy in some scenario:
    function functionCall(data, option) {
        if (someCondition) {
            var dynamicFunc = function1;
            var args = [data, option, 1];
        } else {
            var dynamicFunc = function2;
            var args = [data, option, 2];
        }
        var value = dynamicFunc.apply(dynamicFunc, args);
        // processing value 
    }

Sunday, December 07, 2008

Design Patterns By Example: Strategy vs Template vs Builder vs Bridge

In this post I will go through a few related design patterns. Unlike many online design pattern tutorials, patterns concept and UML diagrams will not be our focus. Instead I will provide more understandable code examples for the discussion.

Strategy Pattern

Strategy defines an interface common to all supported algorithms or behavior. The implementation of the Strategy is dynamically invoked at run time. Generic list sorting in .NET is a good example of using strategy pattern:

using System;
using System.Collections.Generic;

class test {

    class Person
    {
        public string name { get; set; }
        public int Age { get; set; }
    }

    class PersonSortByAge : IComparer<Person>
    {
        public int Compare(Person p1, Person p2)
        {
            return p1.Age.CompareTo(p2.Age);
        }
    }

    static void Main(string[] args)
    {
        List<Person> people = new List<Person>();
        people.Add(new Person() { name = "Andy Wade", Age = 20 });
        people.Add(new Person() { name = "Cary Lee", Age = 10 });
        people.Add(new Person() { name = "Bob Ford", Age = 30 });

        Console.WriteLine("Original order:");
        people.ForEach(p => Console.WriteLine("Name:" + p.name + " Age:" + p.Age));

        people.Sort((p1, p2) => p1.name.CompareTo(p2.name));
        Console.WriteLine("Sort by name:");
        people.ForEach(p => Console.WriteLine("Name:" + p.name + " Age:" + p.Age));
        //Sort by name:
        //Name:Andy Wade Age:20
        //Name:Bob Ford Age:30
        //Name:Cary Lee Age:10

        people.Sort(new PersonSortByAge());
        Console.WriteLine("Sort by age:");
        people.ForEach(p => Console.WriteLine("Name:" + p.name + " Age:" + p.Age));
        //Sort by age:
        //Name:Cary Lee Age:10
        //Name:Andy Wade Age:20
        //Name:Bob Ford Age:30

        Console.ReadKey();
    }
}

In above example Lambda express and strongly typed IComparer are two different strategy or sorting algorithm, which are used to sort the people list by person's name and age respectively. Following code is another example:

using System;
using System.Collections.Generic;
using System.Linq;

class StrategyPattern
{
    enum Temperature { Cold, Mild, Hot }

    interface IActivity
    {
        string GetActivity(Temperature temperature);
    }

    class DayPlanner
    {
        private Temperature Weather;

        public DayPlanner(Temperature weather)
        {
            this.Weather = weather;
        }

        public void SetPlan(IActivity activity)
        {
            Console.WriteLine("Setting plan for " + activity.GetActivity(Weather));
        }
    }

    class IndoorActivity : IActivity
    {
        public string GetActivity(Temperature temperature)
        {
            return (temperature == Temperature.Cold) ? "Hockey" : "Fitness gym";
        }
    }

    class OutdoorActivity : IActivity
    {
        public string GetActivity(Temperature temperature)
        {
            switch (temperature)
            {
                case Temperature.Cold:
                    return "Ice skating";
                case Temperature.Mild:
                    return "Hiking";
                case Temperature.Hot:
                default:
                    return "Swimmig";
            }
        }
    }

    static void Main(string[] args)
    {
        DayPlanner planner = new DayPlanner(Temperature.Mild);
        
        OutdoorActivity outdoorActivity = new OutdoorActivity();
        planner.SetPlan(outdoorActivity); 
        // print out "Setting plan for Hiking"

        IndoorActivity indoorActivity = new IndoorActivity();
        planner.SetPlan(indoorActivity);  
        // print out "Setting plan for Fitness gym"

        Console.ReadKey();
    }
}

Here the DayPlanner holds a reference of an IActivity instance, an implementation of a strategy. With different strategy assigned, DayPlanner shows different activity. Strategy pattern results in dynamic behavior.

Template Method Pattern

Template method defines a sequence of method calls or some algorithms in base class, and the detail implementation of each method or algorithm is overriden by subclass. One obvious example is the ASP.NET Page life cycle: PreLoad, OnLoad, PreRender, etc. Those events are invoked by base Page class and developers are responsible to fill the detail implementation in code-behind for those events. Following code illustrate how the PersonDayPlanner schedules morning, afternoon and night activities by template method:

using System;
using System.Collections.Generic;

class TemplateMethodPattern
{
    abstract class PersonDayPlanner
    {
        // Template Method defines the skeleton steps
        public void Plan()
        {
            MorningPlan();
            AfternoonPlan();
            NightPlan();
        }

        // Abstract methods implemented by child classes
        public abstract void MorningPlan();
        public abstract void AfternoonPlan();
        public abstract void NightPlan();
    }

    class CollegeStudentSaturday : PersonDayPlanner
    {
        public override void MorningPlan()
        {
            Console.WriteLine("Have a simple breakfast and do cleanup.");
        }

        public override void AfternoonPlan()
        {
            Console.WriteLine("Study for 2 hours then go to gym for exercise.");
        }

        public override void NightPlan()
        {
            Console.WriteLine("Eat out with friends and watch movie after");
        }
    }

    class LittleKidSaturday : PersonDayPlanner
    {
        public override void MorningPlan()
        {
            Console.WriteLine("McDonald's happy meal for breakfast, then play at park.");
        }

        public override void AfternoonPlan()
        {
            Console.WriteLine("Friend's birthday party.");
        }

        public override void NightPlan()
        {
            Console.WriteLine("Play LEGO and watch TV.");
        }
    }

    static void Main(string[] args)
    {
        PersonDayPlanner saturday = new CollegeStudentSaturday();
        saturday.Plan();
        Console.WriteLine();
        //print out a student's plan on Saturday:
        // Have a simple breakfast and do cleanup.
        // Study for 2 hours then go to gym for exercise.
        // Eat out with friends and watch movie after

        saturday = new LittleKidSaturday();
        saturday.Plan();
        //print out a kid's plan on Saturday:
        // McDonald's happy meal for breakfast, then play at park.
        // Friend's birthday party.
        // Play LEGO and watch TV.

        Console.ReadKey();
    }
}

Builder Pattern

The Builder pattern separates the construction logic for an object. This pattern is often used when the construction process of an object is complex. By modifying above DayPlanner code demo we have following builder implementation:

using System;
using System.Collections.Generic;

class BuilderPattern
{
    interface IPersonDayPlan
    {
        void MorningPlan();
        void AfternoonPlan();
        void NightPlan();
    }

    class DayPlanBuilder
    {
        private IPersonDayPlan DayPlan { get; set; }

        public DayPlanBuilder() { } // default constructor

        public DayPlanBuilder(IPersonDayPlan dayPlan)
        {
            this.DayPlan = dayPlan;
        }

        public void SetPersonDayPlan(IPersonDayPlan dayPlan)
        {
            this.DayPlan = dayPlan;
        }

        public void BuildPlan()
        {
            if (this.DayPlan != null)
            {
                this.DayPlan.MorningPlan();
                this.DayPlan.AfternoonPlan();
                this.DayPlan.NightPlan();

                // You can control the steps or add new logic inside the builder
                if (this.DayPlan is CollegeStudentSaturday)
                {
                    Console.WriteLine("Play games no later than 12PM.");
                }
            }
        }
    }

    class CollegeStudentSaturday : IPersonDayPlan
    {
        public void MorningPlan()
        {
            Console.WriteLine("Have a simple breakfast and do cleanup.");
        }

        public void AfternoonPlan()
        {
            Console.WriteLine("Study for 2 hours then go to gym for exercise.");
        }

        public void NightPlan()
        {
            Console.WriteLine("Eat out with friends and watch movie after");
        }
    }

    class LittleKidSaturday : IPersonDayPlan
    {
        public void MorningPlan()
        {
            Console.WriteLine("McDonald's happy meal for breakfast, then play at park.");
        }

        public void AfternoonPlan()
        {
            Console.WriteLine("Friend's birthday party.");
        }

        public void NightPlan()
        {
            Console.WriteLine("Play LEGO and watch TV.");
        }
    }

    static void Main(string[] args)
    {
        DayPlanBuilder planBuilder = new DayPlanBuilder(new LittleKidSaturday());
        planBuilder.BuildPlan();
        //print out:
        // McDonald's happy meal for breakfast, then play at park.
        // Friend's birthday party.
        // Play LEGO and watch TV.

        // changing to student's plan
        planBuilder.SetPersonDayPlan(new CollegeStudentSaturday());
        planBuilder.BuildPlan();
        //print out:
        // Have a simple breakfast and do cleanup.
        // Study for 2 hours then go to gym for exercise.
        // Eat out with friends and watch movie after
        // Play games no later than 12PM.

        Console.ReadKey();
    }
}

The Builder pattern plays a little bit different "director" role compared with the Template method in the build process. In above example BuildPlan method can have different build steps for different IPersonDayPlan types; while the Template method has fixed run steps. Builder is creational pattern and it is mainly used to create complex object. Template method is behavior pattern and it defines skeleton of running procedure.

Bridge Pattern

Both Template method and Builder pattern use inheritance to implement different behaviors or algorithms. The situation would become messy when more and more objects join and play in the hierarchy. In above DayPlanner example, we could have AdultMonday, AdultTuesday...SeniorMonday, SeniorTuesday...etc. of many other combination for sub classes. Bridge pattern resolves the problem by decoupling an abstraction from its implementation so that the two can vary independently. Modify the code example again the Bridge version looks like:

using System;
using System.Collections.Generic;

class BridgePattern
{
    interface IDayPlanner
    {
        string DayOfWeek { get; }
        void Plan(int age, bool inSchool);
    }

    abstract class Person
    {
        public int Age { get; set; }
        protected IDayPlanner DayPlanner { get; set; }

        public void SetDayPlanner(IDayPlanner dayPlanner)
        {
            this.DayPlanner = dayPlanner;
        }

        public abstract void PlanDay();
    }

    class CollegeStudent : Person
    {
        public CollegeStudent(int age)
        {
            base.Age = age;
        }

        public override void PlanDay()
        {
            Console.WriteLine("College student's " + this.DayPlanner.DayOfWeek + " schedule:");
            this.DayPlanner.Plan(this.Age, true);
        }
    }

    class LittleKid : Person
    {
        public LittleKid(int age)
        {
            base.Age = age;
        }

        public override void PlanDay()
        {
            Console.WriteLine("Little kid's " + this.DayPlanner.DayOfWeek + " schedule:");
            this.DayPlanner.Plan(this.Age, false);
        }
    }

    class SaturdayPlanner : IDayPlanner
    {
        public void Plan(int age, bool inSchool)
        {
            if (age < 6)
            {
                Console.WriteLine("Play at park, friend's birthday party, and play LEGO.");
            }
            else if (age > 18 && inSchool)
            {
                Console.WriteLine("Study, gym exercise and social activities.");
            }
            // plan for other cases
        }

        public string DayOfWeek { get { return "Saturday"; } }
    }

    class MondayPlanner : IDayPlanner
    {
        public void Plan(int age, bool inSchool)
        {
            if (age < 6)
            {
                Console.WriteLine("Kindergarten.");
            }
            else if (age > 18 && inSchool)
            {
                Console.WriteLine("Classes, library and lab.");
            }
            // plan for other cases
        }

        public string DayOfWeek { get { return "Monday"; } }
    }

    static void Main(string[] args)
    {
        Person kid = new LittleKid(5);
        kid.SetDayPlanner(new SaturdayPlanner());
        kid.PlanDay();
        //Little kid's Saturday schedule:
        //Play at park, friend's birthday party, and play LEGO.

        Person student = new CollegeStudent(20);
        student.SetDayPlanner(new MondayPlanner());
        student.PlanDay();
        //College student's Monday schedule:
        //Classes, library and lab.

        Console.ReadKey();
    }
}

Now person and weekday are separated, and we don't have to do their combinational sub-classes anymore, known as "prefer composition over inheritance" for such scenario. The Provider model in .NET framework, such as Membership Provider and Session State Provider, is an example of using Bridge pattern.

Last but not least, design patterns are useful to resolve certain problems but they are not a silver bullet. Overusing them could introduce unnecessary complexity and cost. We should't force to use patterns as what I do in this posting. The pattern examples I presented above are just for demonstration purpose and it doesn't mean they are the best practices. In reality we should consider those patterns when having a code smell in our work.