Abhängigkeitsinjektion und Entwicklungsproduktivität

Abstrakt

In den letzten Monaten habe ich eine leichte, C # -basierte Game-Engine mit API-Abstraktion und Entity / Component / Scripting-System programmiert. Die ganze Idee dabei ist, den Spielentwicklungsprozess in XNA, SlimDX und so weiter zu vereinfachen, indem eine Architektur bereitgestellt wird, die der der Unity-Engine ähnelt.

Design-Herausforderungen

Wie die meisten Spieleentwickler wissen, gibt es viele verschiedeneDienstleistungen Sie müssen im gesamten Code zugreifen. Viele Entwickler greifen auf die Verwendung globaler statischer Instanzen von z. Ein Render-Manager (oder Komponist), eine Szene, ein Grafikgerät (DX), ein Logger, ein Eingabestatus, ein Ansichtsfenster, ein Fenster usw. Es gibt einige alternative Ansätze für die globalen statischen Instanzen / Singletons. Eine besteht darin, jeder Klasse eine Instanz der Klassen zuzuweisen, auf die sie zugreifen muss, entweder über einen Konstruktor oder eine Konstruktor- / Eigenschaftsabhängigkeitsinjektion (DI). Eine andere besteht darin, einen globalen Service-Locator wie die ObjectFactory von StructureMap zu verwenden, in der der Service-Locator normalerweise wie folgt konfiguriert ist ein IoC-Container.

Abhängigkeitsspritze

Ich habe mich aus vielen Gründen für DI entschieden. Das offensichtlichste Problem ist die Testbarkeit, da die Programmierung über Schnittstellen erfolgt und alle Abhängigkeiten jeder Klasse über einen Konstruktor bereitgestellt werden. Diese Klassen können problemlos getestet werden, da der Testcontainer die erforderlichen Dienste oder deren Mocks instanziieren und in sie einspeisen kann jede Klasse, die getestet werden soll. Ein weiterer Grund für DI / IoC war, ob Sie es glauben oder nicht, die Lesbarkeit des Codes zu verbessern. Schluss mit dem umfangreichen Initialisierungsprozess, bei dem alle verschiedenen Dienste instanziiert und Klassen mit Verweisen auf die erforderlichen Dienste manuell instanziiert werden. Durch die Konfiguration des Kernels (NInject) / der Registrierung (StructureMap) erhalten Sie bequem einen einzigen Konfigurationspunkt für die Engine / das Spiel, an dem Service-Implementierungen ausgewählt und konfiguriert werden.

Meine Probleme

Ich habe oft das Gefühl, Schnittstellen zu schaffen, um der Schnittstellen willenMeine Produktivität ist dramatisch gesunken, da ich mir nur Gedanken darüber mache, wie man Dinge auf DI-Weise macht, anstatt auf schnelle und einfache Weiseglobale statische Weg.In einigen Fällen, z.B. Wenn Sie neue Entities zur Laufzeit instanziieren, benötigen Sie Zugriff auf den IoC-Container / Kernel, um die Instanz zu erstellen. Dadurch entsteht eine Abhängigkeit vom IoC-Container selbst(ObjectFactory in SM, eine Instanz des Kernels in Ninject), was wirklich dem Grund zuwiderläuft, überhaupt einen zu verwenden. Wie kann das gelöst werden? Man denke an abstrakte Fabriken, aber das kompliziert den Code nur noch weiter.Abhängig von den Serviceanforderungen können die Konstruktoren einiger Klassen sehr groß werden, wodurch die Klasse in anderen Kontexten, in denen keine IoC verwendet wird, völlig unbrauchbar wird.

Grundsätzlich verlangsamt DI / IoC meine Produktivität dramatisch und kompliziert in einigen Fällen den Code und die Architektur weiter. Daher bin ich mir nicht sicher, ob es ein Weg ist, dem ich folgen soll, oder ich gebe einfach auf und mache Dinge auf die altmodische Weise. Ich suche keine einzige Antwort, die sagt, was ich tun oder nicht tun soll, sondern eine Diskussion darüber, ob sich die Verwendung von DI auf lange Sicht lohnt, im Gegensatz zur Verwendung der globalen statischen / singleton-Methode, mögliche Vor- und Nachteile, die ich übersehen habe und mögliche Lösungen für meine oben aufgeführten Probleme im Umgang mit DI.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage