Dependency Injection Entwurfsmuster

OO Design muss mit Objekten umgehen, die sich gegenseitig beeinflussen. Dadurch enstehen zwangsläufig Abhängigkeiten zwischen den Objekten. Diese sind potentiell Grund für Seiteneffekte.
Deshalb sollten die Abhängigkeiten möglichst reduziert werden.

Kopplungen & Abhängigkeiten

Ein Beispiel für eine enge Kopplung zwischen zwei Objekten:

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

Ein Tax Objekt ist direkt gekoppelt an die Personen Klasse.
Die Steuerberechnung funktioniert in dieser naiven Implementierung also nur für Personen:

taxation = Taxation.new 5_000
taxation.debit(20)
# => 1000

Allerdings sollten auch andere Objekte (Firmen oder generell Rechtsformen) besteuert werden können.

Dependency Injection

Die nötige Flexibilität kann in solchen Situationen mit Dependency Injection erreicht werden. Dabei wird das benötigte Objekt (Person, Company etc.) nicht direkt in der Klasse (Tax) instantiiert, sondern außerhalb und stattdessen lediglich übergeben:

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

Die Abhängigkeit wurde in das Tax Objekt injiziert.
Die Steuerberechnung funktioniert nun mit allen Objekten, die das erwartete Interface (income) implementiert haben:

taxation = Taxation.new Person.new(5_000)
taxation.debit(20)
# => 1000

Dadurch gibt es keine Kopplung zwischen den Klassen mehr, sondern lediglich zwischen den Interfaces.

Flexibilität

Die erhöhte Flexibilität läßt sich einfach umsetzen und macht sich bezahlt, weil sich die Funktionalität mit Dependency Injection leicht erweitern läßt:

taxation = Taxation.new Company.new(100_000)
taxation.debit(20)
# => 20000