Abhängigkeitsspritze



Alles Wissen, das die Menschen im Laufe der Jahrhunderte über Abhängigkeitsspritze angesammelt haben, ist jetzt im Internet verfügbar, und wir haben es für Sie auf möglichst zugängliche Weise zusammengestellt und geordnet. Wir möchten, dass Sie schnell und effizient auf alles zugreifen können, was Sie über Abhängigkeitsspritze wissen möchten, dass Ihre Erfahrung angenehm ist und dass Sie das Gefühl haben, wirklich die Informationen über Abhängigkeitsspritze gefunden zu haben, nach denen Sie gesucht haben.

Um unsere Ziele zu erreichen, haben wir uns nicht nur bemüht, die aktuellsten, verständlichsten und wahrheitsgetreuesten Informationen über Abhängigkeitsspritze zu erhalten, sondern wir haben auch dafür gesorgt, dass das Design, die Lesbarkeit, die Ladegeschwindigkeit und die Benutzerfreundlichkeit der Seite so angenehm wie möglich sind, damit Sie sich auf das Wesentliche konzentrieren können, nämlich alle verfügbaren Daten und Informationen über Abhängigkeitsspritze zu kennen, ohne sich um irgendetwas anderes kümmern zu müssen, das haben wir bereits für Sie erledigt. Wir hoffen, dass wir unser Ziel erreicht haben und dass Sie die gewünschten Informationen über Abhängigkeitsspritze gefunden haben. Wir heißen Sie also willkommen und ermutigen Sie, die Erfahrung der Nutzung von scientiade.com weiterhin zu genießen.

In der Softwareentwicklung ist Dependency Injection eine Technik, bei der ein Objekt andere Objekte empfängt, von denen es abhängt, die als Abhängigkeiten bezeichnet werden. Typischerweise wird das empfangende Objekt als Client und das übergebene ('injizierte') Objekt als Service bezeichnet . Der Code, der den Dienst an den Client übergibt, wird als Injektor bezeichnet. Anstatt dass der Client den zu verwendenden Dienst angibt, teilt der Injektor dem Client mit, welcher Dienst zu verwenden ist. Die Injektion bezieht sich auf die Übergabe einer Abhängigkeit (einem Dienst) an den Client, der sie verwendet.

Der Dienst wird Teil des Zustands des Kunden . Die grundlegende Anforderung des Musters ist die Übergabe des Dienstes an den Client, anstatt einem Client zu erlauben , den Dienst zu erstellen oder zu finden .

Die Absicht hinter der Abhängigkeitsinjektion besteht darin, eine Trennung der Belange der Konstruktion und der Verwendung von Objekten zu erreichen. Dies kann die Lesbarkeit und die Wiederverwendung von Code erhöhen.

Abhängigkeitsinjektion ist eine Form der umfassenderen Technik der Inversion der Kontrolle . Ein Client, der einige Dienste aufrufen möchte, sollte nicht wissen müssen, wie er diese Dienste erstellt. Stattdessen delegiert der Client an externen Code (den Injektor). Der Kunde ist sich des Injektors nicht bewusst. Der Injektor übergibt die Dienste, die möglicherweise vorhanden sind oder vom Injektor selbst erstellt werden, an den Client. Der Client nutzt dann die Dienste.

Dies bedeutet, dass der Client nicht über den Injektor Bescheid wissen muss, wie er die Dienste erstellt oder sogar welche Dienste er tatsächlich verwendet. Der Client muss lediglich die Schnittstellen der Services kennen, denn diese definieren, wie der Client die Services nutzen darf. Dies trennt die Verantwortung für die Nutzung von der Verantwortung für die Konstruktion.

Überblick

Abhängigkeitsspritze für Fünfjährige

Wenn Sie selbst Dinge aus dem Kühlschrank holen, können Sie Probleme verursachen. Du könntest die Tür offen lassen, vielleicht bekommst du etwas, was Mama oder Papa nicht haben wollen. Vielleicht suchen Sie sogar nach etwas, das wir nicht einmal haben oder das abgelaufen ist.

Was Sie tun sollten, ist ein Bedürfnis zu formulieren: "Ich brauche etwas zu trinken zum Mittagessen", und dann werden wir sicherstellen, dass Sie etwas haben, wenn Sie sich zum Essen hinsetzen.

John Munsch, 28. Oktober 2009.

