it-swarm.com.de

Grundlegendes zu IoC-Containern und Abhängigkeitsinjektion

Schnellvorlauf:

Ich schreibe dies mit der Absicht, ein besseres Verständnis der Abhängigkeitsinjektion und der IoC-Container zu erlangen, aber auch, damit ich anschließend die Fehler darin korrigieren und einige meiner Freunde darüber informieren kann.

Ab sofort habe ich versucht, die Dokumentation für verschiedene Frameworks (Laravel, Fuel, Codeigniter, Symfony) durchzulesen. Dabei stellte ich fest, dass es zu viele verschiedene Aspekte der Frameworks gab, die ich brauchte, um mich damit vertraut zu machen Lernen Sie einfach jedes der Hauptstücke einzeln, bevor Sie versuchen, sie in den Frameworks selbst zu verwenden.

Ich habe stundenlang verschiedene Bedeutungen gegoogelt, Stapelüberlauf-Antworten durchgesehen und verschiedene Artikel gelesen, um zu verstehen, was eine IoC ist und wie man sie verwendet, um Abhängigkeiten richtig zu verwalten, und ich glaube zu verstehen, was sie im Konzept sind, aber ich bin immer noch grau wie man es richtig umsetzt. Ich denke, jemand, der dies liest, kann mir am besten helfen, wenn er mein derzeitiges Verständnis der IoC-Container und der Abhängigkeitsinjektion angibt und dann Personen, die ein besseres Verständnis haben als ich, aufzeigt, wo mein verständnis fehlt.

Mein Verständnis:

  • Eine Abhängigkeit liegt vor, wenn eine Instanz von ClassA eine Instanz von ClassB benötigt, um eine neue Instanz von ClassA zu instanziieren.
  • Eine Abhängigkeitsinjektion liegt vor, wenn ClassA eine Instanz von ClassB entweder über einen Parameter im Konstruktor von ClassA oder über eine Funktion set ~ DependencyNameHere ~ (~ DependencyNameHere ~ $ param) übergeben wird. (Dies ist einer der Bereiche, in denen ich mir nicht ganz sicher bin).
  • Ein IoC-Container ist eine Singleton-Klasse (es kann jeweils nur eine Instanz instanziiert werden), in der die spezifische Art der Instanziierung von Objekten dieser Klasse für dieses Projekt registriert werden kann. Hier ist ein Link zu einem Beispiel dessen, was ich versuche, zusammen mit der Klassendefinition für den IoC-Container, den ich verwendet habe

An diesem Punkt versuche ich, den IoC-Container für kompliziertere Szenarien zu verwenden. Aus heutiger Sicht ist die Verwendung des IoC-Containers auf eine Has-A-Beziehung für praktisch jede Klasse beschränkt, die ich erstellen möchte und deren Abhängigkeiten im IoC-Container definiert werden sollen. Was ist, wenn ich eine Klasse erstellen möchte, die eine Klasse erbt, aber nur, wenn die übergeordnete Klasse auf eine bestimmte Weise erstellt wurde und im IoC-Container registriert wurde?.

Beispiel: Ich möchte eine untergeordnete Klasse von mysqli erstellen, diese Klasse jedoch im IoC-Container registrieren, um nur mit der übergeordneten Klasse zu instantiieren, die so erstellt wurde, wie ich sie zuvor im IoC-Container registriert habe. Ich kann mir keine Möglichkeit vorstellen, dies zu tun, ohne Code zu duplizieren (und da dies ein Lernprojekt ist, versuche ich, es so rein wie möglich zu halten). Hier sind einige weitere Beispiele für das, was ich zu beschreiben versuche.

Hier sind einige meiner Fragen:

  • Ist das, was ich versuche, möglich, ohne ein Prinzip von OOP zu brechen? Ich weiß, dass ich in c ++ dynamischen Speicher und einen Kopierkonstruktor verwenden könnte, um dies zu erreichen, aber ich konnte diese Art von Funktionalität in PHP nicht finden. (Ich gebe zu, dass ich nur sehr wenig Erfahrung mit einer der anderen magischen Methoden außer __construct habe, aber wenn ich das richtig verstanden habe, konnte ich sie im Konstruktor nicht verwenden, um die zu instanziierende Child-Klasse zu einem Klon einer zu machen Instanz der Elternklasse).
  • Wohin sollten alle meine Definitionen von Abhängigkeitsklassen in Bezug auf die IoC gehen? (Sollte meine IoC.php oben nur eine Reihe von require_once ('dependencyClassDefinition.php') haben? Meine Bauchreaktion ist, dass es einen besseren Weg gibt, aber ich habe noch keinen gefunden.)
  • In welcher Datei soll ich meine Objekte registrieren? Derzeit werden alle Aufrufe von IoC :: register () in der Datei IoC.php nach der Klassendefinition ausgeführt.
  • Muss ich eine Abhängigkeit in der IoC registrieren, bevor ich eine Klasse registriere, die diese Abhängigkeit benötigt? Da ich die anonyme Funktion erst dann aufrufe, wenn ich tatsächlich ein im IoC registriertes Objekt instanziiere, gehe ich davon aus, dass dies kein Problem darstellt.
  • Gibt es noch etwas, das ich übersehen habe und das ich tun oder benutzen sollte? Ich versuche, es Schritt für Schritt zu machen, aber ich möchte auch nicht wissen, dass mein Code wiederverwendbar ist und vor allem, dass jemand, der nichts über mein Projekt weiß, es lesen und verstehen kann.

Ich weiß, dass dies extrem lang ist, und wollte mich nur im Voraus bei allen bedanken, die sich die Zeit genommen haben, es zu lesen, und vor allem bei allen, die ihr Wissen teilen.

