Antworten:
DI und Strategie funktionieren auf die gleiche Weise, aber Strategie wird für feinkörnigere und kurzlebigere Abhängigkeiten verwendet.
Wenn ein Objekt mit einer "festen" Strategie konfiguriert ist, z. B. wenn das Objekt erstellt wird, verwischt die Unterscheidung zwischen Strategie und DI. In einem DI-Szenario ist es jedoch ungewöhnlicher, dass sich die Abhängigkeiten von Objekten während ihrer Lebensdauer ändern, während dies bei Strategy nicht ungewöhnlich ist.
Sie können Strategien auch als Argumente an Methoden übergeben, während das verwandte Konzept der Injektion von Methodenargumenten nicht weit verbreitet ist und hauptsächlich nur im Zusammenhang mit automatisierten Tests verwendet wird.
Die Strategie konzentriert sich auf die Absicht und ermutigt Sie, eine Schnittstelle mit verschiedenen Implementierungen zu erstellen, die demselben Verhaltensvertrag entsprechen. Bei DI geht es mehr darum, nur ein bestimmtes Verhalten zu implementieren und bereitzustellen.
Mit DI können Sie Ihr Programm aus anderen Gründen zerlegen, als nur um Teile der Implementierung auszutauschen. Eine in DI verwendete Schnittstelle mit nur einer Implementierung ist sehr verbreitet. Eine "Strategie" mit nur einer konkreten Implementierung (jemals) ist kein wirkliches Problem, liegt aber wahrscheinlich näher an DI.
in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
Der Unterschied ist, was sie erreichen wollen. Das Strategiemuster wird in Situationen verwendet, in denen Sie wissen, dass Sie Implementierungen austauschen möchten. Beispielsweise möchten Sie Daten möglicherweise auf verschiedene Arten formatieren. Sie können das Strategiemuster verwenden, um einen XML- oder CSV-Formatierer usw. auszutauschen.
Die Abhängigkeitsinjektion unterscheidet sich darin, dass der Benutzer nicht versucht, das Laufzeitverhalten zu ändern. Nach dem obigen Beispiel erstellen wir möglicherweise ein XML-Exportprogramm, das einen XML-Formatierer verwendet. Anstatt den Code wie folgt zu strukturieren:
public class DataExporter() {
XMLFormatter formatter = new XMLFormatter();
}
Sie würden den Formatierer in den Konstruktor "einfügen":
public class DataExporter {
IFormatter formatter = null;
public DataExporter(IDataFormatter dataFormatter) {
this.formatter = dataFormatter;
}
}
DataExporter exporter = new DataExporter(new XMLFormatter());
Es gibt einige Gründe für die Abhängigkeitsinjektion, aber die wichtigste ist das Testen. Möglicherweise haben Sie eine Persistenz-Engine (z. B. eine Datenbank). Es kann jedoch schwierig sein, eine echte Datenbank zu verwenden, wenn Sie Tests wiederholt ausführen. Für Ihre Testfälle würden Sie also eine Dummy-Datenbank einfügen, damit Ihnen dieser Aufwand nicht entsteht.
Anhand dieses Beispiels können Sie den Unterschied erkennen: Wir planen immer die Verwendung einer Datenspeicherungsstrategie, die wir übergeben (die echte DB-Instanz). Bei der Entwicklung und beim Testen möchten wir jedoch unterschiedliche Abhängigkeiten verwenden, sodass wir unterschiedliche Konkretionen einfügen.
Sie können DI als Strategiemuster verwenden, um den für jeden Kunden erforderlichen Algorithmus auszutauschen. DI kann jedoch darüber hinausgehen, da nur die Teile einer Anwendung entkoppelt werden können, die nicht Teil davon sind das Strategiemuster.
Es wäre riskant zu sagen, dass DI nur ein umbenanntes Strategiemuster ist, da dies anfängt, das zu verwässern, wofür das Strategiemuster wirklich ist, IMO.
Alter, die Abhängigkeitsinjektion ist ein allgemeineres Muster, und es geht um die Abhängigkeit von Abstraktionen, nicht um Konkretionen, und es ist Teil jedes Musters, aber das Strategiemuster ist eine Lösung für ein spezifischeres Problem
Dies ist die Definition aus Wikipedia:
DI:
Die Abhängigkeitsinjektion (DI) in der objektorientierten Computerprogrammierung ist ein Entwurfsmuster mit einem Kernprinzip der Trennung von Verhalten und Abhängigkeitsauflösung. Mit anderen Worten: Eine Technik zum Entkoppeln stark abhängiger Softwarekomponenten.
Strategiemuster:
Bei der Computerprogrammierung ist das Strategiemuster (auch als Richtlinienmuster bekannt) ein bestimmtes Software-Entwurfsmuster, wobei Algorithmen zur Laufzeit ausgewählt werden können.
Das Strategiemuster soll ein Mittel bereitstellen, um eine Familie von Algorithmen zu definieren, jeden als Objekt zu kapseln und sie austauschbar zu machen. Durch das Strategiemuster können die Algorithmen unabhängig von den Clients variieren, die sie verwenden.
Strategien sind übergeordnete Dinge, die verwendet werden, um die Art und Weise zu ändern, wie Dinge berechnet werden. Mit der Abhängigkeitsinjektion können Sie nicht nur die Art und Weise ändern, wie Dinge berechnet werden, sondern auch, was vorhanden ist.
Für mich wird es bei Unit-Tests deutlich. Für die Ausführung des Produktionscodes sind alle Daten ausgeblendet (dh privat oder geschützt). Bei Unit-Tests sind die meisten Daten öffentlich, sodass ich sie mit den Asserts betrachten kann.
Beispiel einer Strategie:
public class Cosine {
private CalcStrategy strat;
// Constructor - strategy passed in as a type of DI
public Cosine(CalcStrategy s) {
strat = s;
}
}
public abstract class CalcStrategy {
public double goFigure(double angle);
}
public class RadianStrategy extends CalcStrategy {
public double goFigure(double angle) {
return (...);
}
}
public class DegreeStrategy extends CalcStrategy {
public double goFigure(double angle) {
return (...);
}
}
Beachten Sie, dass es keine öffentlichen Daten gibt, die sich zwischen den Strategien unterscheiden. Es gibt auch keine anderen Methoden. Beide Strategien haben dieselben Funktionen und Signaturen.
Nun zur Abhängigkeitsinjektion:
public class Cosine {
private Calc strat;
// Constructor - Dependency Injection.
public Cosine(Calc s) {
strat = s;
}
}
public class Calc {
private int numPasses = 0;
private double total = 0;
private double intermediate = 0;
public double goFigure(double angle) {
return(...);
}
public class CalcTestDouble extends Calc {
// NOTICE THE PUBLIC DATA.
public int numPasses = 0;
public double total = 0;
public double intermediate = 0;
public double goFigure(double angle) {
return (...);
}
}
Verwenden:
public CosineTest {
@Test
public void testGoFigure() {
// Setup
CalcTestDouble calc = new CalcTestDouble();
Cosine instance = new Cosine(calc);
// Exercise
double actualAnswer = instance.goFigure(0.0);
// Verify
double tolerance = ...;
double expectedAnswer = ...;
assertEquals("GoFigure didn't work!", expectedAnswer,
actualAnswer, tolerance);
int expectedNumPasses = ...;
assertEquals("GoFigure had wrong number passes!",
expectedNumPasses, calc.numPasses);
double expectedIntermediate = ...;
assertEquals("GoFigure had wrong intermediate values!",
expectedIntermediate, calc.intermediate, tolerance);
}
}
Beachten Sie die letzten 2 Prüfungen. Sie verwendeten die öffentlichen Daten im Testdouble, das in die zu testende Klasse injiziert wurde. Ich konnte dies mit Produktionscode aufgrund des Prinzips des Versteckens von Daten nicht tun. Ich wollte keinen speziellen Testcode in den Produktionscode einfügen lassen. Die öffentlichen Daten mussten einer anderen Klasse angehören.
Das Testdoppel wurde injiziert. Das ist etwas anderes als nur eine Strategie, da sie Daten betrifft und nicht nur funktioniert.
Die Abhängigkeitsinjektion ist eine Verfeinerung des Strategiemusters, die ich kurz erläutern werde. Zur Laufzeit muss häufig zwischen mehreren alternativen Modulen gewählt werden. Diese Module implementieren alle eine gemeinsame Schnittstelle, sodass sie austauschbar verwendet werden können. Der Zweck des Strategiemusters besteht darin, die Entscheidung über das zu verwendende Modul (dh die "konkrete Strategie" oder Abhängigkeit) zu entlasten, indem der Entscheidungsprozess in einem separaten Objekt zusammengefasst wird, das ich als Strategieobjekt bezeichnen werde.
Die Abhängigkeitsinjektion verfeinert das Strategiemuster, indem sie nicht nur entscheidet, welche konkrete Strategie verwendet werden soll, sondern auch eine Instanz der konkreten Strategie erstellt und diese wieder in das aufrufende Modul "injiziert". Dies ist auch dann nützlich, wenn es nur eine einzige Abhängigkeit gibt, da das Wissen über die Verwaltung (Initialisierung usw.) der konkreten Strategieinstanz auch im Strategieobjekt verborgen sein kann.
Tatsächlich sieht die Abhängigkeitsinjektion auch dem Bridge-Muster sehr ähnlich. Für mich (und gemäß der Definition) soll das Bridge-Muster verschiedene Versionen der Implementierung berücksichtigen, während das Strategiemuster für die völlig andere Logik gilt. Der Beispielcode scheint jedoch DI zu verwenden. Vielleicht ist DI also nur eine Technik oder Implementierung?
Strategie ist eine Arena, um Ihre Fähigkeiten zur Abhängigkeitsinjektion zu nutzen. Es gibt folgende Möglichkeiten, um die Abhängigkeitsinjektion zu implementieren:
Es gibt jedoch eine Sache, die die Strategie auszeichnet. Wie Sie in Unity wissen, werden beim Start der Anwendung alle Abhängigkeiten festgelegt, und wir können sie nicht weiter ändern. Die Strategie unterstützt jedoch die Änderung der Laufzeitabhängigkeit. Aber WIR müssen die Abhängigkeit verwalten / injizieren, nicht die Verantwortung der Strategie!
Tatsächlich spricht die Strategie nicht von Abhängigkeitsinjektion. Bei Bedarf kann dies über Abstract Factory innerhalb eines Strategiemusters erfolgen. Bei der Strategie geht es nur darum, eine Klassenfamilie mit Schnittstelle zu erstellen und damit zu spielen. Wenn wir beim Spielen feststellen, dass sich die Klassen in einer anderen Stufe befinden, müssen wir sie selbst injizieren, aber nicht die Aufgabe der Strategie.
Wenn wir SOLID-Prinzipien berücksichtigen - Wir verwenden das Strategiemuster für das Open-Closed-Prinzip und die Abhängigkeitsinjektion für das Abhängigkeitsinversionsprinzip