visit
In object-oriented design, one of the principle aims is to produce code that is flexible, maintainable, and reusable. One of the ways to do this is to use abstractions in your code rather than concretions. The more your objects know about how one another are implemented, the more dependencies there are in your system. As the number of dependencies grows, the potential for cascading breakage grows as well. But what happens when you have a system that requires certain objects to come from the same family? How do you ensure that any objects you instantiate are indeed from that family without hard-coding a complicated control structure? One solution to this problem is the abstract factory pattern (AFP). Let’s dive in and see how it works.
The AFP originates with the so-called “Gang of Four” (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides), which wanted to specify a means of encapsulating object factories thematically without having to specify any concretions. To do this, the group created the AFP, which they intended to be used to:
“Provide an interface for creating families of related or dependent objects without specifying their concrete classes.” [1]
At first blush, this sounds a bit esoteric, but the idea of using thematically linked classes makes more sense when you think of it as a configuration mechanism. The classic example of the need that the AFP addresses relates to operating systems. Imagine a piece of software that is designed to be portable between different operating systems: Mac; Windows; Linux; etc. Now imagine that this software needs to instantiate different kinds of widgets to do its work. When in a Windows environment, this software should certainly instantiate Windows-compatible buttons and dialogue boxes rather than Mac-compatible versions (and vice versa). Without configuration, your software might inadvertently use the wrong type of widget and cause unexpected errors.
One way to solve the configuration problem would be to hard-code test conditions in your software to see whether it is currently in a Windows, Mac, or Linux environment, and then instantiate the right kind of widget accordingly, as in the following pseudo-code:
function InstantiateWidget(environment) {
if (environment == “Windows”)
return new WindowsWidget;
else if (environment == “Mac”)
return new MacWidget;
else if (environment == “Linux”)
return new LinuxWidget;
else
throw new IncompatibleEnvironmentError();
}
The above might seem like a reasonable approach, and indeed, in a small program it may very well be fine. However, there are a number of problems with this approach. First, if you later want to add a new environment, such as iOS or Android, then you will have to edit this function to include new conditionals. This would be a clear violation of the . Second, in all likelihood you will be making more than one kind of widget in a program (windows, checkboxes, etc.) and all of your functions will thus be littered with conditionals of this sort. Finally, as a result of this structure, your program will be dependent on every kind of widget no matter where it is being used. As your software grows, code like this will quickly become impossible (or at least unpleasant) to manage.
In order to address the problems raised by thematic configuration through conditionals, the AFP proposes a different approach. Rather than concern itself with thematically varying dependencies directly, clients should instead rely on abstractions. These abstractions take two forms: the abstract services the client intends to use (widgets, etc.); and, an abstract factory to instantiate those services. In this case, the client is divorced entirely from the service creation process. Not only does the client not know what services it is using but it doesn’t care. The client only cares that a given service implements an expected set of behaviors.
Before we look at this pattern in more detail, let’s consider the actual purpose of the abstract factory for which the pattern is named. An abstract factory defines the contract that concrete factories will follow. That contract includes specification for the construction of various services. Notably, the contract defined by the abstract factory itself includes abstractions. In other words, the abstract factory exists only to encapsulate a group of thematically similar functions without necessarily knowing how they are implemented or what they do. To continue with the classic example, the abstract factory does not know about WindowsWidgets and MacWidgets, it only knows about abstract Widgets and will expect its descendent concrete factories to know how to implement those concrete widgets in accordance with its particular theme. In following this pattern, a client can accept an abstract factory of any sort (Mac, Windows, etc.) and use it to create thematically appropriate widgets as needed.
Due to its various moving parts, developing a mental model for how the AFP works can be a bit of a challenge. At its core though, the AFP requires just a few elements:
Per usual, the best way to illustrate a design pattern is through actual code. In the following example, our task is to write a program that can build different kinds of thematically-appropriate monuments depending on how a client is configured (that is, which factory it is provided). Take a moment to review the following code and see if you can pick out the roles that each of the entities is playing. Afterwards, we’ll go through it together.
<a href="//medium.com/media/ca641aef74eda8e9870a21bbf9625389/href">//medium.com/media/ca641aef74eda8e9870a21bbf9625389/href</a>
That was a lot of code to sort through for a simple example! (And an illustration of the overhead necessary to use the AFP.) For the sake of simplicity, our various monuments don’t do much, but if we look at each element we can see how they fit into the AFP.
When we instantiate and configure our two clients — greekMonumentHandler and egyptianMonumentHandler — we do so using the appropriate concrete factory. And indeed, when we then use those clients to execute certain behavior, we get thematically-appropriate results.
For a simple program such as the one above, the AFP may seem like an overreaction. Indeed, it’s not appropriate to every situation because it requires greater code overhead upfront. However, we also get the benefit of decoupling our client from the services it uses and the creation of those services. This allows for easy extension because we could create many different kinds of concrete factories to achieve different behavior. In the above example, we might want to create an AztecMonumentFactory and a RomanMonumentFactory in order to support new configurations for our client MonumentHandler. Doing so would be straightforward; however, as the number of concrete factories grows our ability to update them easily diminishes. If, for example, we wanted to add a BuildMausoleum function to our configuration, then we would have to add an implementation of that function to every single concrete factory.
Another benefit to consider is the fact that clients can be configured dynamically and at runtime. A client doesn’t need to know which factory it uses, it only needs to know about the abstract factory. As a result, we can change configuration by pointing the client towards a different concrete factory, thus changing its behavior.
On the whole, the AFP is a great way to insulate clients from the specifics of how their services are created. It’s also a useful means of configuring a client with a family of related objects and behaviors. As with any design pattern though, it’s important to consider trade-offs before choosing a course of action.
The abstract factory pattern (AFP) is a means of encapsulating related objects into a single configuration. This is accomplished through the use of an abstract factory that is aware of abstract services and concrete factories with implementations of the requisite services. Upon instantiation, a concrete factory may be used to configure the behavior of a client object. By using the AFP, it is possible to ensure the thematically-appropriate use of various types of services by a given client. Simultaneously, you can decouple that client from the specifics of its services. Although the AFP is a useful pattern in many situations, it isn’t appropriate for every situation due to upfront overhead and the difficulty of adding new services.
And that’s the abstract factory pattern! If you’re interested in learning more about design patterns, you can check out these articles on the and the , and/or stay tuned for future articles!
If you would like alerts when a new article is published you can follow me here on Medium, on , or subscribe on my where these articles are cross-published. Happy coding!
References