Tuesday, October 21, 2008

Visitor Pattern By Example

Visitor pattern separates some algorightm or computation work to its own class. Being able to "visit" visitable objects, different algorithm implementations can do their work without modification of original objects. Let's say a person has different kind of valuables/assets, and the value calculations could have their own implementation:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        public interface IValuable  // Interface for visitable objects 
        {
            void Value(IValueCalculator calculator);
        }

        public interface IValueCalculator   // Interface for visitor implementation
        {
            decimal GetTotal();
            void Calculate(BankAccount bankAccount);
            void Calculate(Loan load);
        }

        public class NetWorthCalculator : IValueCalculator
        {
            private decimal Total = 0;

            public decimal GetTotal()
            {
                return Total;
            }

            public void Calculate(BankAccount bankAccount)
            {
                Total += bankAccount.Balance;
            }

            public void Calculate(Loan loan)
            {
                Total -= loan.Amount;
            }
        }

        public class MonthlyIncomeCalculator : IValueCalculator
        {
            private decimal Total = 0;

            public decimal GetTotal()
            {
                return Total;
            }

            public void Calculate(BankAccount bankAccount)
            {
                Total += (bankAccount.Balance * bankAccount.InterestRate) / 12;
            }

            public void Calculate(Loan loan)
            {
                Total -= loan.MonthlyPayment;
            }
        }

        public class BankAccount : IValuable
        {
            public string Name { get; set; }
            public decimal Balance { get; set; }
            public decimal InterestRate { get; set; }

            public void Value(IValueCalculator calculator)
            {
                calculator.Calculate(this);
            }
        }

        public class Loan : IValuable
        {
            public string Name { get; set; }
            public decimal Amount { get; set; }
            public decimal MonthlyPayment { get; set; }

            public void Value(IValueCalculator calculator)
            {
                calculator.Calculate(this);
            }
        }

        public class Person : IValuable // Composite pattern: Person itself is also IValuable
        {
            public string Name { get; set; }
            public List<IValuable> Valuables = new List<IValuable>();

            public void Value(IValueCalculator calculator)
            {
                foreach (IValuable item in Valuables)
                {
                    item.Value(calculator);
                }
            }
        }

        static void Main(string[] args)
        {
            Person person = new Person() { Name = "new graduate student" };
            person.Valuables.Add(new BankAccount()
            {
                Name = "Saving Account",
                Balance = 5000,
                InterestRate = 0.05m
            });
            person.Valuables.Add(new BankAccount()
            {
                Name = "Cheque Account",
                Balance = 2000,
                InterestRate = 0.01m
            });
            person.Valuables.Add(new Loan()
            {
                Name = "Student Loan",
                Amount = 5000,
                MonthlyPayment = 500
            });

            NetWorthCalculator netWorth = new NetWorthCalculator();
            person.Value(netWorth);
            Console.WriteLine("Assets' net worth is: " + netWorth.GetTotal());

            MonthlyIncomeCalculator monthlyIncome = new MonthlyIncomeCalculator();
            person.Value(monthlyIncome);
            Console.WriteLine("Monthly income from Assets: " + monthlyIncome.GetTotal().ToString("#.##"));

            Console.Read();
        }
    }
}

Run the console app will show following result:

    Assets' net worth is: 2000
    Monthly income from Assets: -477.5