56
echochamber

Einfach ausgedrückt (da es kein Problem ist, das nur auf die OOP Welt beschränkt ist), ist eine Abhängigkeit eine Situation, in der Komponente A benötigt (abhängig von) Komponente B, um das zu tun, was es tun soll. Das Wort wird auch verwendet, um die abhängige Komponente in diesem Szenario zu beschreiben. Um dies in OOP/PHP-Ausdrücken auszudrücken, betrachten Sie das folgende Beispiel mit der obligatorischen Auto-Analogie:

class Car {

    public function start() {
        $engine = new Engine();
        $engine->vroom();
    }

}

Car hängt ab von Engine. Engine ist die Abhängigkeit von Car. Dieser Code ist allerdings ziemlich schlecht, weil:

  • die Abhängigkeit ist implizit; Sie wissen nicht, dass es dort ist, bis Sie den Code von Car überprüfen
  • die Klassen sind eng miteinander verbunden. Sie können Engine nicht durch MockEngine zu Testzwecken ersetzen, oder TurboEngine, das das Original erweitert, ohne das Car zu ändern.
  • Es sieht irgendwie albern aus, wenn ein Auto in der Lage ist, einen Motor für sich selbst zu bauen, oder?

Die Abhängigkeitsinjektion ist eine Möglichkeit, all diese Probleme zu lösen, indem die Tatsache, dass CarEngine benötigt, explizit und explizit bereitgestellt wird es mit einem:

class Car {

    protected $engine;

    public function __construct(Engine $engine) {
        $this->engine = $engine;
    }

    public function start() {
        $this->engine->vroom();
    }

}

$engine = new SuperDuperTurboEnginePlus(); // a subclass of Engine
$car = new Car($engine);

Das Obige ist ein Beispiel für die Konstruktorinjektion , bei der die Abhängigkeit (das abhängige Objekt) dem abhängigen (Konsumenten) über den Klassenkonstruktor bereitgestellt wird . Eine andere Möglichkeit wäre, eine setEngine -Methode in der Car -Klasse verfügbar zu machen und damit eine Instanz von Engine zu injizieren. Dies wird als Setter-Injection bezeichnet und ist vor allem für Abhängigkeiten nützlich, die zur Laufzeit ausgetauscht werden sollen.

Jedes nicht triviale Projekt besteht aus einer Reihe von voneinander abhängigen Komponenten, und es kann leicht passieren, dass man schnell den Überblick verliert, was wo eingespritzt wird. Ein Abhängigkeitsinjektionscontainer ist ein Objekt, das weiß, wie andere Objekte instanziiert und konfiguriert werden, welche Beziehung sie zu anderen Objekten im Projekt haben und welche Abhängigkeit sie haben spritze für dich. Auf diese Weise können Sie die Verwaltung aller (Inter-) Abhängigkeiten Ihres Projekts zentralisieren und, was noch wichtiger ist, eine oder mehrere davon ändern/verspotten, ohne eine Reihe von Stellen in Ihrem Code bearbeiten zu müssen.

Lassen Sie uns die Auto-Analogie fallen lassen und als Beispiel betrachten, was OP zu erreichen versucht. Nehmen wir an, wir haben ein Database -Objekt, das vom mysqli -Objekt abhängt. Angenommen, wir möchten eine wirklich primitive Abhängigkeitsindektionscontainerklasse DIC verwenden, die zwei Methoden verfügbar macht: register($name, $callback), um eine Methode zum Erstellen eines Objekts unter dem angegebenen Namen und resolve($name) zu registrieren Holen Sie sich das Objekt von diesem Namen. Unser Container-Setup würde ungefähr so ​​aussehen:

$dic = new DIC();
$dic->register('mysqli', function() {
    return new mysqli('somehost','username','password');
});
$dic->register('database', function() use($dic) {
    return new Database($dic->resolve('mysqli'));
});

Beachten Sie, dass wir unseren Container anweisen, eine Instanz von mysqli von sich selbst zu holen, um eine Instanz von Database zusammenzustellen. Um dann eine Database -Instanz mit ihrer Abhängigkeit automatisch einzufügen, würden wir einfach:

$database = $dic->resolve('database');

Das ist der Kern der Sache. Ein etwas ausgefeilterer, aber dennoch relativ einfacher und leicht zu erfassender PHP DI/IoC-Container ist Pimple . Weitere Beispiele finden Sie in der Dokumentation.


Bezüglich des OP-Codes und der Fragen:

  • Verwenden Sie keine statische Klasse oder einen Singleton für Ihren Container (oder für irgendetwas anderes). sie sind beide böse . Schauen Sie sich stattdessen Pimple an.
  • Entscheiden Sie, ob Sie Ihre Klasse mysqliWrapper extendmysql oder depend davon abhängig machen möchten.
  • Wenn Sie IoC aus mysqliWrapper aufrufen, tauschen Sie eine Abhängigkeit gegen eine andere aus. Ihre Objekte sollten den Container nicht kennen oder benutzen. Ansonsten ist es kein DIC mehr, sondern ein Service Locator (Anti) Muster.
  • Sie müssen keine Klassendatei require erstellen, bevor Sie sie im Container registrieren, da Sie nicht wissen, ob Sie überhaupt ein Objekt dieser Klasse verwenden werden. Stellen Sie alle Container an einem Ort auf. Wenn Sie keinen Autoloader verwenden, können Sie in der anonymen Funktion, die Sie für den Container registrieren, require eingeben.

Zusätzliche Ressourcen:

120
lafor