Dependency Injection löst die folgenden Probleme:

  • Wie kann eine Klasse unabhängig davon sein, wie die Objekte, von denen sie abhängt, erzeugt werden
  • Wie kann die Art und Weise, wie Objekte erstellt werden, in separaten Konfigurationsdateien angegeben werden
  • Wie kann eine Anwendung verschiedene Konfigurationen unterstützen

Das Erstellen von Objekten direkt innerhalb der Klasse verpflichtet die Klasse zu bestimmten Implementierungen. Dies macht es schwierig, die Instanziierung zur Laufzeit zu ändern, insbesondere in kompilierten Sprachen, bei denen das Ändern der zugrunde liegenden Objekte eine Neukompilierung des Quellcodes erforderlich machen kann.

Dependency Injection trennt die Erstellung von Abhängigkeiten eines Clients vom Verhalten des Clients, was lose gekoppelte Programme und die Prinzipien der Abhängigkeitsinversion und der Einzelverantwortung fördert . Grundsätzlich basiert die Dependency Injection auf der Übergabe von Parametern an eine Methode .

Dependency Injection ist ein Beispiel für das allgemeinere Konzept der Inversion der Steuerung .

Ein Beispiel für die Inversion der Kontrolle ohne Abhängigkeitsinjektion ist das Muster der Schablonenmethode , bei dem Polymorphismus durch Unterklassenbildung erreicht wird . Dependency Injection implementiert die Umkehrung der Kontrolle durch Komposition und ähnelt oft dem Strategiemuster . Während das Strategiemuster für Abhängigkeiten gedacht ist, die während der gesamten Lebensdauer eines Objekts austauschbar sind , kann es bei der Abhängigkeitsinjektion vorkommen, dass nur eine einzige Instanz einer Abhängigkeit verwendet wird. Dies erreicht immer noch Polymorphismus, aber durch Delegation und Komposition .

Dependency Injection steht im direkten Gegensatz zum Service-Locator-Pattern , das es Clients ermöglicht, über das System Bescheid zu wissen, das sie zum Auffinden von Abhängigkeiten verwenden.

Rollen

Die Abhängigkeitsinjektion umfasst vier Rollen:

  • die Service - Objekte verwendet werden
  • das Client- Objekt, dessen Verhalten von den genutzten Diensten abhängt
  • die Schnittstellen , die definieren, wie der Kunde die Dienste nutzen darf
  • der Injektor , der die Dienste konstruiert und in den Client einfügt

Als Analogie,

  • Service - ein Elektro-, Gas-, Hybrid- oder Dieselauto
  • Kunde - ein Fahrer, der das Auto unabhängig vom Motor gleich nutzt
  • Schnittstelle - Automatikgetriebe , das sicherstellt, dass der Fahrer die Details des Gangwechsels nicht verstehen muss
  • Injektor - der Elternteil, der das Auto für den Fahrer gekauft und entschieden hat, welche Art

Jedes Objekt, das verwendet werden kann, kann als Dienstleistung betrachtet werden . Jedes Objekt, das andere Objekte verwendet, kann als Client betrachtet werden . Die Namen beziehen sich nur auf die Rolle, die die Objekte bei einer Injektion spielen.

Die Schnittstellen sind die Typen, die der Client von seinen Abhängigkeiten erwartet. Der Client sollte nicht die spezifische Implementierung seiner Abhängigkeiten kennen, sondern nur den Namen und die API der Schnittstelle kennen . Als Ergebnis muss sich der Client nicht ändern, selbst wenn sich das, was sich hinter der Schnittstelle befindet, ändert. Dependency Injection kann mit echten Interfaces oder abstrakten Klassen, aber auch mit konkreten Services arbeiten, allerdings würde dies das Dependency Inversion-Prinzip verletzen und die dynamische Entkopplung opfern, die das Testen ermöglicht . Es ist lediglich erforderlich, dass der Client seine Schnittstellen niemals als konkret behandelt, indem er sie konstruiert oder erweitert. Wenn die Schnittstelle von einer Klasse zu einem Schnittstellentyp (oder umgekehrt) umgestaltet wird, muss der Client neu kompiliert werden. Dies ist von Bedeutung, wenn der Kunde und die Leistungen getrennt veröffentlicht werden.

