Factory is an object responsible for creating other objects.
It is often considered a good practice to move the process of object creation from the consumer’s code into the factory. Even more, some people say that you should avoid the use of
new keyword in your code as much as possible. As for me, I think that you should be careful when someone says that you should always …. Instead, it is better to understand why in some cases factories are useful, but sometimes they are a needless overengineering, so you can make a right decision according to the context.
First of all, don’t be afraid to create an object with
new keyword if you need it. Keep KISS principle in mind. The world won’t blow up if you couple one of your classes to another one. By default, you should not use factories. Consider this example of some
Someone can tell that it is a terrible code. We are coupled to
HttpClient’s constructor. When it changes, we need to change this code. Ok, let’s use a factory here and see what changes.
We have added a new class in our system and increased its complexity only to replace one line of code. Our factory doesn’t have any benefits. It only creates a new object. It doesn’t make any decision on how an object should be created or what kind of object it should be. Only a plain
new keyword and that is all. The whole class for only one line of code. Furthermore, if
Api is the only place in the system where
HttpClient is being used, we still need to change the code if
HttpClient constructor changes. The only difference with the factory is that this line is now placed in the factory.
Let’s add a bit more complexity. Here is method
execute to perform various requests:
And again we are creating a
Request object. Should we use a factory here? Or it will add nothing but complexity to the system?
Obviously, there is no difference with the previous example. Again there is a whole class to replace one simple line of code. There is no need in factories when we don’t have any complex creation logic. We won’t get any benefits replacing a line with
new keyword with a static factory call. In our example, the client code (
Api class) knows how to create and how to use
Request object. The client code knows exactly what it wants. It wants an object of the
Request class. And the process of
Request creation is very simple, so the client code can handle it itself. The factory, in this case, will be a needless overengineering and needless abstraction.
This example shows a controller that is used to display reports in different formats: json, pdf and HTML. According to the passed type we create different report objects.
Even if the creation logic of every report in separate is very simple, we should consider this
switch statement as a creation logic of the report object:
And this logic is enough complex. We are making a decision what instance should be created according to some parameter. The controller class doesn’t really care what concrete class it uses, it only needs a report object. It is not the controller’s responsibility to decide what type of report will be better to use for the certain report type. In this case adding a factory comes with some benefits. We can abstract away this complex creation logic and use a simple factory method call:
This code is much better. The creation logic for reports is concentrated in one place. If this logic will be used in many places, it can be easily reused. There is no need to copy and paste all these conditions, especially when we add a new class, there is no risk that we can miss it somewhere.
One more advantage of using factories is that you can use explicit methods names.
The problem with constructors is that they have no names. I think that you will not argue, that
RandomMoneyGenerator::smallerThan(10) looks much more explicit than
new Money(rand(0, 10)).
Factories also decrease the complexity of your tests. You can test your factory that it returns correct objects, and then the client code should be tested that it calls required methods on the returned object. That means that we should test that
ReportFactory returns a correct instance of the report for every type and then test
StatisticsController::report method only once with any type that we like.
The factory is not only for creating objects. It is more complex than that. The factory pattern decides on certain criteria what object should be created, so it is easy to maintain this logic in one place, instead of searching it through the whole system. This creation logic also becomes extensible with the factory. It is easy to add a new class to the factory, without touching the client code, that uses this factory.
Factory will be useful when:
- You need an external object, but you don’t know exactly which one.
- Construction is very complex and you need to reuse it.
And on the contrary, if the factory doesn’t make any decision, the creation logic is very simple or is used only in a single place, the factory will be a needless abstraction. Also, just because you have moved the creation logic into another class, doesn’t mean that the client code becomes decoupled. It is still coupled to the factory class. Don’t use this
new keyword is bad mindset, it will not improve your code.