A very common design pattern used in Object Oriented Programming, is the Factory Pattern. The purpose of this post isn't to explain the factory pattern or why it's useful, rather I want to show a simple way to eliminate a giant switch / case found in many factory pattern implementations. For some good reading on the Factory Pattern, I suggest reading these two articles:
Wikipedia : http://en.wikipedia.org/wiki/Factory_method_pattern
MSDN : http://msdn.microsoft.com/en-us/library/ms954600.aspx
To demonstrate a simple example of the Factory Pattern, I've created a few classes. First, I created a base Vehicle class that looks something like this:
public abstract class Vehicle
{
public virtual int TopSpeed
{
get
{
return 150;
}
}
public abstract int Wheels
{
get;
}
public override string ToString()
{
return String.Format("A {0} has {1} wheels, and a top speed of {2} MPH."
, this.GetType().Name, this.Wheels, this.TopSpeed);
}
}
Just a base class that has one virtual property, one abstract property, and it overrides ToString. Then, I've created 4 subclasses:
public class Car : Vehicle
{
public override int Wheels
{
get { return 4; }
}
}
public class SuperCar : Car
{
public override int TopSpeed
{
get
{
return 200;
}
}
}
public class Truck : Vehicle
{
public override int Wheels
{
get { return 18; }
}
}
public class Motorcycle : Vehicle
{
public override int Wheels
{
get { return 2; }
}
public override int TopSpeed
{
get
{
return 190;
}
}
}
So we have Vehicle, Car, SuperCar, Truck and Motorcycle. Now, say we wanted to create a Factory that returns us the correct Vehicle class based on an enum that we'd supply. So let's create an enum:
public enum VehicleType
{
Car,
SuperCar,
Truck,
Motorcyle
}
We'd then have a method that looks something like this:
public static Vehicle GetVehicle(VehicleType vehicle)
{
switch (vehicle)
{
case VehicleType.Car:
return new Car();
case VehicleType.SuperCar:
return new SuperCar();
case VehicleType.Truck:
return new Truck();
case VehicleType.Motorcyle:
return new Motorcycle();
default:
return null;
}
}
Well, this is all nice, and works fine, but imagine a scenario where you may have many many subclasses. This switch statement would get huge, and unmaintainable quickly. Imagine then that new subclasses come along, you'd have to first update the enum, and then remember to update this switch / case. Well, I think there's a better way to do this.
The premise is simple; create an attribute that has one property called Type. This attribute will go on the enum, and will represent which type should be instantiated for each value of the enum. So, let's first create the Attribute class:
public class VehicleInfoAttribute : Attribute
{
private Type type;
public VehicleInfoAttribute(Type type)
{
this.type = type;
}
public Type Type
{
get
{
return this.type;
}
}
}
Nothing fancy, just a simple attribute that will house the Type to be created. Now, let's go back to our enum, and decorate the values with the correct attributes:
public enum VehicleType
{
[VehicleInfo(typeof(Car))]
Car,
[VehicleInfo(typeof(SuperCar))]
SuperCar,
[VehicleInfo(typeof(Truck))]
Truck,
[VehicleInfo(typeof(Motorcycle))]
Motorcyle
}
Now, each enum has an attribute that tells us which type to be instantiated for that type. Now, the fun part. The reflection bit in the factory method itself:
First, I've created an extension method for enum's that helps with getting custom attributes off of enum values:
public static class Extensions
{
public static T GetAttribute<T>(this Enum enumValue)
where T : Attribute
{
FieldInfo field = enumValue.GetType().GetField(enumValue.ToString());
object[] attribs = field.GetCustomAttributes(typeof(T), false);
T result = default(T);
if (attribs.Length > 0)
{
result = attribs[0] as T;
}
return result;
}
}
This allows you to do something like this:
MyCustomAttribute a = myEnumValue.GetAttribute<MyCustomAttribute>();
Now, we have all the pieces in place to write the Factory Method:
public static Vehicle GetVehicle(VehicleType vehicle)
{
var vehicleAttribute = vehicle.GetAttribute<VehicleInfoAttribute>();
if (vehicleAttribute == null)
{
return null;
}
var type = vehicleAttribute.Type;
Vehicle result = Activator.CreateInstance(type) as Vehicle;
return result;
}
First we call our extension method to get the attribute value for the enum passed in. Then, we use the handy Activator.CreateInstance() to create an object of that type.
To test it out, we can write a quick app:
static void Main()
{
Vehicle v = VehicleFactory.GetVehicle(VehicleType.Truck);
Console.WriteLine(v);
}
This will output:
A Truck has 18 wheels, and a top speed of 150 MPH.
Using this approach, yields two benefits. First, you no longer have a giant ugly switch / case. Secondly, if you ever have a case where another subclass is added, you just add another enum value (which you'd have to do anyway if you were using the switch / case), slap on the attribute, and you're done. The Factory method doesn't need to change at all.
2 comments:
Your GetVehicle method should check the validity of the VehicleType property ex.
if (!Enum.IsDefined(vehicle)) {
throw new InvalidArgumentException();
}
Also, consider throwing an exception instead of returning null if the attribute is not found.
You know, I never knew about the Enum.IsDefined(..) method. I guess the only way for that to fail, is if you deliberately call the GetVehicle method like this:
var v = GetVehicle((VehicleType)100); or some other crazy number...
Not a bad suggestion, but if someone is calling that method like that, that's their problem :-P
As for your second suggestion, I was debating last night if I should throw an exception or return null. You're probably right though, an Exception would make more sense....
Post a Comment