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
An example for a tight coupling between two objects:
class Taxation
attr_reader :person
def initialize(income)
@person = Person.new(income)
end
def debit(vat_in_percent)
person.income.to_d * vat_in_percent.to_d / 100.to_d
end
end
A Tax object is directly coupled with the Person class.
The tax calculation only works for people in this naive implementation:
taxation = Taxation.new 5_000
taxation.debit(20)
# => 1000
However, other objects (companies or generally legal forms) should also be taxable.
Dependency Injection
In such situations the necessary flexibility can be achieved with Dependency Injection. In this case, the required object (Person, Company, etc.) is not instantiated directly in the class (Tax) any longer. But it is created outside and passed instead:
class Taxation
attr_reader :legal_form
def initialize(legal_form)
@legal_form = legal_form
end
def debit(vat)
legal_form.income.to_d * vat_in_percent.to_d / 100.to_d
end
end
The dependency was injected into the Tax object.
The tax calculation works with all objects that have implemented the expected interface (income):
taxation = Taxation.new Person.new(5_000)
taxation.debit(20)
# => 1000
Through Dependecy Injection there is no coupling between the classes, but only between the interfaces.
Flexibility
The resulting flexibility through Dependency Injection is introduced quite easily and it pays off whenever the functionality has to be extended:
taxation = Taxation.new Company.new(100_000)
taxation.debit(20)
# => 20000