Part I. Abstract Factory Overview
The abstract factory is a GOF (Gang of Four) creational pattern where the intent is to "...provide an interface for creating families of related or dependent objects without specifying their concrete classes". ("Design Patterns" -- Gamma, Help, Johnson, Vlissides)
There are four main parts to any abstract factory:
- Abstract Factory: defines generic interface for creation of objects.
- Concrete Factory: implementation of the abstract factory.
- Abstract Product: the generic interface that defines the object to be created.
- Concrete Product: the implementation of the abstract product. In other words, the actual objects.
When the abstract factory is ready for consumption, we will be coding to the abstract members: the abstract factory and the abstract product. As a general rule, if our code base is abstract it should be abstract all the way. Maintenance can be a nightmare if we have concrete implementations for interfacing with some code and abstract classes and interfaces for other parts of the code (how would we then keep track of which is which and manage change?). The actual implementation will exist in the concrete factory and the concrete product.
The abstract factory can be implemented either through abstract base classes or interfaces. I often stand on the "interface soapbox" because it is often the best way to keep the code base flexible and have some structure at the same time. However, sometimes we don't want the rigidity that comes with a framework defined through interfaces, and so go with the alternative which is to use abstract base classes. Such is the case with the provider pattern used extensively in the .NET 2.0 framework, which is basically a configurable implementation of the abstract factory pattern. (But this is a subject for another article.)
If you have not seen it already, here's a short article I wrote on interface development.
Part II. Implementation: an Abstract Automobile Factory.
Loosely based on an article at wikipedia on the ModelT, we'll be implementing an abstract factory to build automobiles. (http://en.wikipedia.org/wiki/Ford_Model_T) I'll be taking quite a few liberties and so all of you "car people" out there will probably be wincing with my lack of automobile knowledge, but please remember, the point here is our factory, not the actual cars we make.
To get started, we'll look at a car in terms of four key areas: the automobile body, engine, suspension, and transmission. These are the definitions for our abstract products.
At the core of everything is our IAuto interface which defines a (very) generic automobile.
// THE AUTOMOBILE -----------------------
interface IAuto
{
IAutoBody Body { get; }
IAutoEngine Engine { get; }
IAutoSuspension Suspension { get; }
IAutoTransmission Transmission { get; }
void Set(IAutoBody body);
void Set(IAutoEngine engine);
void Set(IAutoSuspension suspension);
void Set(IAutoTransmission transmission);
}
Next we have definitions for each of the parts:
// THE BODY ----------------------------------------
enum BodyType
{
Car,
Convertable,
Sedan,
Coupe,
Truck
}
// THE ENGINE -----------------------
// THE SUSPENSION ---------------------
// THE TRANSMISSION ----------------------
enum DriveType
{
FrontWheelDrive,
RearWheelDrive
}
enum TransmissionType
{
Manual,
Automatic
}
Finally, we'll build our definition for the abstract factory.
interface IAutoFactory
{
IAuto GetAuto();
IAutoBody CreateBody();
IAutoEngine CreateEngine();
IAutoSuspension CreateSuspension();
IAutoTransmission CreateTransmission();
}
Part III. Implementation: the Concrete Products.
For our Model T, we need concrete definitions for the automobile and each of the parts: the engine, body, transmission and suspension.
Here is the concrete implementation of the automobile. I'm keeping it as simple as possible here, but in real life there might be all sorts of methods and properties specific to the Model T. We are only going to implement things at the bare minimum.
Keep in mind we are stripping out all the complexity that would exist in a real project and as you look through these, imagine what it would look like if, in the interface definition, there were five more properties and a dozen or so methods outlining how all the parts of the automobile interact and you can get an idea of why we would use an abstract factory for construction of multiple similar complex objects.
// CONCRETE AUTOMOBILE ----------------------------
Now for the definitions of the concrete components of the Model T. We'll keep these as simple as possible for the purposes of this article.
// CONCRETE ENGINE -----------------------------------------
// CONCRETE BODY ---------------------------------
// CONCRETE SUSPENSION ----------------------------------------------
// CONCRETE TRANSMISSION ---------------------------
Part IV. Implementation: the Concrete Factory.
Finally, we need to implement the concrete factory. Notice that I have implemented the interface methods using the "virtual" keyword. This is intentional, so when we want to make a new type of ModelTFactory where there is a minor change in the Model T, we can inherit from this class and make the tweaks without having to re-write the whole factory.
class ModelTFactory: IAutoFactory
{
#region IAutoFactory Members
public IAuto GetAuto()
{
return new ModelT();
}
public virtual IAutoBody CreateBody()
{
return new ModelTBody(500, BodyType.Car, 2, false);
}
public virtual IAutoEngine CreateEngine()
{
return new ModelTEngine(450, 2, 35, 100, 10, "Hand Crank");
}
public virtual IAutoSuspension CreateSuspension()
{
return new ModelTSuspension(150, 99, 56,
"transversely mounted semi-elliptical spring",
"foot pedal applied band around drum");
}
public virtual IAutoTransmission CreateTransmission()
{
return new ModelTTransmission(100, DriveType.FrontWheelDrive,
2, 2, TransmissionType.Manual);
}
#endregion
}
Part V. A Builder.
Now that we've covered all the bases (no pun intended), we'll create automobiles with our abstract factory using a builder method. In a more complex scenario, the builder may possibly be responsible for wiring all the different objects together if they required it. Note: The builder is not a part of the pattern definition, but it makes life much easier, so it is here.
static class AutomobileBuilder
{
public static IAuto Build(IAutoFactory factory)
{
IAuto myAuto = factory.GetAuto();
myAuto.Set(factory.CreateBody());
myAuto.Set(factory.CreateEngine());
myAuto.Set(factory.CreateSuspension());
myAuto.Set(factory.CreateTransmission());
return myAuto;
}
}
Part VI. Implementing the factory.
After all the headache of creating the factory, it is actually a piece of cake to use. Check it out: To build a Model T, you only have to...
// Build a Model T
IAutoFactory factory = new ModelTFactory();
IAuto auto = AutomobileBuilder.Build(factory);
Now, where it really shines, is when we have a new version of the Model T.
In the wiki article it mentions that we can get deeper tread for the "southern road model" so we only have to override our model T factory's CreateSuspension() method and we have a new vehicle coming from our factory (notice I also gave it "anti-lock" breaks in addition to having a tread depth of 60... I know... anti-lock breaking systems weren't around then, but what's the fun in writing articles if you can't take some liberties every now and then).
class SouthernRoadModelTFactory: ModelTFactory
{
public override IAutoSuspension CreateSuspension()
{
return new ModelTSuspension(150, 99, 60,
"transversely mounted semi-elliptical spring",
"anti-lock");
}
}
Not only can we adjust what kind of Model T is produced, but we can take advantage of our abstract factory to build ANY type of automobile: trucks, modern racing cars, anything you can think of.
Hopefully this article helped you understand the basics of the abstract factory pattern.
Until next time,
Happy coding