Design patterns are kind of old news by now. They're the training wheels we use before we really understand what it means to write clean code; before we really come to grips with SOLID principles and all that cal.
I've always been fond of design patterns when building complex applications; they help to manage complexity and solve repeated, simple problems. There are a few that I use every day, and some I haven't used at all.
Creational Patterns are some of the most useful - and factories are really neat. Factories help us to keep the creational code separate from the actual meat and potatoes of our program.
A factory ensures that we always code toward an interface, and never to an implementation. It helps us decouple our code from the dependencies on low level types, and keep calling code as generic as possible.
The only downside I think, is that it's really REALLY really hard to make factories adhere to the Open-Closed principle. There are ways around this, but they just add more complexity and affect the simple elegance of a factory. So just accept it. They won't be Open-Closed adherent; but they'll be really neat.
Let's C a little Sharper
So how does a factory work, you might ask?
Let's demonstrate with a chocolate factory:
We'll create some Maltezers; a Lunch-Bar; a Kit-Kat; and finally an Aero.
Each of these items is a chocolate. They all satisfy what it means to be a chocolate, and belong to the family of chocolates.
First, lets create an interface (family) called Chocolate
public interface Chocolate
{
public void Eat();
}
Next, let's create the different types (instances) of chocolate.
public class Maltezers: Chocolate
{
public void Eat()
{
Console.WriteLine("Crunch!");
}
}
public class LunchBar: Chocolate
{
public void Eat()
{
Console.WriteLine("Crisp!");
}
}
public class KitKat: Chocolate
{
public void Eat()
{
Console.WriteLine("Have a break!");
}
}
public class Aero: Chocolate
{
public void Eat()
{
Console.WriteLine("Bubbles!");
}
}
We'll need some easy way to identify them, and don't get me started on magic strings...
public enum ChocolateType
{
Maltezers,
LunchBar,
KitKat,
Aero
}
And now for the factory. As a matter of style, I like to name the method creating the object after the family. It's just a little developer preference. The name is usually a noun, and this reads like English.
public class ChocolateFactory
{
public Chocolate Chocolate(ChocolateType identifier)
{
switch(identifier)
{
case ChocolateType.Maltezers:
return new Maltezers();
case ChocolateType.LunchBar:
return new LunchBar();
case ChocolateType.KitKat:
return new KitKat();
case ChocolateType.Aero:
return new Aero();
default:
throw new NotImplementedException();
}
}
}
And there you have it. What would you like? A Kit-Kat? Well certainly, let me just...
var factory = new ChocolateFactory();
var yourKitKat = factory.Chocolate(ChocolateType.KitKat);
And there you go - a Kit-Kat that looks, feels, and definitely tastes like a chocolate. It responds to being a chocolate and your calling code would never know it was anything other than a chocolate, decoupling the code from the implementation KitKat, and allowing you to code to the interface Chocolate.
Top comments (0)