This article builds on the Factory Design Pattern in Kotlin and Java — Made Easy and Simple article which covers the basics of the Factory Method. We continue the example of building a CarFactory
below.
How a decent Factory method implementation looks
While this is a passable approach, there is room for improvement. Let’s review some of the areas of improvement below.
The CarFactory
breaks the open-closed design principle.
The Open-Closed design principle state software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. If we wanted to add another CarManufacturer
and a new Car
implementation, then we would have to change the code in the CarFactory
to support the new Car
. Instead we want to be able to continually extend CarFactory
to provide more Car
implementations.
The CarFactory has too many responsibilities.
The Single Responsibility design principle states every class, module, or function in a program should have one responsibility/purpose in a program. The CarFactory
is constructing several subclasses of Car
when ideally it should only be responsible for providing just one. In this example, each subclass of Car
could have several dependencies and construction steps that would quickly make the getCarByManufacturer
unscalable and unsustainable.
The CarFactory tightly couples every Car and CarManufacturer.
On the client-side, which is the BasicDealership
, any changes or additions done to the CarFactory
will always be made available to the client. Imagine we wanted to remove a Car
implementation from one Dealership
but not another. This would be impossible because the CarFactory
couples the responsibilities of all the Car
subclass creations which means if it’s removed from the getCarByManufacturer
function, then its removed from all clients that depend solely on CarFactory
. The client should be able to decide it’s own composition. The Dealership
should be able to decide what CarFactories
it wants to support.
How we improved the implementation
Below is the new and improved implementation of the CarFactory
called BetterCarFactory
. Let’s review some of the changes.
The BetterCarFactory (CarFactory improved)
The BetterCarFactory
starts by being declared as an interface rather than a concrete class. This will allow subclasses to determine how to produce a Car
and will still enforce the Single Responsibility principle and Factory method getCar()
.
The old CarFactory
requires the CarManufacturer
in its create method, while the BetterCarFactory
does not. This is because the interface does not care what subclass of Car
is being created, only that some subclass of Car
is created.
The BetterCarFactory Subclasses
The BetterCarFactory
subclasses now encapsulate their own specific Car
creation implementations. This more maintainable and scalable as compared to the getCarByManufacturer
function in the CarFactory
which contained the implementation details of all the Car
subclasses. New Car
and BetterCarFactory
implementations may be created without affecting other existing implementations.
The Dealership (Client)
The AdvancedDealership
can now determine which Factories and Cars it wants to support. The old BasicDealership
had access to only the Cars the CarFactory
had available, which essentially made it impossible to compose different Dealerships
based on the uniqueness of the Cars that are available. Now different Dealerships
can be created with different sets of factories.
That’s it! Much more improved! You can find the example repository here! Follow me for more coding made simple content!