There are several situations, when we want to use creational design patterns. The problem is that, we sometimes have troubles ‘which’ creational pattern should be implemented and ‘when’.
There are several creational patterns. In this article I’ll try to compare Abstract Factory with Factory Method patterns, because of plenty of people, whose have thoughts about differences between the patterns. With that knowledge you will do less mistakes.
» Class scope (like Factory Method) – based on inheritance, used to modify classes, whose instances are created
» Object scope (like Abstract Factory) – creating new instances is delegated to another object
These design patterns are about instantiation and do not retain ownership of instances they create. They enable us to preserve system independence from the way we create, represent, and fold objects.
System (as Client) will be focused only on usage of newly created objects through common interface – irrespective of the implementation of each one. I noticed that there are almost always two scenarios:
- These patterns encapsulate (hide) information on which specific classes the system uses
- They give us a great deal of flexibility in what is being created, who does it, how it goes and when it takes place
EXAMPLE FROM REALITY:
Let’s imagine that you have online shop, and there are different carriers, whose ship products to the clients.
Each carrier have an API, which give us possibility to call courier, generate labels for each parcel, etc. This API have a lot of endpoints, whose are divided by features (groups of feature-related endpoints).
SOLUTION 1: ABSTRACT FACTORY
It’s mainly an interface to create family of related objects, without specifying their concrete classes. We are taking into consideration two random carriers like FedEx and Gls (there may be more carriers of course).
So we have many carriers with many common features. They are fitting perfectly to Abstract Factory. In this case each feature is a family of related objects, whose have common interface. And by implementing concrete factories (specific for each carrier) we are able to create all features from current carrier. Client depends only on abstraction, thanks that we can easily exchange one carrier to another.
PROS & CONS
it helps us with:
- replacing feature families of the carrier
- isolating concrete classes – the client does not have a clue as to which concrete classes to use (and does not need to know) because the factory encapsulates it
- it maintains consistency (enforces interfaces) between individual features, so you can use them in the same way
it hinders some cases, when:
- we want to add new features, but we want to use them only in one carrier, we have to add them also to the interfaces and concrete factories of each carrier to stay consistent – this case generates some redundant work to do by developer
- sometimes one carrier have unique feature, which isn’t available in other carriers – and we want to use this feature – so it will be problem where we should implement it:
- in interface and concrete factories (it will affect empty implementations of this unique feature in other carriers, which don’t have it – it is not a good approach)
- in other place outside abstract factory (also not good idea, because then we will lose cohesion)
WHEN SHOULD WE USE THIS PATTERN?
- our system (client) should be independent of the process of creating, submitting, and representing concrete classes
- we know in advance what kind of family (carrier) we will use – already in the code – before we run our application
- we have many classes that perform the same task / feature and we want to make them consistent
SOLUTION 2: CUSTOMIZED FACTORY METHOD
Let’s see how is looks like with the Factory Method. Let’s recall what this pattern is all about.
It is an specification of interface for object creation, but it allows you to pass instances of the instance creation process to their subclasses. Thanks to “Factory” method, which will be overwritten in subclasses – it defines creating concrete instances of specific carriers.
This pattern also has some variations:
- An abstract class Creator with a non-implemented factory method (abstract method) – the most commonly used variant
- An abstract class Creator with implemented factory method (which creates default concrete objects in case of the absence or unforeseen situation) – e.g. concrete carrier does not have some specific feature so we will use some default object, which should give us this feature
- Parametrized Factory Method pattern – it allows you to create objects of many types. The “Factory” method takes a parameter that defines the type of object that is being created
Now let’s move on to the diagram – in this case we will use the Parametrized Factory Methods to have available multiple features:
As you can see – we have more opportunities in terms of unique features – but in case they are missing in any concrete factory we would have (for example) to throw an exception. So ultimately this is not an elegant solution.
By this example I wanted to show that the classic type of Factory Method does not fit well into the whole family of products creation – I was forced to use multiple, parametrized factory methods.
- helps to maintain relationships between specific objects (just like in an Abstract Factory)
- encapsulates information on which carrier subclass you need to create and you can keep this information out of the client
- in comparison with the Abstract Factory – creation takes place in the subclasses of the specific main class
- it facilitates the creation of new types of concrete objects that are not defined at the beginning
WHEN SHOULD WE USE THIS PATTERN?
- we don’t know at the code/compilation level what kind of subclass we will use, but we know exactly where and when we want to use it
- we want the subclasses of a our class to determine what objects we create
- there are many classes that do the same job, we want to make them more consistent as family
- we also have a Template Method pattern implemented and we need to give it a concrete object, which implements a common interface (these two patterns are often used together e.g. in online shop payments)
HOW CAN YOU DO IT EVEN BETTER?
Regardless of whether you are using an Abstract Factory or a Factory Method, it is worth making concrete factories as Singletons. Almost always in the whole system we will need only one instance of concrete factories to produce many complex and simple objects
Families of concrete classes can implements or extends either interfaces or abstraction classes. So if there is such a case, you don’t know what would fit better (and you don’t see the need to store implementation of some methods in the abstract class) – it is definitely worth to use the interface.
Why? Because classes can implements multiple interfaces, but they can extend only one class (if we have an object language such as PHP7, which does not support multiple-inheritance). In case of future need to extend some class you may have an closed door to use inheritance – if you choosed abstract class instead of interface when implementing Abstract Factory.
HOW CAN THESE TWO PATTERNS COMPLEMENT ONE ANOTHER?
Now having an Abstract Factory – we have only an interface, that can be used to create object families. Everything is ok if we already know at the code stage – what concrete factory (carrier) we will use.
However, when the choice of a specific factory will take place while the application is running (dynamically) – then we need to supply a concrete factory depending on e.g. the user selection (when ordering online – the user selects the carrier).
In this case the Parametrized Factory Method pattern is usually used – which, according to user choice, will provide us with a instance of the concrete factory (from Abstract Factory pattern interface). By this we also isolate ourselves from creating directly concrete factories in system(client) code. This is often referred to as Factory of the Factories.
In simple cases, it is usually sufficient to use a Singleton of concrete factories (belonging to the Abstract Factory pattern).
When using the patterns described in this article, you always should have some time to consider which of them to use and from what reason. It all depends on the situation, as you can see Factory Method is sometimes used to implements Abstract Factory. You can recognize that objects, whose you want to create through this patterns – if they are an families of features – Abstract Factory might be much better option than Factory method.