Introduction to Factory Pattern (1): Simple Factory Pattern
Contents
This post is also available in following languages
Nowadays, design patterns are not that popular to talk about. This could be related to the rapid iteration of web services, and the popularity of Micro Services. Design patterns were once considered useless dogma, thus as an anti-pattern, during the hype of Function Programming.
Of course, design patterns might have been thrown away as the essence of Object-Oriented Programming, because Functional Programming was preferred to OOP and was a hype during the time.
But if we think back, these design patterns are actually useful experiences extracted from practice. They do improve readability and scalability of programs to some extent. On the other hand, as well known conventions, design patterns deepen understanding between author and reader. In this sense, they can be considered as a medium of communication. Of course, this has to be used at correct scenarios. In terms of correct scenarios, we are talking about suitable scenarios — not using because we want to use, but for practical purpose.
In this blog post, I will introduce one of the most used design patterns: Factory Pattern, and talk about my understanding on it.
Introduction
Factory Pattern could arguably be the most widely used design pattern in Java. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create object. In Factory pattern, we create an object through a factory without exposing the creation logic to the client and refer to newly created object using a common interface.
Let’s first look at the simplist factory pattern.
Simple Factory Pattern
In my daily coding life, Simple Factory Pattern is enough to use. It suits well when we want to use a factory class to encapsulate creation logic of some product class that we would like to create. This product class can be extended by many sub-classes, which we are usually not interested in. We would rather like the factory to do its work, choose suitable sub-classes and create corresponding instances, by passing type information into the factory method.
Let’s look at a concret example.
Say we have a product class called Car
, stands for cars. Different car factory can manufactory different cars, which can be considered as sub-classes of Car
. So we can have BmwCar
, VolvoCar
, TeslaCar
, and so on, demonstrated below.
Suppose we have a magic car factory called CarFactory
, which is able to create multiple brands of cars. In order words, it can create BmwCar
, VolvoCar
and TeslaCar
. When some type of Car
is requested, the only thing to do is to tell the CarFactory
what type of Car
to create, and leave creating the car to this factory. How the car is manufactured does not concern, only that the factory will deliver the car fufilling all the requirements concerns.
As a client of this factory, how do I tell CarFactory
which type of cars to create? It can be passed to createCar()
method of CarFactory
via method arguments.
|
|
Inside this createCar()
method, the factory can choose the actual car type to create based on arguments passed in.
|
|
See complete code example at this github repo: https://github.com/gdong42/factory-pattern
Pros and Cons
What’s the benefit of this pattern? In this pattern, the caller side does not need to know the exact type implementation and its creation details, it only provides the required type name information. So this pattern reduces the complexity of the client, and decouples the creation logic of various implementation from invocation logic to some extent.
What about the cons? Let’s think about it in real world. There are few such factories. Because this magical factory needs to know how to create every product, which is almost impossible. OOP reflects the real world, therefore it is not reasonable to have a super magic factory that can create all type of products as well in the software world. In other words, each time when we want to add a new car type, we need to update createCar()
. This should better be avoided.
Another problem is that SimpleCarFactory
class has to depend on all car types, due to the truth that it contains all creation invocation logic in one giant factory. This leads to the client code coupling all product types, which might never be called by the client, and thus should be optional instead of mandatory dependencies. What we expect is that when we need some kind of product, we use a factory to create it, only with required dependency that has the underlying implementation module.
Real Example
In Java, an good example is the String.valueOf()
method series.
The String
class in Java represents a string. Sometimes, you need to get a string from a boolean or an integer. But String
doesn’t provide constructors like String(Integer i)
or String(Boolean b)
. Instead, it provides multiple simple factory methods String.valueOf(…)
.
|
|
A Step Further
Let’s take a step further and see how to improve this pattern to solve above problems.
There are two ways that might be useful to avoid the drawbacks mentioned above.
Class Registration - maintain a mapping of the actual type and the name prameter of the type. This can be done with two flavors:
- with reflection
- without reflection
Factory Method Pattern - the famous pattern described in the book of GoF’s design patterns.
Next let’s look at them in a bit more details.
Class Registration
The registration can be done by invoking a registering method of the factory. The actual creation of the factory can be done using two ways: with or without reflection. Using this technique, we can maintain the name and type mappings in a HashMap in the factory. To allow registering new mappings, we need to add a method allowing new types to be registered. This way, adding new product types no longer requires any change in the factory class code.
Class Registration, with reflection
For actual object creating logic, the type associated with the requesting type identifier is looked up from the internal mappings, and then the actual create instance logic is called by invoking the constructor using reflection. This can be demonstrated as followed.
|
|
This approach requires one to register a car type to the factory before calling the createCar
method. It can be done by calling the registerCar
method explicitly from the client code. For example,
|
|
This reflection implementation has its own drawbacks.
- Performance.
- Cannot handle well when product has complex constructor parameters.
- The registering logic seems a burden that the client needs to bear with.
Class Registration, without reflection
If for some reason we cannot use reflection, how to do the registration? We can move the registration to the concrete product class itself. This way, the factory does not require knowledge of the concrete product class. Adding new product type does not require changes to the factory as well.
We first add a new method in the abstract product interface, which is Car
in our case.
|
|
|
|
Above we register the Car
instance into the Factory registration map. We also implement the create()
method to produce new instances of BmwCar
when required.
The factory and its usage look like below:
|
|
This non-reflection implementation has its own drawbacks as well.
- The client code need to call
Class.forName()
explicitly in order to make sure the registration logic is invoked, which is not convenient. - The concrete product class requires to know the presence of the Factory, and needs to call its register logic.
In the real world, the registration part is usually presented by some kind of framework, thus above drawbacks can be mitigated.
Factory Method Pattern
If we take a second thought on above non-reflection class registration, it has a create()
method in the Car
interface. This is an abstrace method that is used to encapsulate the creation logic to the concrete product itself. It looks more like what a factory does.
Besides, the example registers a Car
instance in the static code, which is solely used to be called with the create()
method to create other Car
instances. See? the design is a bit awkward.
If we make the factory specific to each concrete car types, maybe we can use CarFactory
type and BmwCarFactory
, TeslaCarFactory
and VolvoCarFactory
subtypes to create concrete car types. This way some awkwardness above can be avoided. In fact, this leads us to the Factory Method Pattern.
In next installment of this article, I will talk about Factory Method Pattern.
Author Gan Dong
LastMod 2019-10-06