Separate Instantiation from Usage
Keep the creation of objects separate from where they are used in order to benefit from po
Last updated
Keep the creation of objects separate from where they are used in order to benefit from po
Last updated
Suppose we need to calculate taxes for two different types of entities in our system : people and companies.
The rules to calculate taxes are definitely not the same. In my country, France, people pay tax using a progressive scale. The more they earn, the higher the tax. A rate is applied by slices of revenues.
For companies, the tax is easier to calculate : 15% until a certain threshold and 25% above. For simplicity, we'll say the rate is fixed to 15%.
As you can see, simple leads us to note that :
All entities in the system pay taxes : that's our commonality
But the rule to pay tax differs from one entity to another : that's our variability
The use case of the system is to calculate taxes, so we'll have something like this at some point.
The question is : where should that calculation policy come from ? We really have two solutions.
Either the TaxCalculator itself computes it
Or some other object computes it
The first solution, in a polymorphic context, must be avoided.
Why ? Because it breaks the Single Responsibility Principle and the Open/Closed Principle.
The SRP violation is prevalent : the TaxCalculator
not only has the responsibility of creating the calculation policy, but it also have to use it.
SRP isn't simply about cohesion : it's about cohesion from the perspective of actors. An object should have only one vector of change, and clearly, TaxCalculator
has two :
It can change because a new type of entity has been added to the system, or because the rules to determine which policy to create have evolved
Or it can change because the calculation itself involve additional steps (such as applying tax reductions
The OCP violation is directly linked to the first vector or change : adding a new type of entity into the system would involve modifying the TaxCalculator.
Imagine if there's more than two policies. For example, companies under a certain legal status have different rules, and people who are war veteran have a different tax policy. The code of the TaxCalculator
will rapidly grow and is definitely not closed to modification. It will contain both the complexity of deciding which object to create, and how to use it.
The cleaner, more extensible solution is to have some other object create it : a factory, or more exactly, an Abstract Factory. Our factory is abstract because the product it creates, the TaxCalculationPolicy
, is an abstract product.
We thus end up we two objects :
A factory, responsible for maintaining the rule of object creation
A client, responsible for managing the object
The instantiation of the correct object has been isolated into TaxCalculationPolicies
that serves as an Abstract Factory, while the usage of the object lies in the TaxCalculator
.
The instantiation has been separated from usage.