Nehmen wir ein einfaches Beispiel - vielleicht injizieren Sie ein Protokollierungsmittel.
Injizieren einer Klasse
class Worker: IWorker
{
ILogger _logger;
Worker(ILogger logger)
{
_logger = logger;
}
void SomeMethod()
{
_logger.Debug("This is a debug log statement.");
}
}
Ich denke, das ist ziemlich klar, was los ist. Wenn Sie einen IoC-Container verwenden, müssen Sie nicht einmal explizit etwas einfügen. Fügen Sie dies einfach zu Ihrem Kompositionsstamm hinzu:
container.RegisterType<ILogger, ConcreteLogger>();
container.RegisterType<IWorker, Worker>();
....
var worker = container.Resolve<IWorker>();
Beim Debuggen Worker
muss ein Entwickler lediglich den Kompositionsstamm konsultieren, um festzustellen, welche konkrete Klasse verwendet wird.
Wenn ein Entwickler eine kompliziertere Logik benötigt, kann er mit der gesamten Oberfläche arbeiten:
void SomeMethod()
{
if (_logger.IsDebugEnabled) {
_logger.Debug("This is a debug log statement.");
}
}
Injizieren einer Methode
class Worker
{
Action<string> _methodThatLogs;
Worker(Action<string> methodThatLogs)
{
_methodThatLogs = methodThatLogs;
}
void SomeMethod()
{
_methodThatLogs("This is a logging statement");
}
}
Beachten Sie zunächst, dass der Konstruktorparameter jetzt einen längeren Namen hat methodThatLogs
. Dies ist notwendig, weil Sie nicht sagen können, was eine Action<string>
tun soll. Bei der Schnittstelle war es völlig klar, aber hier müssen wir uns auf die Benennung der Parameter verlassen. Dies scheint inhärent weniger zuverlässig und schwieriger während eines Builds durchzusetzen zu sein.
Wie injizieren wir diese Methode? Nun, der IoC-Container erledigt das nicht für Sie. Sie müssen es also beim Instanziieren explizit injizieren Worker
. Dies wirft einige Probleme auf:
- Es ist mehr Arbeit, a zu instanziieren
Worker
- Entwickler, die versuchen zu debuggen,
Worker
werden feststellen, dass es schwieriger ist, herauszufinden, welche konkrete Instanz aufgerufen wird. Sie können nicht nur die Kompositionswurzel konsultieren; Sie müssen den Code nachverfolgen.
Wie wäre es, wenn wir eine kompliziertere Logik brauchen? Ihre Technik macht nur eine Methode verfügbar. Nun, ich nehme an, Sie könnten das komplizierte Zeug in das Lambda backen:
var worker = new Worker((s) => { if (log.IsDebugEnabled) log.Debug(s) } );
aber wenn Sie Ihre Komponententests schreiben, wie testen Sie diesen Lambda-Ausdruck? Es ist anonym, sodass Ihr Unit-Test-Framework es nicht direkt instanziieren kann. Vielleicht können Sie eine clevere Möglichkeit finden, dies zu tun, aber es wird wahrscheinlich eine größere PITA sein als die Verwendung einer Schnittstelle.
Zusammenfassung der Unterschiede:
- Das Injizieren nur einer Methode erschwert das Ableiten des Zwecks, wohingegen eine Schnittstelle den Zweck klar kommuniziert.
- Wenn nur eine Methode injiziert wird, ist für die Klasse, die die Injektion erhält, weniger Funktionalität verfügbar. Auch wenn Sie es heute nicht brauchen, können Sie es morgen brauchen.
- Sie können nicht automatisch nur eine Methode mithilfe eines IoC-Containers injizieren.
- Sie können der Kompositionswurzel nicht entnehmen, welche konkrete Klasse in einer bestimmten Instanz aktiv ist.
- Es ist ein Problem, den Lambda-Ausdruck selbst zu testen.
Wenn Sie mit all dem Obenstehenden einverstanden sind, ist es in Ordnung, nur die Methode zu injizieren. Ansonsten würde ich vorschlagen, dass Sie bei der Tradition bleiben und eine Schnittstelle einfügen.