Abstract Factory
Introduction
This is going to be the first post in a new series that I’m doing on design patterns. Now, I’m certainly not the first person to post a taxonomy of design patterns and explain them, but I would like to put a different spin on the usual. Normally, I see postings that contain a UML diagram and then verbiage like “encapsulates indirection in a creational paradigm between concrete implementers and….” To put it another way, I see definitions that are useful if and only if you already know the definition. So, my idea here is to relate the design patterns in the context of a story rather than a stuffy-sounding taxonomy. Why do people do this? What problem does it solve? Why is my code potentially worse if I don’t do this?
Toward that end, my tone will tend toward the narrative and conversational here, and I’m hoping that people will find this useful. I will also tend to favor the practical over the pedantically/academically correct. This is a field guide to design patterns.
Quick Information/Overview
Pattern Type | Creational |
Applicable Language/Framework | Agnostic OOP |
Pattern Source | Gang of Four |
Difficulty | Hard to wrap your head around at first, easy enough to implement |
Up Front Definitions
I’m going to define a few terms in advance that will make this post easier to understand. These apply to the remainder of this post.
- Context–A broad scenario in an application, such as, “are we using a MySQL or SQL Server database?” or, “are we logging on errors or exiting the application?”
- Polymorph–In this post, I’ll use this term specifically to mean an interface implementer or a subclass of a base.
The Problem
Let’s say that you’re gearing up for a new car purchase, and, because you’re a coder by identity and not by job description, you write a little utility to track average purchase price of recent sales. Like most car purchasers, you’re not sure what car you want to buy, or even what manufacturer and form factor. That is, you may decide to go with a Ford Explorer, but you might also want a Honda Civic or a smart car. You’re keeping your options open.
As a good C# and object-oriented programmer, you realize that there is a lot of commonality in cars that can be shared in a polymorphic object structure. So, in design and prototyping, you create an abstract Automobile base class and abstract inheritors like “SUV”, “Sports”, and “Sedan”. Now, with this object structure, Explorer is going to inherit from SUV and, let’s say, have a property called “Manufacturer” that initializes to “Ford.”
public class Explorer : Suv
{
private const int DefaultPrice = 25000;
public Explorer()
{
Manufacturer = "Ford";
Price = DefaultPrice;
}
}
For your purposes, car is an entity that represents concepts like “price,” “warranty,” “Consumer Reports Rating,” etc. That is, the abstraction for this application’s purposes does not represent things like “Start()” or “ChangeOil()”. The reason I mention this is that having, say, “Escape” by Ford and “Tribute” by Mazda be two different classes is important in this abstraction where it wouldn’t be in the physical one since those two cars are more or less the same, physically.
Now, let’s think in terms of the application how these objects get created. Presumably, you write some web crawling utility that looks for sale prices and other information and collects them in local persistence such as a file or a database. From there, when you launch the utility, you have some kind of service that reads the file/db and massages the data into a format that interests you (average price, standard warranty duration, etc.) Let’s also say that one of the screens offered is one in which you first choose a make of car and then are able to see all models of that manufacturer. Your context is car manufacturer, and, based on that context, you create a different set of polymorphs.
Think of the most basic way that might go. Somewhere in the presentation tier (ViewModel, code-behind, etc), we’re going to receive information from the service and build our objects:
public partial class MainWindow : Window
{
ICarFactory _factory;
private List _cars = new List();
public MainWindow()
{
InitializeComponent();
BuildCar("Ford"); //Use ford by default until/unless user changes it
}
private void BuildCar(string makerName)
{
_cars.Add(BuildCar(makerName, ModelType.Sedan));
_cars.Add(BuildCar(makerName, ModelType.Sports));
_cars.Add(BuildCar(makerName, ModelType.Sports));
}
public Automobile BuildCar(string makerName, ModelType type)
{
switch (makerName)
{
case "Ford":
switch (type)
{
case ModelType.Sedan: return new Focus();
case ModelType.Sports: return new Mustang();
case ModelType.Suv: return new Explorer();
default: return null;
}
case "Chevy":
switch (type)
{
case ModelType.Sedan: return new Malibu();
case ModelType.Sports: return new Camaro();
case ModelType.Suv: return new Tahoe();
default: return null;
}
default:
return null;
}
}
}
You might think that we could just have one giant, non-nested switch over model, but we’re allowing for the (albeit remote) possibility that two auto manufacturers have the same model name. Also, I’m not passing in information about price or anything like that here, for the sake of clarity. In this example, assume that whoever calls the build method fills in those details.
So what’s wrong with this code? Well, for starters, it’s ugly, but aesthetics is hardly grounds for a refactoring or reconsideration. But in this case, the ugliness represents a code smell. And the code smell in question is brittleness. This is a piece of code in code-behind or a ViewModel, and the design here is such that, each and every time we want to add a new model of car, we have to open this class up and modify it. Presentation tier classes should change when the logic for managing the GUI state changes, not when the application receives more data. What we have here is an egregious violation of the open/closed principle.
So, What to Do?
The first thing we could do is pull this code out of the code-behind/ViewModel and put it into a class called “CarFactory”. The code-behind/ViewModel would then have a reference to a car factory and would not need to change as a result of the logic for constructing cars changing. With that in place, let’s think about potential inefficiencies. What we have is a factory class with two levels of switch statements. So, every time you add a car model, you have to change the factory method, and every time you add a car make, such as when Kia comes into existence, you have to add a whole bunch of code to the factory (an outer case and a bunch of inner cases). You’re picking your poison between adding a little code to a lot of statements or a lot of code to one statement.
public class CarFactory
{
public Automobile BuildCar(string makerName, ModelType type)
{
switch (makerName)
{
case "Ford":
switch (type)
{
case ModelType.Sedan: return new Focus();
case ModelType.Sports: return new Mustang();
case ModelType.Suv: return new Explorer();
default: return null;
}
case "Chevy":
switch (type)
{
case ModelType.Sedan: return new Malibu();
case ModelType.Sports: return new Camaro();
case ModelType.Suv: return new Tahoe();
default: return null;
}
default:
return null;
}
}
}
Now, let’s improve it by using some polymorphism on the context instead of just on the polymorphs. I’m going to write a new class called ICarFactory and define implementers FordFactory and ChevyFactory. In the ICarFactory, we’re going to define a contract for building an SUV, a Sedan, and a Sports. So Ford’s implementation will return an Explorer, Focus, and a Mustang, while Chevy will return Tahoe, Malibu, and Camaro, respectively.
public interface ICarFactory
{
public Automobile GetCar(ModelType type);
}
public class FordFactory : ICarFactory
{
public Automobile GetCar(ModelType type)
{
switch (type)
{
case ModelType.Sedan: return new Focus();
case ModelType.Sports: return new Mustang();
case ModelType.Suv: return new Explorer();
default: return null;
}
}
}
public class ChevyFactory : ICarFactory
{
public Automobile GetCar(ModelType type)
{
switch (type)
{
case ModelType.Sedan: return new Malibu();
case ModelType.Sports: return new Camaro();
case ModelType.Suv: return new Tahoe();
default: return null;
}
}
}
public partial class MainWindow : Window
{
ICarFactory _factory;
private List _cars = new List();
public MainWindow()
{
InitializeComponent();
BuildFactory("Ford"); //Default to Ford until the user selects something else
BuildCars();
}
public void BuildFactory(string makerName)
{
if (makerName == "Ford")
{
_factory = new FordFactory();
}
else if (makerName == "Chevy")
{
_factory = new ChevyFactory();
}
}
public void BuildCars()
{
_cars.Add(_factory.GetCar(ModelType.Sedan));
_cars.Add(_factory.GetCar(ModelType.Sports));
_cars.Add(_factory.GetCar(ModelType.Suv));
}
}
The power of this is that we can figure out which make we want and let the manufacturer implementations define what objects they’re going to create. By having context polymorphism, we separate context creation from polymorph creation. This implementation is far less brittle than what we had before. The factories change only when their particular versions of the model change. If I add a new model called “Crossover” and only Chevy has one, I don’t have to edit the Ford factory. If I add an entirely new manufacturer, like Mazda, I just create the Mazda classes and a new Mazda factory. The only existing code I need to touch is in the BuildFactory() method where I add a case. (BuildFactory() should probably be its own class or somehow settable/injectable into our class here, but I’m trying to condense a bit for illustration purposes.)
A More Official Explanation
Abstract Factory gives us the power to partition object (polymorph) creation that is hierarchical in nature. You have one place that context creation is done and another that polymorph creation is done. In our example, when we create cars, we first decide who the maker is and then we decide, based on the maker, what the model is. That hierarchical, context-based creation paradigm is the first requisite element for the Abstract Factory pattern. The second requisite element is that our decision hierarchy results in the same type of object, depending on the first decision. In our example, whether you pick Ford, Chevy, etc., all will have a Sports object and an SUV object (or null).
In other words, the Abstract Factory pattern is appropriate when you have object families, and, in different contexts, you want different families. Chevy context corresponds to one set of {SUV, Coupe, Sports} polymorphs and Ford to another one. You cannot encapsulate, say, Toshiba in this Abstract Factory implementation and return polymorphs of type {VCR, Television}. The returned objects have to be familial variations on the same object. The general form of this is that you have some set of contexts (say, c1, c2… cn) and some set of polymorphs (foo, bar, baz, etc), and for each context/polymorph pair, you have some concrete inheritor. So, you have c1Foo, c2Foo … cnFoo, c1Bar, c2Bar, cnBar, c1Baz, c2Baz, … cnBaz.
A good, quick way to think of this is that Abstract Factory is suitable for situations where groups of inheritors are always found together or not at all in the context in which you are operating. In our case, in a window displaying Fords, you wouldn’t randomly find a Toyota Corolla. You’re getting a Focus or you’re getting null. Logically, this constraint already exists in the design spec. The Abstract Factory pattern simply puts this form into class-method form instead of case statement-nested case statement form.
Other Quick Examples
To cement the understanding here, let’s consider a few other scenarios in which Abstract Factory would be appropriate:
- You are reading and writing to persistent storage from an application, and you have two abstract entities, Reader and Writer. You might define MySqlReader, MySqlWriter, SqlServerReader, SqlServerWriter, and FileReader, FileWriter. Then, you’d define IPersistenceFactory with GetReader() and GetWriter() methods and your MySqlPersistenceFactory, SqlServerPersistenceFactory, and FilePersistenceFactory would all return their respective objects from their two-member families.
- You have a GUI that you want to display different color buttons, text boxes, etc., depending on the gender of the person logged in. Everything should be blue for males and pink for females. You define IControlFactory with inheritors MaleControlFactory and FemaleControlFactory that return inheritors of Button, TextBox, etc. So, depending on the gender of the user, you select that gender’s factory and depend on it for supplying the appropriate controls.
A Good Fit – When to Use
Now that we’ve identified a few examples of when this might be a reasonable pattern to use, let’s generalize a bit. Here are some heuristics for when you can or should use the Abstract Factory pattern.
- The Abstract Factory pattern is a good one to keep in mind any time you find yourself creating one set of inheritors in one context and another set in another context. Usually, you can see a good opportunity to use this when you have large switch blocks and/or nested switch blocks inside of which you’re mainly creating object polymorphs.
- In addition, you might consider this pattern preemptively rather than as a refactoring when you’ve got requirements and/or a design that implies making the same creational decision over and over when creating subclassed objects. That is, you’re looking at a design and seeing that you need A-related objects a third of the time, B-related objects a third of the time, and C-related objects a third of the time because broader contexts A, B, and C happen a third of the time each. This is appropriate when your requirements practically guarantee that you’re going to be adding more contexts later. We’re going to revisit this in the “don’t use” section with a subtle distinction.
Square Peg, Round Hole – When Not to Use
I think it’s just as important to address when not to use a pattern as when to use it. A lot of people encounter a design pattern or a technique for solving problems in general and get the “golden hammer” syndrome–every problem looks like a nail because of the expensive new hammer. So when is Abstract Factory not appropriate?
- I touched on it a bit earlier, but you do not want to use the Abstract Factory creational pattern when the contexts are not related. You wouldn’t create an Abstract Factory to encapsulate creation of televisions and VCRs if the manufacturer was Toshiba and SUVs and Sports cars if the manufacturer was Ford. Even if you did create an inheritance structure that had television and SUV inheriting from the same base class in order to make this work (after all, in C#, they both technically inherit from object), the abstraction would be an incomprehensible nightmare. The result would be that when you got this “object” that might be either a television or a car, you would invariably use the “is” or “as” operator to figure out which it was, and that defeats the entire purpose of the pattern. The context polymorphism is designed to allow you to perform operations on the objects returned from the factory without caring which specific objects they are.
- You also don’t want to use this pattern if you don’t have any context. Imagine that we decided we had narrowed our search down to only Fords and were choosing between Ford models. The context becomes pointless and the Abstract Factory an unnecessary abstraction. We gain nothing with Abstract Factory that we couldn’t get by simply having FordFactory. This is another, distinct pattern that I’ll get to later called Factory Method. In the “good fit” section, I suggested using Abstract Factory if you’re pretty sure you’re going to need it. But, if you don’t think you will, or you aren’t sure, you’re better off following the YAGNI principle.
- Finally, don’t use this if you don’t have a polymorphic structure. You can try setting it all up, but you’ll find that you won’t be able to implement this pattern if your created objects don’t have a common ancestor or interface. You could, theoretically, return object or create some interface that the objects inherit just to create them this way, but I promise you that will be a headache. If they don’t naturally share functionality, you don’t want to create them this way, as you’ll just wind up casting them back to their original state to figure out how to use them. And that defeats the entire purpose of polymorphism. Don’t try to create things with an Abstract Factory just to use one.
So What? Why is this Better?
I’ve touched on this throughout, but I’ll summarize it here. Without Abstract Factory, you’ll have either inline creation logic or creation logic that makes more decisions in one place than it should. The single responsibility principle advises that classes should only have one reason to change: for the purposes of easy maintainability.
Inlining object creation is a subject unto itself, but suffice it to say that it comes with problems. Good luck testing a class that creates a bunch of objects and then uses them. And good luck maintaining it. In our example, once your ViewModel class starts keeping track of the UI and also keeping track of car creation, it will get messy. You’ll change the view model to accommodate a growing UI. You’ll change the view model to accommodate a growing corpus of data. The two functionalities will start blending together in the one place, and changes for the sake of one will start to break the other functionality–not to mention that, if you want to swap in a new object creation scheme or UI scheme, you’ll have to detangle the two.
And, in the same vein, Abstract Factory versus plain factory, when there are multiple contexts, decouples even further. The logic for car context is separate from the logic for individual car creation. You could employ different factory creation schemes if you decide you want multiple ways of generating Fords without loading the factories down with conditionals.
Generally speaking, decoupling the code is good for a number of reasons. The Abstract Factory pattern is simply a well-recognized and tried-and-true way of doing this. Used properly, it will help you have maintainable code and code in which new requirements being added means adding classes/methods rather than editing existing ones and opening yourself up to regressions.
Well I definitely enjoyed reading it. This tip procured by you is very constructive for proper planning.
[…] keeping with the vehicle theme from the previous post, let’s say that we have an application that models vehicle operation. Among other things, we […]