The Factory Method Pattern
I get it; Design Patterns take time to understand. It took me a few attempts to get my head around some of them.
Factory Method proved to be one of those tricky patterns that took me a long time to understand. Having said that, it’s a handy pattern worth learning.
The difficulty in understanding Factory Method may have to do with how it’s usually explained: As the obvious solution to a well-defined design problem. However, that’s not how it works in the messy trenches of day-to-day programming—most of the time, no clear ‘best’ design will appear until we’ve worked through many small and almost unconscious pattern-recognition steps. Then, finally, something clicks, and we gain an insight that a particular approach will be the last fitting puzzle piece, turning our local code into a concise, elegant design.
In this article, I want to recreate at each step the palpable insights that will ultimately lead to the use of the Factory Method pattern. In this way, you, the reader, will get a deeper understanding of the signposts along the way, which indicate where the Factory Method will shine as an excellent solution. And the best way to do this is to look at an example problem and write the code.
Before we get to that, a high-level description of the Factory Method Design Pattern is appropriate: Factory Method is a creational design pattern. That means it’s concerned with instantiating objects. In particular, Factory Method, has a central abstract class outlining a general step-by-step flow of how objects are built up and instantiated. Concrete child class implementations then provide specific overrides for some (or all) of those steps, as depicted in the below figure.
Let’s look at an example.
Car Assembly
Say, you work as a programmer for a company that builds cars. One day, a group of car plant managers approach you—they’re very informal there and know you get stuff done—because they want you to write a new software package to track how the workers and robots assemble the vehicles. You are to model the car assembly process, so management can later analyse and conduct business process improvements (as in use more robots and fire workers to make shareholders slightly better off). Suspecting the real purpose of the software, you reluctantly get to work.
Petrol Car Factory
The company’s assembly lines currently produce petrol cars with petrol engines only. The steps[1] to make petrol cars are:
- Put down the car chassis, then
- Add the petrol engine, then
- Connect the fuel tank, and finally
- Attach the wheels.
You get to it, and in short order, you have a PetrolCarFactory
class containing those four steps in an Assembly()
method:
And the PetrolCar
class looks like this:
You’re happy with the result. When you instantiate a PetrolCarFactory
, it can assemble PetrolCar
instances:
Electric Car Factory
Since the popularity of electric cars is increasing, management asks you to alter the software so that it can also assemble electric cars.
The process for assembling electric cars:
- Construct the car chassis, then
- Add the electric motor, then
- Instantiate the battery array, and finally
- Attach the wheels.
You model the new ElectricCarFactory
and the ElectricCar
classes as follows:
and
And, no surprise, this two-liner builds a new ElectricCar
:
So far, so good. Nothing to see here; move along.
From here on, things are getting more interesting.
On closer inspection of the two assembly procedures, you notice that the process for electric cars mirrors the one for petrol cars, with a couple of variations:
- Step 2 requires an electric motor rather than a petrol engine, and
- Step 3 uses a battery array rather than a fuel tank.
So the only difference is in the type of motor/engine used in step 2 and the energy source type installation in step 3. Yet, the overall assembly workflow is identical.
Factory Method
Given the striking similarity of the general outline of both assembly workflows, you realise how cool it would be to combine the two separate car assembly workflows into a single, abstract workflow. Obviously, you’d also need a way to override individual assembly steps with either a petrol or electric car variant, but that’s it.
Assembly process changes – Before you’re comfortable and willing to go ahead with this design change, you’d like to be reasonably confident that the overall workflow won’t change much between the two car factories, or that future car factory assembly modification will still work for both car types. E.g. If you add a step for the attachment of car doors, will this work for both car types, and can you have this step in both factories? (Answer: Yes and Yes).
Car type changes – What will happen if management introduces more car types and corresponding assembly processes? Extracting a common generic car assembly process will be optimal if it can be the only process for all new car types. We cannot fully predict the future, yet, it is clear that all cars must have a chassis, engine, power source, and wheels—it’s been 50 years since The Jetsons, yet there are still no wheel-less flying cars. In this manner, you’re confident that a generic workflow will cater for all likely future cars.
Finding Common Abstractions – Before you can move ahead with a common process, you recognise that there are a couple of design inconsistencies to overcome:
An ElectricCar
doesn’t know about a PetrolEngine
or FuelTank
, just like a PetrolCar
is ignorant of ElectricMotor
or BatteryArray
. If you are to compose one common car assembly workflow with individually overridable steps for the instantiation of appropriate variants—think PetrolEngine
or ElectricMotor
—then we, then it is necessary to find abstractions for those particular variants. In the case of PetrolEngine
vs ElectricMotor
, the term ‘Motor
‘ works as a category. Similarly, in the case of FuelTank
vs BatteryArray
, you must stretch your generalisation even further—The broad term ‘EnergyStore
‘ captures the essential function. Let’s make those changes to PetrolCar
and ElectricCar
:
Hey, both definitions are the same, apart from the type name! You might as well combine these into a single Car
type:
Nice!
When it comes to extracting a general car assembly process, you feel in the pit of your stomach that it would be wrong to embed the general car assembly process in either the specific car factory classes—ElectricCarFactory
or PetrolCarFactory
. You realise that such a general car instantiation process represents a higher-level function and ought to be housed separately from the specifics—in a new abstract CarFactory
class’ Assembly()
method.
However, there is a problem. While it’s almost too easy to extract the common elements from the specific car factories, like instantiating the chassis and fixing the number of wheels, how can you manage the motor or energy store, where different versions are needed: PetrolEngine
vs ElectricMotor
, and FuelTank
vs BatteryArray
?
Tricky. After some reflection, you have an insight: Create overridable methods which can (virtual
) or must (abstract
) be overridden by the specific instantiations within the child class factories. Since these methods will only be called by the central Assemble()
method, the ‘factory method’, you can declare these overridable methods with the protected
access specifier:
Note: MakeMotor()
and MakeEnergyStore()
are declared as abstract
, so child classes must provide custom overrides.
The final implementations for PetrolCarFactory
and ElectricCarFactory
are super-concise and only contain the overrides for MakeMotor()
and MakeEnergyStore()
:
Well, done! You’ve implemented the Factory Method creational design pattern. The final result is a reusable car assembly workflow which can vary by the type of motor and energy store, depending on which type of car factory we use. The thing that I like about the Factory Method pattern is the absence of the same workflow repeated between the car factories and only specific overrides for some of the steps within the abstract workflow.
Conclusion
I hope you enjoyed this exploration and explanation of the Factory Method design pattern. Some of you may have noticed that the Factory Method pattern is closely related to the Template Method pattern; we use the former to control object construction, while the latter provides overridable program behaviour.
To recap, Factory Method allows us to reduce duplication of complicated step-by-step object initialisation code, which differs between the variants of initialised objects. The amount of code required for each of the child car factories is simple and expresses the local variations only.
You can find the C# code example on GitHub at https://github.com/olafthielke/DesignPatterns
Footnotes:
1. In a genuine car factory, there would likely be hundreds of different steps, but these four steps will do for our purposes.
Leave a Reply
Want to join the discussion?Feel free to contribute!