Der Injektor führt dem Client Dienste ein. Oft konstruiert es auch den Client. Ein Injektor kann einen komplexen Objektgraphen verbinden, indem er dasselbe Objekt an einem Punkt sowohl als Client als auch an einem anderen als Dienst behandelt. Der Injektor selbst kann tatsächlich aus vielen Objekten bestehen, die zusammenarbeiten, aber möglicherweise nicht der Client (da dies eine zirkuläre Abhängigkeit erzeugen würde ). Der Injektor kann als Assembler, Anbieter, Container, Fabrik, Erbauer, Feder oder Konstruktionscode bezeichnet werden.

Als Disziplin verlangt Dependency Injection, dass alle Objekte Konstruktion und Verhalten trennen. Die Verwendung eines DI-Frameworks für die Konstruktion kann dazu führen, dass das newSchlüsselwort verboten oder, weniger streng, nur die direkte Konstruktion von Wertobjekten zugelassen wird .

Struktur

Ein Beispiel für ein UML-Klassen- und Sequenzdiagramm für das Dependency Injection-Entwurfsmuster.

Im obigen UML -KlassendiagrammClient erfordert die Klasse ServiceAund ServiceB, instanziiert jedoch die ServiceA1und ServiceB1-Objekte nicht direkt.

Stattdessen erstellt eine InjectorKlasse die Objekte und fügt sie in die Client. Das Clientist unabhängig davon, welche Klassen instanziiert werden.
Das rechte UML- Sequenzdiagramm zeigt die Laufzeitinteraktionen:

  1. Das InjectorObjekt erzeugt die ServiceA1und ServiceB1-Objekte.
  2. Das Injectorerstellt das ClientObjekt.
  3. Der Injectorinjiziert die ServiceA1und ServiceB1-Objekte in das ClientObjekt.

Vorteile und Nachteile

Vorteile

Ein grundlegender Vorteil der Abhängigkeitsinjektion ist die verringerte Kopplung zwischen Klassen und ihren Abhängigkeiten. Durch das Entfernen des Wissens eines Clients darüber, wie seine Abhängigkeiten implementiert sind, werden Programme wiederverwendbarer, testbarer und wartbarer.

Dies führt auch zu einer erhöhten Flexibilität: Ein Client kann auf alles reagieren, was die intrinsische Schnittstelle unterstützt, die der Client erwartet.

Viele Vorteile von Dependency Injection sind für Unit-Tests besonders relevant .

Zum Beispiel kann Dependency Injection verwendet werden, um die Konfigurationsdetails eines Systems in Konfigurationsdateien auszulagern, wodurch das System ohne Neukompilierung neu konfiguriert werden kann. Für unterschiedliche Situationen, die unterschiedliche Implementierungen von Komponenten erfordern, können separate Konfigurationen geschrieben werden. Dazu gehört auch das Testen. Da die Abhängigkeitsinjektion keine Änderung des Codeverhaltens erfordert, kann sie auch als Refactoring auf Legacy-Code angewendet werden . Das Ergebnis sind Clients, die unabhängiger sind und leichter isoliert zu testen sind, indem Stubs oder Mock-Objekte verwendet werden , die andere nicht getestete Objekte simulieren. Diese einfache Testbarkeit ist oft der erste Vorteil, der bei der Verwendung von Dependency Injection bemerkt wird.

Allgemeiner ausgedrückt reduziert Dependency Injection den Boilerplate-Code , da die gesamte Abhängigkeitserstellung von einer einzigen Komponente abgewickelt wird.

Schließlich ermöglicht Dependency Injection eine gleichzeitige Entwicklung. Zwei Entwickler können unabhängig voneinander Klassen entwickeln , die sich gegenseitig verwenden, während sie nur die Schnittstelle kennen müssen, über die die Klassen kommunizieren. Plugins werden oft von Drittanbietern entwickelt, die nie mit den Entwicklern sprechen, die das Produkt entwickelt haben, das die Plugins verwendet.

Nachteile

Kritiker der Dependency Injection argumentieren, dass sie:

  • Erstellt Clients, die Konfigurationsdetails anfordern, was bei offensichtlichen Standardeinstellungen mühsam sein kann.
  • Erschweren Sie das Nachverfolgen von Code, da er das Verhalten von der Konstruktion trennt.
  • Wird typischerweise mit Reflektion oder dynamischer Programmierung implementiert. Dies kann die IDE- Automatisierung behindern .
  • Erfordert in der Regel mehr Entwicklungsaufwand im Vorfeld.
  • Erzwingt Komplexität aus Klassen heraus und in die Verbindungen zwischen Klassen, die möglicherweise schwieriger zu verwalten sind.
  • Fördern Sie die Abhängigkeit von einem Framework.

