Gibt es eine alternative zu bastard-injection? (AKA poor man ' s injection via default Konstruktor)
Ich am häufigsten bin versucht zu verwenden, "bastard-Injektion" in ein paar Fällen. Wenn ich ein "richtiges" dependency injection constructor:
public class ThingMaker {
...
public ThingMaker(IThingSource source){
_source = source;
}
Dann aber für Klassen, die ich bin, die beabsichtigen als öffentlichen APIs (Klassen, anderen Entwicklungsteams verbrauchen), ich finde nie eine bessere Möglichkeit, als zu schreiben, ein Standard - "bastard" - Konstruktor mit den meisten-wahrscheinlich brauchte Abhängigkeit:
public ThingMaker() : this(new DefaultThingSource()) {}
...
}
Der offensichtliche Nachteil hier ist, dass diese erstellt eine statische Abhängigkeit von DefaultThingSource; im Idealfall gäbe es keine solche Abhängigkeit, und die Verbraucher würden immer Spritzen, was IThingSource Sie wollten. Dies ist jedoch zu schwer zu verwenden; wollen die Verbraucher von neuen ein ThingMaker und an die Arbeit machen, Dinge, die dann Monate später injizieren etwas anderes, wenn die Notwendigkeit entsteht. Dies lässt nur ein paar Optionen meiner Meinung nach:
- Weglassen der bastard Konstruktor; macht der Verbraucher von ThingMaker zu verstehen IThingSource, zu verstehen, wie ThingMaker interagiert mit IThingSource, finden oder schreiben Sie eine konkrete Klasse, und dann Spritzen Sie eine Instanz in Ihrem Konstruktor aufrufen.
- Weglassen der bastard Konstruktor und eine separate Fabrik -, container -, oder andere bootstrapping-Klasse/Methode; irgendwie machen die Verbraucher verstehen, dass Sie nicht brauchen, um zu schreiben Ihre eigenen IThingSource; macht der Verbraucher von ThingMaker zu finden und zu verstehen, die Fabrik oder bootstrapper und verwenden Sie es.
- Halten die bastard-Konstruktor, so dass der Verbraucher zu "neu" ein Objekt aus und führen Sie mit ihm, und der Umgang mit dem optionalen statischen Abhängigkeit von DefaultThingSource.
Boy, #3 hat sicher scheint attraktiv. Gibt es eine andere, bessere option? #1 oder #2 einfach nicht scheinen, es lohnt sich.
- Warum ist die Abhängigkeit von DefaultThingSource schlimmer als die Abhängigkeit von IThingSource? Gehen Sie mit #3.
- Da DefaultThingSource ist ein potenziell sehr spezifische Umsetzung, mit Abhängigkeit von einem externen system in einer anderen assembly, die auch überholt in ein paar Monaten; IThingSource ist in der stets korrekt und nie gebunden an eine spezifische Implementierung.
- Natürlich weiß ich nicht, wie Komplex DefaultThingSource ist. Aber ich würde Wetten, dass wenn Sie die Dinge einfach halten und es gut design, in Wirklichkeit werden Sie feststellen, dass Ihr code wird nicht veraltet sein, so schnell wie Sie Angst-mit Sicherheit nicht schnell genug, um zu rechtfertigen, all diese Herzschmerz.
- Das ist das problem mit guten dependency-inversion - kann es sein, flexibler und besser zu gestalten, aber es ist nicht so einfach für einen neuen programmer zu verwenden.
- Sie können auch wollen, um einen Blick auf diesen Artikel: lostechies.com/jimmybogard/2009/07/03/...
- Beachten Sie auch, dass diese Praxis ist auch bekannt als "Poor Man' s Dependency Injection".
- DefaultthingSource ist ... überholt in ein paar Monaten" - Dann ist es ein sehr schlechter Kandidat für eine "Standard" - in einem öffentlichen Aufruf. Ich vote für #2
- Deshalb habe ich "möglicherweise" veraltet-wir wissen nicht, wie Sie sich entwickeln wird, in Verwendung. Wir hoffen, dass unsere Verbraucher finden Sie alle Arten von wertvollen Möglichkeiten, unsere software zu nutzen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Soweit ich das verstanden habe, diese Frage bezieht sich auf, wie setzen Sie eine lose gekoppelte API mit einigen passenden Standardeinstellungen. In diesem Fall haben Sie möglicherweise eine gute Lokalen Standard -, in welchem Fall der Abhängigkeit angesehen werden können, werden als optional. Eine Möglichkeit für den Umgang mit optionale Abhängigkeiten ist die Verwendung Eigenschaft Injektion statt Constructor Injection - in der Tat, das ist die Art von poster-Szenario für die Property-Injection.
Jedoch die Reale Gefahr des Bastard-Injektion ist, wenn der Standardwert ist ein Ausländischen Default, denn das würde bedeuten, dass der Standard-Konstruktor entlang zieht sich eine unerwünschte Kopplung an die Montage der Umsetzung der Vorgabe. Wie verstehe ich diese Frage, jedoch ist der beabsichtigte Standard würde entstehen, in der gleichen assembly, in dem Fall sehe ich keine Besondere Gefahr.
In jedem Fall könnte man auch überlegen, eine Fassade, wie beschrieben, in einer meiner früheren Antworten: Abhängigkeit Injizieren (DI) "freundliche" Bibliothek
BTW, das hier verwendete Terminologie basiert auf der pattern language von mein Buch.
Meine trade-off ist ein spin auf @BrokenGlass:
1) der Einzige Konstruktor ist parametrisierten Konstruktor
2) Verwenden Sie die factory-Methode zum erstellen einer ThingMaker und pass auf, dass die Standard-Quelle.
Offensichtlich nicht zu beseitigen, Ihre Abhängigkeit, aber es macht es übersichtlicher für mich, dass dieses Objekt mit Abhängigkeiten, die von einem Anrufer kann tief Tauchen Sie ein in wenn Sie interessiert. Sie können machen, dass die factory-Methode noch deutlicher, wenn Sie mögen (CreateThingMakerWithDefaultThingsource), ob das hilft, mit Verständnis. Ich bevorzuge, dies zu überschreiben der IThingSource factory-Methode, da Sie es weiterhin bevorzugen Komposition. Sie können auch hinzufügen einer neuen Fabrik-Methode, wenn die DefaultThingSource ist veraltet und haben einen klaren Weg zu finden, alle den code mit dem DefaultThingSource und zu markieren, die aktualisiert werden.
Sie überdachte die Möglichkeiten, die in Ihrer Frage. Factory-Klasse, die anderswo für Komfort oder einigen Komfort innerhalb der Klasse selbst. Die einzige andere unschöne Möglichkeit wäre die Reflexion-basiert, ausblenden der Abhängigkeit noch weiter.
Eine alternative ist es, eine die factory-Methode
CreateThingSource()
in IhremThingMaker
Klasse schafft, dass die Abhängigkeit für Sie.Zum testen oder wenn Sie brauchen eine andere Art von
IThingSource
man müsste dann erstellen Sie eine Unterklasse vonThingMaker
und überschreibenCreateThingSource()
zur Rückgabe der konkreten Art Sie wollen. Offensichtlich ist dieser Ansatz nur lohnt, wenn man hauptsächlich in der Lage sein müssen, um zu injizieren, die in Abhängigkeit zum testen, aber für die meisten/alle anderen Zwecke benötigen keine andereIThingSource
Ich vote für die #3. Sie werde machen Ihrem Leben-und das Leben anderer Entwickler,--leichter.
Wenn Sie ein "Standard" - Abhängigkeit, auch bekannt als "Poor Man' s Dependency Injection, dann haben Sie zu initialisieren und "Draht -" die Abhängigkeit irgendwo.
Behalte ich die zwei Konstruktoren haben aber eine Fabrik nur für die Initialisierung.
Nun in der Fabrik erstellen Sie die Standard-Instanz und lassen die Methode overrided:
Update:
, Warum mit zwei Konstruktoren:
Zwei Konstruktoren zeigen deutlich, wie die Klasse verwendet werden soll. Der parameterlose Konstruktor Staaten: erstellen Sie einfach eine Instanz und der Klasse führen alle it-Aufgaben. Nun der zweite Konstruktor besagt, dass die Klasse hängt von IThingSource und bietet eine Möglichkeit, eine Umsetzung anders als die Voreinstellung.
Warum eine Fabrik:
1 - Disziplin: Erstellen von neuen Instanzen sollten nicht Teil der Aufgaben dieser Klasse eine factory-Klasse sinnvoller ist.
2 - TROCKEN: stellen Sie sich vor, dass in der gleichen API-andere Klassen auch davon abhängen, IThingSource und das gleiche tun. Überschreiben Sie einmal die factory-Methode zurückgeben IThingSource und alle Klassen in der API automatisch starten, verwenden Sie die neue Instanz.
Sehe ich nicht ein problem in der Kupplung ThingMaker um eine default-Implementierung von IThingSource solange diese Umsetzung sinnvoll, um die API als ganzes, und auch Sie bieten Möglichkeiten zum überschreiben diese Abhängigkeit für die Prüfung und Erweiterung der Zwecke.
Sind Sie unzufrieden mit der OO-Verunreinigung dieser Abhängigkeit, aber Sie nicht wirklich sagen, was für Probleme es letztlich bewirkt.
Ich denke, das größte problem hier ist die Wahl des namens, nicht, ob die Technik zu nutzen.
Die Beispiele, die in der Regel mit dieser Art von Injektion sind oft extrem simplisitic: "in der default-Konstruktor für die Klasse
B
, rufen Sie einen überladenen Konstruktor mitnew A()
und werden auf Ihrem Weg!"Die Realität ist, dass die Abhängigkeiten sind oft äußerst Komplex zu konstruieren. Was zum Beispiel, wenn
B
braucht eine nicht-class-Abhängigkeit wie eine Datenbank-Verbindung oder application-Einstellung? Dann Band KlasseB
zu denSystem.Configuration
namespace, die Steigerung Ihrer Komplexität und Kopplung, während die Senkung Ihrer Kohärenz, die alle zu Kodieren details, die man einfach hätte externalisiert durch weglassen der default-Konstruktor.Diese Art der Injektion vermittelt dem Leser, dass Sie haben erkannt, die Vorteile der entkoppelten design, aber nicht bereit sind, zu Begehen, um es. Wir alle wissen, dass, wenn jemand sieht, dass saftige, leicht, reibungsarm, Standard-Konstruktor, Sie gehen, zu nennen es egal, wie starr das Programm von diesem Punkt an. Sie können nicht verstehen, die Struktur Ihres Programms Lesen, ohne den source-code für die Standard-Konstruktor, der nicht eine option, wenn man nur verteilen Sie die Baugruppen. Dokumentieren Sie die Konventionen der Verbindung Zeichenfolge-name und die app-Einstellungen-Taste, sondern an dieser Stelle der code nicht auf eigenen Beinen stehen, und Sie legte die Beweislast auf die Entwickler, auf die Jagd nach dem richtigen Zauberspruch.
Optimieren von code, damit diejenigen, die es schreiben kann, ohne zu verstehen, was Sie sagen, ist eine Sirene-Lied, ein anti-pattern, dass führt letztendlich zu mehr Zeit verloren in der Entschlüsselung der Magie als die Zeit gespeichert, in der anfänglichen Aufwand. Entweder entkoppeln oder nicht; halten Sie einen Fuß in jedem Muster verringert sich der Fokus der beiden.
Für was es Wert ist, all die standard-code, den ich gesehen habe, in Java funktioniert es so:
Jemand, der nicht will, eine
DefaultThingSource
Objekt außer Kraft setzen könnencreateIThingSource
. (Wenn möglich, den Anruf zucreateIThingSource
wäre irgendwo anderen als den Konstruktor.) C# nicht empfehlen überschreiben wie Java funktioniert, und es vielleicht nicht so offensichtlich, wie es sein würde, in Java, dass die Anwender können und müssen vielleicht Ihre eigenen IThingSource Umsetzung. (Noch so offensichtlich wie zu geben.) Meine Vermutung ist, dass die #3 ist der Weg zu gehen, aber ich dachte, ich würde erwähnen.Nur eine Idee - vielleicht ein bisschen mehr elegant, aber leider nicht loszuwerden, die Abhängigkeit:
Haben eine interne Fabrik (interne Bibliothek), die Karten die DefaultThingSource zu IThingSource, die aufgerufen wird, aus der Standard-Konstruktor.
Dies ermöglicht das "neue" der ThingMaker Klasse ohne Parameter oder irgendwelche Kenntnisse der IThingSource und ohne direkte Abhängigkeit von DefaultThingSource.
Für wirklich öffentliche APIs, ich in der Regel behandeln diese mit einem zweiteiligen Ansatz:
Der wirklich schwierige Teil hier ist zu entscheiden, Wann zu aktivieren, den container #2, und die best-choice-Ansatzes hängt stark von Ihrem geplanten API-Konsumenten.
Unterstütze ich option #1, mit einer Erweiterung: machen
DefaultThingSource
eine öffentliche Klasse. Deine Formulierung oben impliziert, dassDefaultThingSource
werden vor der öffentlichkeit verborgen Konsumenten der API, aber ich verstehe deine situation, es gibt keinen Grund, nicht setzen Sie den Standard. Darüber hinaus können Sie leicht dokumentieren die Tatsache, dass außerhalb von besonderen Umständen einnew DefaultThingSource()
können immer übergeben werden, um dieThingMaker
.