Dasselbe Grundproblem tritt häufig bei der objektorientierten Programmierung, bei Stilregeln und bei so ziemlich allem anderen auf. Es ist möglich - und sogar sehr verbreitet -, zu viel zu abstrahieren, zu viele Umwege zu machen und allgemein gute Techniken übermäßig und an den falschen Stellen anzuwenden.
Jedes Muster oder andere Konstrukt, das Sie anwenden, bringt Komplexität mit sich. Abstraktion und Indirektion streuen Informationen umher, wodurch manchmal irrelevante Details aus dem Weg geräumt werden, es aber auch manchmal schwieriger wird, genau zu verstehen, was passiert. Jede Regel, die Sie anwenden, bringt Unflexibilität mit sich und schließt Optionen aus, die vielleicht gerade die beste Herangehensweise sind.
Es geht darum, einen Code zu schreiben, der seine Aufgabe erfüllt und robust, lesbar und wartbar ist. Sie sind ein Software-Entwickler - kein Elfenbeinturm-Bauer.
Einschlägige Links
http://thedailywtf.com/Articles/The_Inner-Platform_Effect.aspx
http://www.joelonsoftware.com/articles/fog0000000018.html
Die wahrscheinlich einfachste Form der Dependency Injection (lachen Sie nicht) ist ein Parameter. Der abhängige Code ist von Daten abhängig, und diese Daten werden durch die Übergabe des Parameters injiziert.
Ja, es ist albern und geht nicht auf den objektorientierten Punkt der Dependency Injection ein, aber ein funktionaler Programmierer wird Ihnen sagen, dass (wenn Sie Funktionen erster Klasse haben) dies die einzige Art von Dependency Injection ist, die Sie brauchen. Es geht hier darum, ein triviales Beispiel zu nehmen und die möglichen Probleme aufzuzeigen.
Nehmen wir diese einfache traditionelle Funktion - die C++-Syntax ist hier nicht von Bedeutung, aber ich muss sie irgendwie buchstabieren...
void Say_Hello_World ()
{
std::cout << "Hello World" << std::endl;
}
Ich habe eine Abhängigkeit, die ich extrahieren und injizieren möchte - den Text "Hello World". Einfach genug...
void Say_Something (const char *p_text)
{
std::cout << p_text << std::endl;
}
Inwiefern ist das unflexibler als das Original? Nun, was ist, wenn ich beschließe, dass die Ausgabe in Unicode erfolgen soll. Dann möchte ich wahrscheinlich von std::cout zu std::wcout wechseln. Aber das bedeutet, dass meine Zeichenketten dann wchar_t und nicht char sein müssen. Entweder muss jeder Aufrufer geändert werden, oder (vernünftiger), die alte Implementierung wird durch einen Adapter ersetzt, der den String übersetzt und die neue Implementierung aufruft.
Das sind Wartungsarbeiten, die nicht nötig wären, wenn wir das Original behalten hätten.
Und wenn es Ihnen trivial erscheint, schauen Sie sich diese reale Funktion aus der Win32-API an...
http://msdn.microsoft.com/en-us/library/ms632680%28v=vs.85%29.aspx
Das sind 12 "Abhängigkeiten", mit denen man umgehen muss. Wenn zum Beispiel die Bildschirmauflösungen wirklich riesig werden, brauchen wir vielleicht 64-Bit-Koordinatenwerte - und eine weitere Version von CreateWindowEx. Und ja, es gibt bereits eine ältere Version, die vermutlich hinter den Kulissen auf die neuere Version abgebildet wird...
http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx
Diese "Abhängigkeiten" sind nicht nur ein Problem für den ursprünglichen Entwickler - jeder, der diese Schnittstelle verwendet, muss nachschlagen, was die Abhängigkeiten sind, wie sie spezifiziert sind und was sie bedeuten, und herausfinden, was für seine Anwendung zu tun ist. An dieser Stelle können die Worte "sinnvolle Standardeinstellungen" das Leben sehr viel einfacher machen.
Die objektorientierte Dependency Injection ist im Prinzip nicht anders. Das Schreiben einer Klasse bedeutet einen Mehraufwand, sowohl an Quelltext als auch an Entwicklungszeit, und wenn diese Klasse so geschrieben ist, dass sie Abhängigkeiten gemäß den Spezifikationen einiger abhängiger Objekte bereitstellt, dann ist das abhängige Objekt an die Unterstützung dieser Schnittstelle gebunden, selbst wenn die Implementierung dieses Objekts ersetzt werden muss.
Das soll nicht heißen, dass Dependency Injection schlecht ist - ganz im Gegenteil. Aber jede gute Technik kann übermäßig und an der falschen Stelle angewendet werden. So wie nicht jede Zeichenkette extrahiert und in einen Parameter verwandelt werden muss, muss auch nicht jedes Low-Level-Verhalten aus High-Level-Objekten extrahiert und in eine injizierbare Abhängigkeit verwandelt werden.