Arten der Abhängigkeitsinjektion

Es gibt mindestens drei Möglichkeiten, wie ein Client eine Referenz auf ein externes Modul erhalten kann:

  • Konstruktorinjektion: Die Abhängigkeiten werden über den Klassenkonstruktor eines Clients bereitgestellt.
  • Setter-Injektion: Der Client macht eine Setter-Methode verfügbar, die der Injektor verwendet, um die Abhängigkeit zu injizieren.
  • Schnittstelleninjektion: Die Schnittstelle der Abhängigkeit stellt eine Injektormethode bereit, die die Abhängigkeit in jeden an sie übergebenen Client injiziert.

DI-Frameworks und Testframeworks können andere Injektionsarten verwenden.

Einige moderne Testframeworks erfordern nicht einmal, dass Clients Dependency Injection aktiv akzeptieren, wodurch Legacy-Code testbar wird. In Java kann Reflection beim Testen private Attribute öffentlich machen und somit Injektionen per Zuweisung akzeptieren.

Einige Versuche der Inversion of Control ersetzen einfach eine Form der Abhängigkeit durch eine andere. Als Faustregel gilt: Wenn ein Programmierer sich nur den Client-Code ansehen und erkennen kann, welches Framework verwendet wird, dann hat der Client eine hartcodierte Abhängigkeit vom Framework.

Ohne Abhängigkeitsinjektion

Im folgenden Java- Beispiel Cliententhält die Klasse eine im Konstruktor initialisierte Service Membervariable . Der Kunde kontrolliert, welcher Dienst verwendet wird und wie er aufgebaut ist. Der Client hat eine hartcodierte Abhängigkeit von . ExampleService

public class Client {
    // Internal reference to the service used by this client
    private ExampleService service;

    // Constructor
    Client() {
        // Specify a specific implementation instead of using dependency injection
        service = new ExampleService();
    }

