Suche…


Einführung

Abhängigkeitsinjektion (DI) ist ein ausgefallener Begriff für "Weitergeben" . Alles was es wirklich bedeutet, besteht darin, die Abhängigkeiten eines Objekts über den Konstruktor und / oder Setter zu übergeben, anstatt sie bei der Objekterstellung im Objekt zu erstellen. Abhängigkeitsinjektion kann sich auch auf Abhängigkeitsinjektionsbehälter beziehen, die die Konstruktion und Injektion automatisieren.

Konstruktorinjektion

Objekte hängen oft von anderen Objekten ab. Anstatt die Abhängigkeit im Konstruktor zu erstellen, sollte die Abhängigkeit als Parameter an den Konstruktor übergeben werden. Dies stellt sicher, dass keine enge Kopplung zwischen den Objekten besteht, und ermöglicht die Änderung der Abhängigkeit von der Klasseninstanziierung. Dies hat eine Reihe von Vorteilen, einschließlich der Erleichterung des Lesens von Code durch explizite Abhängigkeiten sowie des Testens, da die Abhängigkeiten leichter ausgetauscht und simuliert werden können.

Im folgenden Beispiel hängt Component von einer Instanz von Logger , erstellt jedoch keine Instanz. Es muss stattdessen eines als Argument an den Konstruktor übergeben werden.

interface Logger {
    public function log(string $message);
}

class Component {
    private $logger;

    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }
}

Ohne Abhängigkeitsinjektion würde der Code wahrscheinlich ähnlich aussehen:

class Component {
    private $logger;

    public function __construct() {
        $this->logger = new FooLogger();
    }
}

Die Verwendung von new zum Erstellen neuer Objekte im Konstruktor zeigt an, dass die Abhängigkeitsinjektion nicht verwendet wurde (oder unvollständig verwendet wurde) und dass der Code eng gekoppelt ist. Es ist auch ein Zeichen dafür, dass der Code unvollständig getestet wurde oder spröde Tests enthält, die falsche Annahmen über den Programmstatus treffen.

In dem obigen Beispiel, in dem wir stattdessen die Abhängigkeitseinspritzung verwenden, können wir leicht zu einem anderen Logger wechseln, wenn dies erforderlich wäre. Beispielsweise können wir eine Logger-Implementierung verwenden, die an einem anderen Speicherort protokolliert oder ein anderes Protokollierungsformat verwendet oder die in der Datenbank statt in einer Datei protokolliert.

Setter-Injektion

Abhängigkeiten können auch von Setters eingefügt werden.

interface Logger {
    public function log($message);
}

class Component {
    private $logger;
    private $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }

    public function core() {
        $this->logSave();    
        return $this->databaseConnection->save($this);
    }

    public function logSave() {
         if ($this->logger) {
            $this->logger->log('saving');
        }
    }
}

Dies ist besonders interessant, wenn die Kernfunktionalität der Klasse nicht auf die Abhängigkeit von der Arbeit angewiesen ist.

Hier ist die einzige notwendige Abhängigkeit die DatabaseConnection also im Konstruktor. Die Logger Abhängigkeit ist optional und muss daher nicht Teil des Konstruktors sein, wodurch die Verwendung der Klasse vereinfacht wird.

Beachten Sie, dass es bei Verwendung der Setterinjektion besser ist, die Funktionalität zu erweitern, anstatt sie zu ersetzen. Wenn Sie eine Abhängigkeit festlegen, gibt es nichts, was bestätigt, dass sich die Abhängigkeit irgendwann nicht ändert, was zu unerwarteten Ergebnissen führen kann. Zum Beispiel kann ein FileLogger kann zunächst festgelegt werden, und dann ein MailLogger könnte eingestellt werden. Dies unterbricht die Kapselung und macht es schwer, Protokolle zu finden, da wir die Abhängigkeit ersetzen .

Um dies zu verhindern, sollten wir eine Abhängigkeit mit der Setterinjektion hinzufügen , z.

interface Logger {
    public function log($message);
}

class Component {
    private $loggers = array();
    private $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function addLogger(Logger $logger) {
        $this->loggers[] = $logger;
    }

    public function core() {
        $this->logSave();
        return $this->databaseConnection->save($this);
    }

    public function logSave() {
        foreach ($this->loggers as $logger) {
            $logger->log('saving');
        }
    }
}

Wenn wir also die Kernfunktionalität verwenden, wird diese Funktion auch dann nicht beschädigt, wenn keine Protokollabhängigkeit hinzugefügt wird. Jeder hinzugefügte Logger wird auch dann verwendet, wenn ein anderer Logger hinzugefügt wurde. Wir erweitern die Funktionalität, anstatt sie zu ersetzen .

Behälterinjektion

Abhängigkeitseinspritzung (Dependency Injection, DI) im Zusammenhang mit der Verwendung eines Abhängigkeitsinjektionscontainers (DIC) kann als Obermenge der Konstruktorinjektion angesehen werden. Ein DIC analysiert in der Regel die Typenhintergründe eines Klassenkonstruktors und befriedigt deren Anforderungen, wodurch die für die Instanzausführung erforderlichen Abhängigkeiten effektiv eingefügt werden.

Die genaue Implementierung geht weit über den Rahmen dieses Dokuments hinaus, aber im Grunde setzt ein DIC die Signatur einer Klasse voraus ...

namespace Documentation;

class Example
{
    private $meaning;

    public function __construct(Meaning $meaning)
    {
        $this->meaning = $meaning;
    }
}

... um es automatisch zu instanziieren, wobei die meiste Zeit von einem automatischen Ladesystem abhängig ist .

// older PHP versions
$container->make('Documentation\Example');

// since PHP 5.5
$container->make(\Documentation\Example::class);

Wenn Sie PHP mindestens in Version 5.5 verwenden und einen Namen einer Klasse in der oben gezeigten Weise erhalten möchten, ist der zweite Ansatz der richtige Weg. Auf diese Weise können Sie mithilfe moderner IDEs schnell Verwendungen der Klasse finden, was Ihnen beim möglichen Refactoring sehr helfen wird. Sie möchten sich nicht auf reguläre Zeichenfolgen verlassen.

In diesem Fall weiß die Documentation\Example dass sie eine Meaning , und eine DIC würde wiederum einen Meaning instanziieren. Die konkrete Implementierung muss nicht von der Verbraucherinstanz abhängen.

Stattdessen setzen wir im Container vor der Objekterstellung Regeln, die angeben, wie bestimmte Typen bei Bedarf instanziiert werden sollen.

Dies hat eine Reihe von Vorteilen, die ein DIC bieten kann

  • Teilen Sie gemeinsame Instanzen
  • Stellen Sie eine Factory bereit, um eine Typensignatur aufzulösen
  • Auflösen einer Schnittstellensignatur

Wenn wir Regeln definieren, wie bestimmte Typen verwaltet werden müssen, können wir eine genaue Kontrolle darüber haben, welche Typen gemeinsam genutzt, instanziiert oder aus einer Fabrik erstellt werden.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow