Problem
Ich habe kürzlich viel darüber gelesen, dass Singletons schlecht sind und wie die Abhängigkeitsinjektion (die ich als "Verwenden von Schnittstellen" verstehe) besser ist. Als ich einen Teil davon mit Callbacks / Interfaces / DI implementierte und mich an das Prinzip der Schnittstellentrennung hielt, war ich ziemlich durcheinander.
Die Abhängigkeiten eines übergeordneten UI-Elements, in denen im Grunde alle seiner untergeordneten Elemente zusammengefasst sind. Je weiter oben in der Hierarchie ein UI-Element liegt, desto aufgeblähter ist sein Konstruktor.
Ganz oben auf der UI-Hierarchie befand sich eine Anwendungsklasse, die Informationen zur aktuellen Auswahl und einen Verweis auf ein 3D-Modell enthielt, das Änderungen widerspiegeln muss. Die Anwendungsklasse implementierte 8 Schnittstellen, und dies war nur ein Fünftel der kommenden Produkte (/ Schnittstellen)!
Ich arbeite derzeit mit einem Singleton, der die aktuelle Auswahl und die UI-Elemente enthält und eine Funktion hat, um sich selbst zu aktualisieren. Diese Funktion durchsucht den UI-Baum und die UI-Elemente und greift dann nach Bedarf auf den aktuellen Auswahl-Singleton zu. Der Code erscheint mir auf diese Weise sauberer.
Frage
Ist ein Singleton vielleicht für dieses Projekt geeignet?
Wenn nicht, gibt es einen grundlegenden Fehler in meinem Denken und / oder in der Implementierung von DI, der es so umständlich macht?
Zusätzliche Informationen zum Projekt
Typ: Einkaufskorb für Apartments mit Schnickschnack
Größe: 2 Mannmonate für Code und Benutzeroberfläche
Wartung: Keine laufenden Updates, aber möglicherweise "Version 2.0" später
Umgebung: Verwenden von C # in Unity, das eine Entität verwendet Komponentensystem
In fast allen Fällen löst die Benutzerinteraktion mehrere Aktionen aus. Zum Beispiel, wenn der Benutzer ein Element auswählt
- Der UI-Teil, der dieses Element und seine Beschreibung zeigt, muss aktualisiert werden. Dazu muss es auch einige Informationen von einem 3D-Modell erhalten, um den Preis zu berechnen.
- Weiter oben in der Benutzeroberfläche muss der Gesamtpreis aktualisiert werden
- Eine entsprechende Funktion in einer Klasse eines 3D-Modells muss aufgerufen werden, um die Änderungen dort anzuzeigen
quelle
Antworten:
Ich denke, die Frage ist ein Symptom, keine Lösung.
Eine Lösung, die nach einem Problem sucht; Das und das Missverständnis verfälschen wahrscheinlich Ihr Design. Haben Sie diese SO-Frage zu DI vs Singleton gelesen , verschiedene Konzepte oder nicht? Ich habe das so gelesen, als würde man einfach einen Singleton einwickeln, damit der Client sich nicht mit einem Singleton befassen muss. Es ist nur eine gute alte Verkapselung. Ich denke, das ist genau richtig.
Bauen Sie zuerst die kleineren Bits und übergeben Sie sie dann an den Konstruktor des Objekts, zu dem sie gehören, und übergeben Sie das größere Objekt an den Konstruktor des nächstgrößeren Objekts ...
Wenn Sie komplexe Konstruktionsprobleme haben, verwenden Sie das Factory- oder Builder-Muster. Unterm Strich gibt es diese komplexe Konstruktion, die in ihre eigene Klasse aufgenommen wurde, um andere Klassen ordentlich, sauber, verständlich usw. zu halten.
Dies klingt nach fehlender Erweiterbarkeit. Das Kerndesign fehlt und alles wird von oben eingepfercht. Es sollte mehr Bottom-up-Konstruktion, Zusammensetzung und Vererbung geben.
Ich frage mich, ob Ihr Design "kopflastig" ist. Klingt so, als würden wir versuchen, eine Klasse zu allem oder allem zu machen, was es sein könnte. Und die Tatsache, dass es sich um eine UI-Klasse handelt, nicht um eine Business-Domain-Klasse, lässt mich wirklich über die Trennung von Bedenken nachdenken.
Überdenken Sie Ihr Design von Anfang an und stellen Sie sicher, dass es eine solide, grundlegende Produktabstraktion gibt, auf der aufgebaut werden kann, um komplexere oder unterschiedliche Kategorienproduktionen zu erstellen. Dann haben Sie besser benutzerdefinierte Sammlungen dieser Dinge, damit Sie irgendwo Funktionen auf "Sammlungsebene" platzieren können - wie das von Ihnen erwähnte "3. Modell".
Vieles davon passt möglicherweise in benutzerdefinierte Sammlungsklassen. Aufgrund der Tiefe und Komplexität kann es sich auch um eine eigenständige Klassenstruktur handeln. Diese beiden Dinge schließen sich nicht gegenseitig aus.
Lesen Sie mehr über das Besuchermuster. Es ist die Idee, ganze Funktionsfutter abstrakt mit verschiedenen Typen zu verbinden.
Design und DI
90% aller Abhängigkeitsinjektionen, die Sie jemals durchführen werden, sind Konstruktorparameterübergaben. So sagt der Quy, der das Buch geschrieben hat . Gestalten Sie Ihre Klassen gut und vermeiden Sie es, diesen Denkprozess mit einer vagen Vorstellung davon zu verschmutzen, dass Sie ein DI-Container-Ding verwenden müssen. Wenn Sie es brauchen, wird Ihr Design es Ihnen sozusagen vorschlagen.
Konzentrieren Sie sich auf die Modellierung der Apartment-Shopping-Domain.
Vermeiden Sie den Designansatz von Jessica Simpson : "Ich weiß überhaupt nicht, was das bedeutet, aber ich will es."
Folgendes ist einfach falsch:
quelle
"Klassenerbschaft" ist eine rote Fahne. Hypothetisch: eine Webseite mit 5 Widgets. Die Seite ist kein Vorfahr eines der Widgets. Es kann einen Verweis auf diese Widgets enthalten. Aber es ist kein Vorfahr. Erwägen Sie anstelle der Klassenerbschaft die Verwendung von Komposition. Jedes der 5 Widgets kann für sich erstellt werden, ohne auf die anderen Widgets oder die Startseite zu verweisen. Die obere Seite wird dann mit genügend Informationen erstellt, um eine Basisseite zu erstellen und die Widget-Objekte (Sammlung) zu gestalten, die an sie übergeben werden. Die Seite ist für das Layout usw. verantwortlich, nicht jedoch für die Konstruktion und Logik der Widgets.
Sobald Sie Komposition verwenden, ist DI Ihr bester Freund. Mit DI können Sie jedes Widget gegen die in DI definierte Version oder den Widget-Typ austauschen. Die Konstruktion der Widgets wird im DI erfasst und ist von der oberen Seite getrennt. Vielleicht kann sogar die Zusammensetzung der Sammlung in Ihrem DI definiert werden. Die oberste Seite führt das Layout basierend auf den Widgets aus, die an sie übergeben wurden. Es sind keine Änderungen am Konstruktor der obersten Seite erforderlich. Die oberste Seite benötigt nur die Logik, um das Layout der Widgets durchzuführen und Informationen basierend auf den definierten Schnittstellen an das / vom Widget zu übergeben.
Fügen Sie den Widgets, die sie benötigen, eine Sammlung von Listenern hinzu, anstatt die Listener in der Kompositionskette auf und ab zu leiten. Injizieren Sie die Sammlung in einen Verlag, damit dieser in der Sammlung veröffentlichen kann. Injizieren Sie die Sammlung in die Listener, damit sie sich selbst zur Sammlung hinzufügen können. Die Sammlung umfasst alle Objekte in der Kompositionskette.
quelle