Häufig gestellte Fragen zu DI (FAQ)

Ist DI ein anderer Name für IoC?

Die Inversion of Control (IoC) ist ein Prinzip, das sich auf die Art und Weise konzentriert, wie Code ausgeführt wird – ob Ihr Code externen Code initiiert oder Ihr Code in externen Code integriert ist, der ihn dann aufruft. IoC ist ein umfassendes Konzept, das Ereignisse, das so genannte Hollywood-Prinzip und andere Aspekte einschließt. Fabriken, die Teil von Regel Nr. 3: Let the Factory Handle It sind und eine Umkehrung des new -Operators darstellen, sind ebenfalls Bestandteil dieses Konzepts.

Bei der Dependency Injection (DI) geht es darum, wie ein Objekt über ein anderes Objekt Bescheid weiß, d.h. um die Abhängigkeit. Es handelt sich um ein Entwurfsmuster, das die explizite Weitergabe von Abhängigkeiten zwischen Objekten erfordert.

Somit kann DI als eine spezielle Form von IoC bezeichnet werden. Allerdings sind nicht alle Formen von IoC im Hinblick auf die Reinheit des Codes geeignet. Zu den Anti-Patterns gehören zum Beispiel alle Techniken, die mit einem globalen Zustand oder dem sogenannten Service Locator arbeiten.

Was ist ein Service Locator?

Ein Service Locator ist eine Alternative zur Dependency Injection. Dabei wird ein zentraler Speicher angelegt, in dem alle verfügbaren Dienste oder Abhängigkeiten registriert werden. Wenn ein Objekt eine Abhängigkeit benötigt, fordert es diese beim Service Locator an.

Im Vergleich zu Dependency Injection verliert es jedoch an Transparenz: Abhängigkeiten werden nicht direkt an Objekte weitergegeben und sind daher nicht leicht zu erkennen, was eine Untersuchung des Codes erfordert, um alle Verbindungen aufzudecken und zu verstehen. Auch das Testen ist komplizierter, da wir nicht einfach Mock-Objekte an die getesteten Objekte übergeben können, sondern den Weg über den Service Locator gehen müssen. Außerdem stört der Service Locator das Design des Codes, da die einzelnen Objekte von seiner Existenz wissen müssen, was sich von der Dependency Injection unterscheidet, bei der die Objekte keine Kenntnis vom DI-Container haben.

Wann ist es besser, DI nicht zu verwenden?

Es sind keine Schwierigkeiten bekannt, die mit der Verwendung des Dependency Injection Design Pattern verbunden sind. Im Gegenteil, die Beschaffung von Abhängigkeiten von global zugänglichen Stellen führt zu einer Reihe von Komplikationen, ebenso wie die Verwendung eines Service Locators. Daher ist es ratsam, immer DI zu verwenden. Dies ist kein dogmatischer Ansatz, aber es wurde einfach keine bessere Alternative gefunden.

Es gibt jedoch bestimmte Situationen, in denen wir Objekte nicht aneinander übergeben und sie aus dem globalen Raum beziehen. Zum Beispiel beim Debuggen von Code, wenn man einen Variablenwert an einem bestimmten Punkt im Programm ausgeben, die Dauer eines bestimmten Teils des Programms messen oder eine Meldung protokollieren muss. In solchen Fällen, in denen es sich um temporäre Aktionen handelt, die später aus dem Code entfernt werden, ist es legitim, einen global zugänglichen Dumper, eine Stoppuhr oder einen Logger zu verwenden. Diese Werkzeuge gehören schließlich nicht zum Design des Codes.

Hat die Verwendung von DI auch Nachteile?

Bringt die Verwendung von Dependency Injection irgendwelche Nachteile mit sich, wie z. B. eine höhere Komplexität beim Schreiben von Code oder eine schlechtere Leistung? Was verlieren wir, wenn wir anfangen, Code in Übereinstimmung mit DI zu schreiben?

DI hat keinen Einfluss auf die Anwendungsleistung oder den Speicherbedarf. Die Leistung des DI-Containers kann eine Rolle spielen, aber im Fall von Nette DI wird der Container in reines PHP kompiliert, so dass sein Overhead während der Laufzeit der Anwendung im Wesentlichen gleich Null ist.

Beim Schreiben von Code ist es notwendig, Konstruktoren zu erstellen, die Abhängigkeiten akzeptieren. In der Vergangenheit konnte dies zeitaufwändig sein, aber dank moderner IDEs und der Förderung von Konstruktoreigenschaften ist dies nun eine Sache von wenigen Sekunden. Factories lassen sich mit Nette DI und einem PhpStorm-Plugin mit wenigen Klicks erzeugen. Andererseits besteht keine Notwendigkeit, Singletons und statische Zugriffspunkte zu schreiben.

Daraus lässt sich schließen, dass eine richtig konzipierte Anwendung mit DI im Vergleich zu einer Anwendung mit Singletons weder kürzer noch länger ist. Teile des Codes, die mit Abhängigkeiten arbeiten, werden einfach aus den einzelnen Klassen extrahiert und an neue Stellen verschoben, d.h. in den DI-Container und die Fabriken.

Wie schreibt man eine Legacy-Anwendung auf DI um?

