Dependency Injection pattern
OO design has to deal with objects interacting with each other. The resulting dependencies between those objects are potential reasons for side effects.
Therefore, the dependencies should be reduced as much as possible.
Coupled dependencies
Let’s imagine a story concerning taxation as an example for a tight coupling between two objects: Taxation
and the Person
to be taxed.
We go with Ruby because Ruby is lovely readable.
class Taxation
attr_reader :person
def initialize(income)
@person = Person.new(income: income)
end
def charge
person.income.to_d * person.tax_rate.to_d / 100
# algorithm for collecting the tax
end
end
With this naive approach obviously only people are taxable.
Taxation.new(5_000).taxation.charge 20
# person is charged 1.000
The dependency to the taxable entity is baked right into the taxation object itself. However there are also other entities like companies or nonprofit organizations having their specific tax rate. They also should be taxable.
Dependency Injection
Oftentimes the required flexibility could be achieved with Dependency Injection
. It basically means to separate dependent data or algorithm and merge them at a central location. In this example the objective is to merge the tax data like income and tax rate with the algorithm how to tax.
class Taxation
attr_reader :taxable
def initialize(taxable:)
@taxable = taxable
end
def charge
tax = taxable.income.to_d * taxable.tax_rate.to_d / 100.to_d
# algorithm for collecting the tax
end
end
The dependency (taxable entity) is injected into the taxation object. Charging the tax works with any kind of taxable object providing the expected interfaces. Even aliens could be taxed as long as they have an income and a tax rate.
person = Person.new(income: 5_000)
Taxation.new(taxable: person).charge
# person is charged 1.000
company = Company.new(income: 100_000)
Taxation.new(taxable: company).charge
# company is charged 20.000