The NetWorthCalculator and MonthlyIncomeCalculator are totally separated from the target IValuable objects. We can implement other type of calculation without any modification on existing code. When adding new IValuable type, the ICalculator interface and its implementations need to include the new type. Back to our example, assume the student found a job after graduation from school, paid off most of the student loan in six months and bought some Microsoft stocks. Two new IValuable types are introduced:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        public interface IValuable  // Interface for visitable objects
        {
            void Value(IValueCalculator calculator);
        }

        public interface IValueCalculator   // Interface for visitor implementation
        {
            decimal GetTotal();
            void Calculate(BankAccount bankAccount);
            void Calculate(Loan load);
            void Calculate(Job stock);
            void Calculate(Stock stock);
        }

        public class NetWorthCalculator : IValueCalculator
        {
            private decimal Total = 0;

            public decimal GetTotal()
            {
                return Total;
            }

            public void Calculate(BankAccount bankAccount)
            {
                Total += bankAccount.Balance;
            }

            public void Calculate(Loan loan)
            {
                Total -= loan.Amount;
            }

            public void Calculate(Job job)
            {
                // Do nothing
            }

            public void Calculate(Stock stock)
            {
                Total += stock.Price * stock.Share;
            }
        }

        public class MonthlyIncomeCalculator : IValueCalculator
        {
            private decimal Total = 0;

            public decimal GetTotal()
            {
                return Total;
            }

            public void Calculate(BankAccount bankAccount)
            {
                Total += (bankAccount.Balance * bankAccount.InterestRate) / 12;
            }

            public void Calculate(Loan loan)
            {
                Total -= loan.MonthlyPayment;
            }

            public void Calculate(Job job)
            {
                Total += job.Salary / 12;
            }

            public void Calculate(Stock stock)
            {
                // Do nothing
            }
        }

        public class BankAccount : IValuable
        {
            public string Name { get; set; }
            public decimal Balance { get; set; }
            public decimal InterestRate { get; set; }

            public void Value(IValueCalculator calculator)
            {
                calculator.Calculate(this);
            }
        }

        public class Loan : IValuable
        {
            public string Name { get; set; }
            public decimal Amount { get; set; }
            public decimal MonthlyPayment { get; set; }

            public void Value(IValueCalculator calculator)
            {
                calculator.Calculate(this);
            }
        }

        public class Job : IValuable
        {
            public string Name { get; set; }
            public decimal Salary { get; set; }

            public void Value(IValueCalculator calculator)
            {
                calculator.Calculate(this);
            }
        }

        public class Stock : IValuable
        {
            public string Name { get; set; }
            public decimal Price { get; set; }
            public int Share { get; set; }

            public void Value(IValueCalculator calculator)
            {
                calculator.Calculate(this);
            }
        }

        public class Person : IValuable // Composite pattern: Person itself is also IValuable
        {
            public string Name { get; set; }
            public List<IValuable> Valuables = new List<IValuable>();

            public void Value(IValueCalculator calculator)
            {
                foreach (IValuable item in Valuables)
                {
                    item.Value(calculator);
                }
            }
        }

        static void Main(string[] args)
        {
            Person person = new Person() { Name = "6-month after graduation" };
            person.Valuables.Add(new BankAccount()
            {
                Name = "Saving Account",
                Balance = 20000,
                InterestRate = 0.05m
            });
            person.Valuables.Add(new BankAccount()
            {
                Name = "Cheque Account",
                Balance = 5000,
                InterestRate = 0.01m
            });
            person.Valuables.Add(new Loan()
            {
                Name = "Student Loan",
                Amount = 1000,
                MonthlyPayment = 500
            });
            person.Valuables.Add(new Job()
            {
                Name = "Software Developer",
                Salary = 60000
            });
            person.Valuables.Add(new Stock()
            {
                Name = "Microsoft",
                Price = 20,
                Share = 100
            });

            NetWorthCalculator netWorth = new NetWorthCalculator();
            person.Value(netWorth);
            Console.WriteLine("Assets' net worth is: " + netWorth.GetTotal());

            MonthlyIncomeCalculator monthlyIncome = new MonthlyIncomeCalculator();
            person.Value(monthlyIncome);
            Console.WriteLine("Monthly income from Assets: " + monthlyIncome.GetTotal().ToString("#.##"));

            Console.Read();
        }
    }
}

Run the app again and now the net worth and monthly income are both positive:

    Assets' net worth is: 26000
    Monthly income from Assets: 4587.5