Die Umstellung einer Legacy-Anwendung auf Dependency Injection kann ein schwieriger Prozess sein, insbesondere bei großen und komplexen Anwendungen. Es ist wichtig, diesen Prozess systematisch anzugehen.

  • Bei der Umstellung auf Dependency Injection ist es wichtig, dass alle Teammitglieder die Prinzipien und Praktiken verstehen, die verwendet werden.
  • Führen Sie zunächst eine Analyse der bestehenden Anwendung durch, um die wichtigsten Komponenten und ihre Abhängigkeiten zu ermitteln. Erstellen Sie einen Plan, welche Teile in welcher Reihenfolge refaktorisiert werden sollen.
  • Implementieren Sie einen DI-Container oder, noch besser, verwenden Sie eine vorhandene Bibliothek wie Nette DI.
  • Schrittweise Umstrukturierung jedes Teils der Anwendung, um Dependency Injection zu verwenden. Dies kann die Änderung von Konstruktoren oder Methoden beinhalten, um Abhängigkeiten als Parameter zu akzeptieren.
  • Ändern Sie die Stellen im Code, an denen Abhängigkeitsobjekte erstellt werden, so dass die Abhängigkeiten stattdessen vom Container injiziert werden. Dies kann die Verwendung von Fabriken beinhalten.

Denken Sie daran, dass der Wechsel zu Dependency Injection eine Investition in die Codequalität und die langfristige Nachhaltigkeit der Anwendung ist. Auch wenn es eine Herausforderung sein mag, diese Änderungen vorzunehmen, sollte das Ergebnis ein sauberer, modularer und leicht testbarer Code sein, der für zukünftige Erweiterungen und Wartung bereit ist.

Warum ist Komposition der Vererbung vorzuziehen?

Die Komposition ist der Vererbung vorzuziehen, da sie die Wiederverwendung von Code ermöglicht, ohne dass man sich über die Folgen von Änderungen Gedanken machen muss. Sie bietet also eine lockerere Kopplung, bei der wir uns keine Sorgen machen müssen, dass die Änderung eines Codes die Notwendigkeit mit sich bringt, anderen abhängigen Code zu ändern. Ein typisches Beispiel ist eine Situation, die als Konstruktorhölle bezeichnet wird.

Kann Nette DI Container auch außerhalb von Nette verwendet werden?

Auf jeden Fall. Der Nette DI Container ist Teil von Nette, aber er ist als eigenständige Bibliothek konzipiert, die unabhängig von anderen Teilen des Frameworks verwendet werden kann. Installieren Sie ihn einfach mit Composer, erstellen Sie eine Konfigurationsdatei, die Ihre Dienste definiert, und verwenden Sie dann ein paar Zeilen PHP-Code, um den DI-Container zu erstellen. Und schon können Sie damit beginnen, die Vorteile der Dependency Injection in Ihren Projekten zu nutzen.

Das Kapitel Nette DI Container beschreibt, wie ein spezifischer Anwendungsfall aussieht, einschließlich des Codes.

Warum ist die Konfiguration in NEON-Dateien?

NEON ist eine einfache und leicht lesbare Konfigurationssprache, die innerhalb von Nette entwickelt wurde, um Anwendungen, Dienste und deren Abhängigkeiten einzurichten. Im Vergleich zu JSON oder YAML bietet sie für diesen Zweck wesentlich intuitivere und flexiblere Möglichkeiten. In NEON lassen sich auf natürliche Weise Bindungen beschreiben, die in Symfony & YAML entweder gar nicht oder nur durch eine komplexe Beschreibung möglich wären.

Verlangsamt das Parsen von NEON-Dateien die Anwendung?

Obwohl NEON-Dateien sehr schnell geparst werden, ist dieser Aspekt nicht wirklich von Bedeutung. Der Grund dafür ist, dass das Parsen von Dateien nur einmal beim ersten Start der Anwendung erfolgt. Danach wird der DI-Container-Code generiert, auf der Festplatte gespeichert und bei jeder nachfolgenden Anforderung ausgeführt, ohne dass ein weiteres Parsen erforderlich ist.

So funktioniert es auch in einer Produktionsumgebung. Während der Entwicklung werden die NEON-Dateien jedes Mal geparst, wenn sich ihr Inhalt ändert, so dass der Entwickler immer über einen aktuellen DI-Container verfügt. Wie bereits erwähnt, ist das eigentliche Parsen eine Sache von einem Augenblick.

Wie greife ich in meiner Klasse auf die Parameter aus der Konfigurationsdatei zu?

Denken Sie an Regel Nr. 1: Lass es dir übergeben. Wenn eine Klasse Informationen aus einer Konfigurationsdatei benötigt, brauchen wir nicht herauszufinden, wie wir auf diese Informationen zugreifen können; stattdessen fragen wir sie einfach ab – zum Beispiel über den Klassenkonstruktor. Und wir führen die Übergabe in der Konfigurationsdatei durch.

In diesem Beispiel ist %myParameter% ein Platzhalter für den Wert des Parameters myParameter, der an den Konstruktor MyClass übergeben wird:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Wenn Sie mehrere Parameter übergeben oder Autowiring verwenden wollen, ist es sinnvoll, die Parameter in ein Objekt zu verpacken.

Unterstützt Nette die PSR-11 Container-Schnittstelle?

Nette DI Container unterstützt PSR-11 nicht direkt. Wenn Sie jedoch Interoperabilität zwischen dem Nette DI Container und Bibliotheken oder Frameworks benötigen, die das PSR-11 Container Interface erwarten, können Sie einen einfachen Adapter erstellen, der als Brücke zwischen dem Nette DI Container und PSR-11 dient.

Version: 3.x