    // Method that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

Wir können dieses Beispiel mit den unten beschriebenen Techniken anpassen.

Konstruktorinjektion

Diese Methode erfordert, dass der Client einen Parameter in einem Konstruktor für die Abhängigkeit bereitstellt . Dadurch wird sichergestellt, dass sich das Clientobjekt immer in einem gültigen Zustand befindet, anstatt dass einige seiner Abhängigkeiten null oder nicht festgelegt sind. Dies kann ein erster Schritt sein, um den Client unveränderlich und damit threadsicher zu machen . Für sich allein fehlt diesem Muster jedoch die Flexibilität, um seine Abhängigkeiten später ändern zu können.

// Constructor
Client(Service service, Service otherService) {
    if (service == null) {
        throw new InvalidParameterException("service must not be null");
    }
    if (otherService == null) {
        throw new InvalidParameterException("otherService must not be null");
    }

    // Save the service references inside this client
    this.service = service;
    this.otherService = otherService;
}

Setterinjektion

Diese Methode erfordert, dass der Client eine Setter-Methode für die Abhängigkeit bereitstellt . Injektoren können die Abhängigkeitsreferenzen dann jederzeit manipulieren.

Dies bietet Flexibilität, aber es ist schwierig sicherzustellen, dass alle Abhängigkeiten eingefügt werden, bevor der Client verwendet wird. Da diese Injektionen unabhängig voneinander erfolgen, kann eine Abhängigkeit einfach dadurch null bleiben, dass der Injektor seinen Setter nicht aufruft. Im Gegensatz dazu bedeutet eine in einem Konstruktor deklarierte Abhängigkeit, dass ein Objekt nicht konstruiert werden kann, wenn die Abhängigkeit nicht erfüllt ist.

Das folgende Beispiel zeigt, wie ein Client bei der Verwendung überprüfen kann, ob die Injektion abgeschlossen wurde.

// Set the service to be used by this client
public void setService(Service service) {
    this.service = service;
}

// Set the other service to be used by this client
public void setOtherService(Service otherService) {
    this.otherService = otherService;
}

// Check the service references of this client
private void validateState() {
    if (service == null) {
        throw new IllegalStateException("service must not be null");
    }
    if (otherService == null) {
        throw new IllegalStateException("otherService must not be null");
    }
}

// Method that uses the service references
public void doSomething() {
    validateState();
    service.doYourThing();
    otherService.doYourThing();
}

Schnittstelleninjektion

Mit Schnittstelleninjektion können Abhängigkeiten ihre Clients völlig ignorieren, aber dennoch Verweise auf neue Clients senden und empfangen.

Auf diese Weise werden die Abhängigkeiten zu Injektoren. Der Schlüssel ist, dass die Injektionsmethode (die nur eine Setter-Methode sein könnte) über eine Schnittstelle bereitgestellt wird.

Es wird noch ein Assembler benötigt, um den Client und seine Abhängigkeiten einzuführen. Der Assembler nimmt eine Referenz auf den Client, wandelt sie in die Setter-Schnittstelle um, die diese Abhängigkeit setzt, und übergibt sie an das Abhängigkeitsobjekt, das wiederum eine Referenz auf sich selbst an den Client zurückgibt.

Damit die Schnittstelleninjektion einen Wert hat, muss die Abhängigkeit zusätzlich zum einfachen Zurückgeben eines Verweises auf sich selbst noch etwas tun. Dies könnte als Factory oder Sub-Assembler fungieren, um andere Abhängigkeiten aufzulösen, wodurch einige Details vom Haupt-Assembler abstrahiert werden. Es könnte eine Referenzzählung sein, damit die Abhängigkeit weiß, wie viele Clients sie verwenden. Wenn die Abhängigkeit eine Sammlung von Clients verwaltet, könnte sie später alle mit einer anderen Instanz von sich selbst injizieren.

// Service setter interface.
public interface ServiceSetter {
    public void setService(Service service);
}

// Client class
public class Client implements ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

// Injector class
public class ServiceInjector {
	Set<ServiceSetter> clients;
	public void inject(ServiceSetter client) {
		clients.add(client);
		client.setService(new ServiceFoo());
	}
	public void switchToBar() {
		for (Client client : clients) {
			client.setService(new ServiceBar());
		}
	}
}

// Service classes
public class ServiceFoo implements Service {}
public class ServiceBar implements Service {}

Montage

Der einfachste Weg, Dependency Injection zu implementieren, besteht darin, Dienste und Clients manuell zu arrangieren, was normalerweise im "Root" des Programms erfolgt, wo es mit der Ausführung beginnt.

public class Injector {
    public static void main(String[] args) {
        // Build the dependencies first
        Service service = new ExampleService();

        // Inject the service, constructor style
        Client client = new Client(service);

        // Use the objects
        System.out.println(client.greet());
    }	
}

Das obige Beispiel erstellt das Objektdiagramm manuell und ruft es dann auf. Dieser Injektor ist nicht rein, da er eines der von ihm konstruierten Objekte verwendet ( Client).

Die manuelle Konstruktion kann komplexer sein und Bauarbeiter , Fabriken oder andere Konstruktionsmuster einbeziehen .

Frameworks

Die manuelle Abhängigkeitsinjektion wird zu einem Abhängigkeitsinjektions- Framework, sobald der Konstruktionscode nicht mehr an die Anwendung angepasst ist, sondern universell ist.

Anwendungsframeworks wie Weld , Spring , Guice , Play Framework , Salta , Glassfish HK2 , Dagger und Managed Extensibility Framework unterstützen Abhängigkeitsinjektion, sind aber nicht für die Abhängigkeitsinjektion erforderlich.

Frameworks wie Spring können Objekte erstellen und miteinander verbinden, bevor eine Referenz an den Client zurückgegeben wird. Da Frameworks wie Spring das Externalisieren von Assemblydetails in Konfigurationsdateien ermöglichen, können alle Erwähnungen des Konkreten ExampleServicevom Code in die Konfigurationsdaten verschoben werden:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Injector {
	public static void main(String[] args) {
		// -- Assembling objects -- //
		BeanFactory beanfactory = new ClassPathXmlApplicationContext("Beans.xml");
		Client client = (Client) beanfactory.getBean("client");

		// -- Using objects -- //
		System.out.println(client.greet());
	}
}

Selbst bei einem langen und komplexen Objektgraphen ist die einzige im Code erwähnte Klasse der Einstiegspunkt, in diesem Fall Client.

 <xml version="1.0" encoding="UTF-8">
 <beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="service" class="ExampleService">
    </bean>