The Visitor Pattern is quite useful for some scenarios. But again we use the design patterns to resolve certain problems, and let the code more extensible and maintainable. They may not be suitable for some situations and over using them may make the code unnecessarily complicated.

Friday, October 03, 2008

Invoke Generic Method Dynamically In .NET

Generic method was first introduced in .NET 2.0, and we can write method with dynamic types like:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

class Test
{
public class Foo
{
public string Name { get; set; }
public Foo(string name)
{
Name = "Foo" + name;
}
}
public static List<T> ParseValues<T>(string[] data)
{
List<T> values = new List<T>();
Type type = typeof(T);
if (type == typeof(int))
{
foreach (string str in data)
values.Add((T)((object)int.Parse(str)));
}
else if (type == typeof(double))
{
foreach (string str in data)
values.Add((T)((object)double.Parse(str)));
}
else if (type == typeof(Foo))
{
foreach (string str in data)
values.Add((T)((object)new Foo(str)));
}
return values;
}

static void Main()
{
string[] values = new string[] { "1", "2", "3" };
List<int> numbers = ParseValues<int>(values);
List<double> doubles = ParseValues<double>(values);
List<Foo> foos = ParseValues<Foo>(values);

//Print 1 2 3 1 2 3 Foo1 Foo2 Foo3
numbers.ForEach(i => Console.Write(i + " "));
doubles.ForEach(i => Console.Write(i + " "));
foos.ForEach(i => Console.Write(i.Name + " "));

Console.Read();
}
}
In order to invoke a generic method you have to declare the type statically, and it will be initiated at compile time. Is there a way to get a generic type dynamically? We can do something like:
    Type genericListType = typeof(List<>).MakeGenericType(typeof(double));
var list = Activator.CreateInstance(genericListType);
But that doesn't resolve our problem because we can't determine the T type at compile time to invoke the method of public static List<T> ParseValues<T>(string[] data).

One way I can think of is use the reflection and invoke the method dynamically:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

class Test
{
public class Foo
{
public string Name { get; set; }
public Foo(string name)
{
Name = "Foo" + name;
}
}
public static List<T> ParseValues<T>(string[] data)
{
List<T> values = new List<T>();
Type type = typeof(T);
if (type == typeof(int))
{
foreach (string str in data)
values.Add((T)((object)int.Parse(str)));
}
else if (type == typeof(double))
{
foreach (string str in data)
values.Add((T)((object)double.Parse(str)));
}
else if (type == typeof(Foo))
{
foreach (string str in data)
values.Add((T)((object)new Foo(str)));
}
return values;
}

static void Main()
{
string[] values = new string[] { "1", "2", "3" };
List<int> numbers = ParseValues<int>(values);
List<double> doubles = ParseValues<double>(values);
List<Foo> foos = ParseValues<Foo>(values);

//Print 1 2 3 1 2 3 Foo1 Foo2 Foo3
numbers.ForEach(i => Console.Write(i + " "));
doubles.ForEach(i => Console.Write(i + " "));
foos.ForEach(i => Console.Write(i.Name + " "));
Console.WriteLine();

Type numberType = typeof(int);
MethodInfo genericMethod = typeof(Test).GetMethod("ParseValues").MakeGenericMethod(numberType);
var dynamicList = genericMethod.Invoke(null, new object[] { values });

// Print 1 2 3
foreach (var item in dynamicList as IEnumerable)
Console.Write(item + " ");

Type fooType = typeof(Foo);
genericMethod = typeof(Test).GetMethod("ParseValues").MakeGenericMethod(fooType);
dynamicList = genericMethod.Invoke(null, new object[] { values });

// Print Foo1 Foo2 Foo3
foreach (var item in dynamicList as IEnumerable)
Console.Write((item as Foo).Name + " ");

Console.Read();
}
}
Bear in mind that using Reflection is costly and it would affect performance significantly.