Ist es schlecht, zwei Klassen zu haben, die sich gegenseitig brauchen?
Ich schreibe ein kleines Spiel, in dem ich eine GameEngine
Klasse habe, die ein paar GameState
Objekte hat. Um auf verschiedene Rendering-Methoden zugreifen zu können, müssen diese GameState
Objekte auch die GameEngine
Klasse kennen - es handelt sich also um eine zirkuläre Abhängigkeit.
Würdest du das schlechtes Design nennen? Ich frage nur, weil ich nicht ganz sicher bin und ich diese Dinge derzeit noch umgestalten kann.
c++
architecture
shad0w
quelle
quelle
Antworten:
Es ist von Natur aus kein schlechtes Design, aber es kann leicht außer Kontrolle geraten. Tatsächlich verwenden Sie dieses Design in Ihren alltäglichen Codes. Beispielsweise weiß vector, dass es sich um den ersten Iterator handelt, und Iteratoren haben Zeiger auf ihren Container.
In Ihrem Fall ist es jetzt viel besser, zwei separate Klassen für GameEnigne und GameState zu haben. Da diese beiden im Grunde genommen unterschiedliche Dinge tun, können Sie später viele Klassen definieren, die GameState erben (wie ein GameState für jede Szene in Ihrem Spiel). Und niemand kann ihre Notwendigkeit, Zugang zu einander zu haben, leugnen. Grundsätzlich führt GameEngine Gamestates aus, daher sollte es einen Zeiger darauf haben. Und GameState verwendet in GameEngine definierte Ressourcen (wie Rendered, Physics Manager usw.).
Sie können und sollten diese beiden Klassen auch nicht ineinander kombinieren, da sie von Natur aus verschiedene Dinge tun und das Kombinieren zu einer sehr großen Klasse führt, die niemand mag.
Bisher wissen wir, dass wir in unserem Design eine zirkuläre Abhängigkeit brauchen. Es gibt mehrere Möglichkeiten, dies sicher zu erstellen:
Um seine Antwort abzuschließen, können Sie eine dieser drei Methoden anwenden, aber Sie müssen vorsichtig sein, um keine dieser Methoden zu übertreiben, da, wie ich bereits sagte, all diese Entwürfe wirklich gefährlich sind und leicht zu einem nicht zu wartenden Code führen können.
quelle
Es ist ein bisschen ein Code-Geruch , aber man kann damit gehen. Wenn dies der einfachere und schnellere Weg ist, um Ihr Spiel zum Laufen zu bringen, dann versuchen Sie es. Aber denken Sie daran, denn es besteht eine gute Chance, dass Sie es irgendwann überarbeiten müssen.
Die Sache mit C ++ ist, dass zirkuläre Abhängigkeiten nicht so einfach kompiliert werden können . Daher ist es möglicherweise eine bessere Idee, sie loszuwerden, als Zeit mit der Korrektur Ihrer Kompilierung zu verbringen.
Siehe diese Frage auf SO für ein paar weitere Meinungen.
Nein, es ist immer noch besser, als alles in eine Klasse zu bringen.
Es ist nicht so toll, aber es kommt den meisten Implementierungen, die ich gesehen habe, ziemlich nahe. Normalerweise haben Sie eine Manager-Klasse für Spielzustände ( Vorsicht! ) Und eine Renderer-Klasse, und es ist durchaus üblich, dass es sich um Singletons handelt. Die zirkuläre Abhängigkeit ist also "verborgen", aber möglicherweise vorhanden.
Wie Sie in den Kommentaren erfahren haben, ist es etwas seltsam, dass Klassen mit Spielstatus eine Art Rendering ausführen. Sie sollten nur Statusinformationen enthalten, und das Rendern sollte von einem Renderer oder einer grafischen Komponente von Spielobjekten selbst durchgeführt werden.
Jetzt könnte es das ultimative Design geben. Ich bin gespannt, ob andere Antworten eine gute Idee bringen. Trotzdem sind Sie wahrscheinlich derjenige, der das beste Design für Ihr Spiel findet.
quelle
Es wird oft als schlechtes Design angesehen, zwei Klassen zu haben, die sich direkt aufeinander beziehen, ja. In der Praxis kann es schwieriger sein, den Kontrollfluss durch den Code zu verfolgen, der Besitz von Objekten und deren Lebensdauer können kompliziert sein. Dies bedeutet, dass keine Klasse ohne die andere wiederverwendbar ist von diesen Klassen in einer dritten "Vermittler" -Klasse und so weiter.
Es ist jedoch für 2- Funktionen sehr häufig und das Modell enthält einen Verweis auf ein beobachtbares Objekt, bei dem es sich möglicherweise um eine Ansicht handelt. Wenn sich das Modell ändert, werden alle Observables aufgerufen, und die Ansicht wird implementiert, indem sie das Modell zurückruft und alle aktualisierten Informationen abruft. Der Vorteil hierbei ist, dass das Modell überhaupt nichts über Ansichten weiß (und warum sollte es das tun?) Und in anderen Situationen wiederverwendet werden kann, auch wenn keine Ansichten vorhanden sind. Objekte aufeinander beziehen. Der Unterschied besteht darin, dass die Beziehung in einer Richtung in der Regel abstrakter ist. Beispielsweise kann in einem Model-View-Controller-System das View-Objekt einen Verweis auf das Model-Objekt enthalten und alles darüber wissen, so dass auf alle seine Methoden und Eigenschaften zugegriffen werden kann, damit sich die View mit relevanten Daten füllen kann . Das Model-Objekt enthält möglicherweise einen Verweis auf die Ansicht, sodass die Ansicht aktualisiert werden kann, wenn sich die eigenen Daten geändert haben. Anstatt jedoch das Modell mit einer Ansichtsreferenz zu versehen, die das Modell von der Ansicht abhängig macht, implementiert die Ansicht normalerweise eine Observable-Schnittstelle, häufig mit nur 1
Update()
Update()
Update()
Sie haben eine ähnliche Situation in Ihrem Spiel. Die GameEngine kennt normalerweise GameStates. Der GameState muss jedoch nicht alles über die GameEngine wissen - er benötigt lediglich Zugriff auf bestimmte Rendering-Methoden in der GameEngine. Das sollte einen kleinen Alarm in Ihrem Kopf auslösen, der besagt, dass entweder (a) GameEngine versucht, zu viele Dinge innerhalb einer Klasse zu tun, und / oder (b) GameState nicht die gesamte Spiel-Engine benötigt, sondern nur den darstellbaren Teil.
Drei Lösungsansätze sind:
quelle
Es wird allgemein als gute Praxis angesehen, eine hohe Kohäsion bei geringer Kopplung zu haben. Hier einige Links zu Kopplung und Zusammenhalt.
Wikipedia-Kopplung
Wikipedia Zusammenhalt
geringe Kopplung, hohe Kohäsion
Stackoverflow auf Best Practice
Wenn zwei Klassen aufeinander verweisen, ist die Kopplung hoch. Das Google Guice Framework zielt darauf ab, durch Abhängigkeitsinjektion eine hohe Kohäsion bei geringer Kopplung zu erreichen. Ich würde vorschlagen, dass Sie sich ein bisschen mehr mit dem Thema befassen und dann Ihren eigenen Aufruf in Ihrem Kontext machen.
quelle