Letzte Woche haben wir in der Dependency Injection Serie die Setter Injection gesehen. Und genau dieses Konzept wollen wir jetzt noch einen Schritt weiter treiben. Die heute vorgestellte Vorgehensweise entkoppelt die beiden Klassen dazu noch etwas mehr.
Interface Injection:
Das Konzept hierbei ist eigentlich recht einfach. Statt eine spezielle Klasse zu erfordern, nutzt man nun ein Interface. Das bedeutet für uns, dass unsere Klasse, die später über den Setter gesetzt wird, dieses Interface implementieren muss.
Das Interface definieren wir beispielsweise so:
interface DBAccess { public function query($sql); }
Man kann sehen, dass eine Methode query
vorhanden sein muss. Wenn wir uns an letzte Woche erinnern, so wissen wir, dass die Datenbank-Klasse genau diese Methode benutzt. Somit können wir uns schon vorstellen wie MyDatabase
aussehen könnte:
class MyDatabase implements DBAccess { public function query($sql) { // send query to database } // some more code }
Update (26.06.2011):
Die Example-Klasse ebenfalls ein Interface implementieren. Dieses gibt an, über welche Methode die Dependency gesetzt werden kann. Somit benötigen wir auch hier ein Interface. Dieses sieht dann bspw. so aus:
interface InjectDBAccess { public function setDatabase(DBAccess $db); }
/Update
Nun müssen wir nun noch die Example
-Klasse anpassen. Dazu wird der Setter so abgeändert, dass der Type Hint das Interface fordert:
class Example implements InjectDBAccess { private $db; public function __construct() { // init something } public function setDatabase(DBAccess $db) { $this->db = $db; } public function getSomething() { if ($this->db == null) { throw new Exception("database connection must not be null"); } return secretFunction($this->db->query("Select * from test")); } }
Da das Interface erfordert, dass die query
-Methode vorhanden ist – sonst würde unsere MyDatabase
-Klasse den Vertrag ja nicht erfüllen – können wir die Methode einfach aufrufen.
Vorteil:
Da man durch das Interface die Klassen entkoppelt, kann man die Klasse sehr schnell ersetzen, ohne die Type Hints anpassen zu müssen. Natürlich kann hier eine moderne IDE helfen, aber sowas ist schon eine Fehlerquelle.
Nachteil:
Man muss hier wie auch bei der Setter Injection die Klasse „von Hand“ setzen und die Nachteile aus Teil 2 sind somit immer noch präsent.