Ich habe eine große Codebasis mit vielen "Anti-Pattern" -Singletons, Dienstprogrammklassen mit statischen Methoden und Klassen, die ihre eigenen Abhängigkeiten mithilfe von new
Schlüsselwörtern erstellen . Es macht es sehr schwierig, einen Code zu testen.
Ich möchte Code schrittweise in den Abhängigkeitsinjektionscontainer migrieren (in meinem Fall Guice
, weil es sich um ein GWT
Projekt handelt). Nach meinem Verständnis der Abhängigkeitsinjektion ist alles oder nichts. Entweder werden alle Klassen von Spring / Guice verwaltet oder keine. Da die Codebasis groß ist, kann ich den Code nicht über Nacht transformieren. Ich brauche also einen Weg, dies schrittweise zu tun.
Das Problem ist, dass ich, wenn ich mit einer Klasse beginne, die in andere Klassen eingefügt werden muss, @Inject
in diesen Klassen keine einfache verwenden kann, da diese Klassen noch nicht vom Container verwaltet werden. Dies schafft also eine lange Kette bis zu den "Top" -Klassen, die nirgendwo injiziert werden.
Die einzige Möglichkeit, die ich sehe, besteht darin, einen Injector
/ application-Kontext vorerst global über einen Singleton verfügbar zu machen , damit andere Klassen verwaltete Beans daraus erhalten können. Dies widerspricht jedoch der wichtigen Idee, das composition root
der Anwendung nicht preiszugeben .
Ein anderer Ansatz wäre Bottom-up: Beginnen Sie mit "High-Level" -Klassen, fügen Sie sie in den Abhängigkeitsinjektionscontainer ein und wechseln Sie langsam zu "kleineren" Klassen. Aber dann muss ich lange warten, da ich diese kleineren Klassen testen kann, die immer noch von Globals / Statics abhängen.
Was wäre der Weg, um eine solche schrittweise Migration zu erreichen?
PS Die Frage Allmähliche Ansätze zur Abhängigkeitsinjektion ist im Titel ähnlich, beantwortet aber meine Frage nicht.
quelle
Antworten:
Tut
C#
mir leid, ist meine Sprache der Wahl, ich kann lesenJava
, würde aber wahrscheinlich die Syntax, die versucht, sie zu schreiben, abschlachten ... Die gleichen Konzepte gelten zwischenC#
undJava
obwohl. Hoffentlich zeigt dies die Schritte, wie Sie Ihre Codebasis schrittweise verschieben können, um mehr zu werden testbar.Gegeben:
könnte leicht umgestaltet werden, um DI ohne die Verwendung eines IOC-Containers zu verwenden - und Sie könnten es möglicherweise sogar in mehrere Schritte aufteilen:
(potenziell) Schritt eins - Abhängigkeiten berücksichtigen, aber keine Änderung am aufrufenden (UI) Code vornehmen:
Refactor 2 (oder erster Refactor, wenn der IOC-Container implementiert und der Aufrufcode sofort geändert wird):
Schritt 2 könnte technisch von selbst erledigt werden - wäre aber (möglicherweise) viel mehr Arbeit - abhängig davon, wie viele Klassen derzeit die Funktionalität "neu" machen, die Sie für DI suchen.
Wenn Sie Schritt 1 -> Schritt 2 wählen, können Sie
Foo
unabhängig davon Komponententests erstellenBar
. Während es vor dem Refactor von Schritt 1 nicht einfach war, ohne die tatsächliche Implementierung beider Klassen zu verwenden. Wenn Sie Schritt 1 -> Schritt 2 (und nicht sofort Schritt 2) ausführen, können Sie im Laufe der Zeit kleinere Änderungen vornehmen, und Sie haben bereits einen Testkabelbaum gestartet, um sicherzustellen, dass Ihr Refactor ohne Konsequenzen funktioniert.quelle
Das Konzept ist das gleiche, egal ob Sie Java, PHP oder sogar C # verwenden. Diese Frage wird von Gemma Anible in diesem YouTube-Video ziemlich gut behandelt:
https://www.youtube.com/watch?v=Jccq_Ti8Lck (PHP, sorry!)
Sie ersetzen den nicht testbaren Code durch "Fassade" (mangels eines besseren Begriffs), die neuen, testbaren Code aufruft. Dann können Sie nach und nach die älteren Anrufe durch die eingespeisten Dienste ersetzen. Ich habe das in der Vergangenheit gemacht und es funktioniert ziemlich gut.
quelle