    <bean id="client" class="Client">
        <constructor-arg value="service" />        
    </bean>
</beans>

Clientund Servicehaben keine Änderungen vorgenommen, um mit Spring zu arbeiten und POJOs zu bleiben . Spring kann Dienste und Clients verbinden, die seine Existenz nicht kennen. Dadurch, dass Spring-spezifische Anmerkungen und Aufrufe nicht auf viele Klassen verteilt werden, bleibt das System nur lose von Spring abhängig.

POJOs rein zu halten erfordert Anstrengung. Anstatt komplexe Konfigurationsdateien zu verwalten, können Klassen Annotationen verwenden, damit Spring die harte Arbeit mit Konvention statt Konfiguration erledigen kann .

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Injector {
	public static void main(String[] args) {
		// Assemble the objects
		BeanFactory beanfactory = new AnnotationConfigApplicationContext(MyConfiguration.class);
		Client client = beanfactory.getBean(Client.class);

		// Use the objects
		System.out.println(client.greet());
	}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan
public class MyConfiguration {
    @Bean
    public Client client(ExampleService service) {
        return new Client(service);
    }
}
@Component
public class ExampleService {
    public String getName() {
        return "World!";
    }
}

Verschiedene Injektoren (Fabriken, Service-Locator und Abhängigkeits-Injektionsbehälter) unterscheiden sich hauptsächlich darin, wo sie verwendet werden können. Das Verschieben von Aufrufen an eine Factory oder einen Service-Locator aus dem Client und in das Programmstammverzeichnis entspricht der Verwendung eines Dependency-Injection-Containers.

Durch das Entfernen des Wissens über den Injektor wird der Kunde frei von Wissen über die Außenwelt, wird zurückgelassen. Jedes Objekt, das andere Objekte verwendet, kann jedoch als Client betrachtet werden, einschließlich des Objekts, das den Programmstamm enthält. Als Ergebnis verwendet maindas obige Beispiel tatsächlich das Service-Locator-Muster. Dies lässt sich nicht vermeiden, da die Wahl der Dienstimplementierungen irgendwo getroffen werden muss.

Beispiele

AngularJS

In AngularJS gibt es nur drei Möglichkeiten, wie eine Komponente (Objekt oder Funktion) direkt auf ihre Abhängigkeiten zugreifen kann:

  1. Die Komponente kann die Abhängigkeit erstellen, normalerweise mit dem newOperator.
  2. Die Komponente kann die Abhängigkeit nachschlagen, indem sie auf eine globale Variable verweist.
  3. Der Komponente kann die Abhängigkeit dort übergeben werden, wo sie benötigt wird.

Die ersten beiden Optionen sind nicht optimal, da sie die Abhängigkeit von der Komponente fest kodieren. Dies ist bei Tests problematisch , bei denen es oft wünschenswert ist, Abhängigkeiten für die Testisolation zu simulieren.

Die dritte Option entfernt die Verantwortung für das Auffinden der Abhängigkeit von der Komponente.

function SomeClass(greeter) {
  this.greeter = greeter;
}

SomeClass.prototype.doSomething = function(name) {
  this.greeter.greet(name);
}

SomeClasswird bei der Instanziierung einfach dem Greeter übergeben. Dadurch wird dem Code, der konstruiert wird, die Verantwortung für die Abhängigkeitskonstruktion übertragen SomeClass.

Um dies zu verwalten, verfügt jede AngularJS-Anwendung über einen 'Injektor': einen Service-Locator, der für den Aufbau und das Nachschlagen von Abhängigkeiten verantwortlich ist.

// Provide the wiring information in a module
var myModule = angular.module('myModule', []);

// Teach the injector how to build a greeter service. 
// greeter is dependent on the $window service. 
// The greeter service is an object that
// contains a greet method.
myModule.factory('greeter', function($window) {
  return {
    greet: function(text) {
      $window.alert(text);
    }
  };
});

Erstellen Sie einen neuen Injektor, der im myModuleModul definierte Komponenten bereitstellen kann, und fordern Sie unseren Greeter-Service vom Injektor an.

var injector = angular.injector(['myModule', 'ng']);
var greeter = injector.get('greeter');

Das Abfragen von Abhängigkeiten löst das Problem der harten Codierung, bedeutet aber auch, dass der Injektor während der gesamten Anwendung weitergegeben werden muss. Das Passieren des Injektors bricht das Demeter-Gesetz . Um dies zu beheben, verwenden wir in unseren HTML-Templates eine deklarative Notation, um die Verantwortung für die Erstellung von Komponenten an den Injektor zu übergeben:

<div ng-controller="MyController">
  <button ng-click="sayHello()">Hello</button>
</div>
function MyController($scope, greeter) {
  $scope.sayHello = function() {
    greeter.greet('Hello World');
  };
}

Wenn AngularJS den HTML-Code kompiliert, verarbeitet es die ng-controllerDirektive, die wiederum den Injektor auffordert, eine Instanz des Controllers und seiner Abhängigkeiten zu erstellen.

injector.instantiate(MyController);

Da die ng-controllerInstanziierung der Klasse an den Injektor verschoben wird, kann er die Abhängigkeiten erfüllen, MyControllerohne dass der Controller von dem Injektor weiß. Der Anwendungscode deklariert einfach die benötigten Abhängigkeiten und behält dabei das Demeter-Gesetz bei .

C#

Beispiel für die Konstruktorinjektion , Setterinjektion und Schnittstelleninjektion in C#

using System;

namespace DependencyInjection
{
    // An interface for the library
    interface IGamepadFunctionality
    {
        String GetGamepadName();
        void SetVibrationPower(float InPower);
    }

    // Concrete implementation of the xbox controller functionality
    class XBoxGamepad : IGamepadFunctionality
    {
        readonly String GamepadName = "XBox Controller";
        float VibrationPower = 1.0f;
        public String GetGamepadName() => GamepadName;
        public void SetVibrationPower(float InPower) => VibrationPower = Math.Clamp(InPower, 0.0f, 1.0f);

    }

    // Concrete implementation of the playstation controller functionality
    class PlaystationJoystick : IGamepadFunctionality
    {
        readonly String ControllerName = "Playstation controller";
        float VibratingPower = 100.0f;
        public String GetGamepadName() => ControllerName;
        public void SetVibrationPower(float InPower) => VibratingPower = Math.Clamp(InPower * 100.0f, 0.0f, 100.0f);
    }

    // Concrete implementation of the steam controller functionality
    class SteamController : IGamepadFunctionality
    {
        readonly String JoystickName = "Steam controller";
        double Vibrating = 1.0;
        public String GetGamepadName() => JoystickName;
        public void SetVibrationPower(float InPower) => Vibrating = Convert.ToDouble(Math.Clamp(InPower, 0.0f, 1.0f));
    }

    // An interface for gamepad functionality injections
    interface IGamepadFunctionalityInjector
    {
        void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality);
    }

    class CGamepad : IGamepadFunctionalityInjector
    {
        IGamepadFunctionality _GamepadFunctionality;

        public CGamepad()
        {

        }
        // Constructor injection
        public CGamepad(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

        // Setter injection
        public void SetGamepadFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

        // Interface injection
        public void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

        public void Showcase()
        {
            String Message = String.Format("We're using the {0} right now, do you want to change the vibrating power\r\n", _GamepadFunctionality.GetGamepadName());
            Console.WriteLine(Message);
        }
    }

    enum EPlatforms: byte
    {
        Xbox,
        Playstation,
        Steam
    }

    class CGameEngine
    {
        EPlatforms _Platform;
        CGamepad _Gamepad;
        public void SetPlatform(EPlatforms InPlatform)
        {
            _Platform = InPlatform;
            switch(_Platform)
            {
                case EPlatforms.Xbox:

                    // injects dependency on XBoxGamepad class through Constructor Injection
                    _Gamepad = new CGamepad(new XBoxGamepad());
                    break;
                case EPlatforms.Playstation:
                    _Gamepad = new CGamepad();

                    // injects dependency on PlaystationJoystick class through Setter Injection
                    _Gamepad.SetGamepadFunctionality(new PlaystationJoystick());
                    break;
                case EPlatforms.Steam:
                    _Gamepad = new CGamepad();

                    // injects dependency on SteamController class through Interface Injection
                    _Gamepad.InjectFunctionality(new SteamController());
                    break;
            }

            _Gamepad.Showcase();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            
            CGameEngine Engine = new CGameEngine();

            Engine.SetPlatform(EPlatforms.Steam);

            Engine.SetPlatform(EPlatforms.Xbox);

            Engine.SetPlatform(EPlatforms.Playstation);
        }
    }
}

Siehe auch

Verweise

Externe Links

Opiniones de nuestros usuarios

Annegret Kröger

Endlich ein Artikel über Abhängigkeitsspritze, der leicht zu lesen ist.

Wolfgang Bock

Große Entdeckung dieser Artikel über Abhängigkeitsspritze und die ganze Seite. Es geht direkt zu den Favoriten