Zur Behebung des if-Web- Problems können Sie eine Regelengine erstellen, in der jede einzelne Regel unabhängig codiert wird. Eine weitere Verfeinerung hierfür wäre das Erstellen einer domänenspezifischen Sprache (DSL), um die Regeln zu erstellen. Ein DSL allein verschiebt das Problem jedoch nur von einer Codebasis (Haupt) zu einer anderen (DSL). Ohne Struktur wird das DSL nicht besser abschneiden als die Muttersprache (Java, C # usw.), daher werden wir darauf zurückkommen, nachdem wir einen verbesserten strukturellen Ansatz gefunden haben.
Das grundlegende Problem ist, dass Sie ein Modellierungsproblem haben. Wann immer Sie auf solche kombinatorischen Situationen stoßen, ist dies ein klares Zeichen dafür, dass Ihre Modellabstraktion, die die Situation beschreibt, zu grob ist. Sie kombinieren höchstwahrscheinlich Elemente, die zu verschiedenen Modellen gehören sollen, in einer einzigen Entität.
Wenn Sie Ihr Modell immer wieder auflösen, wird sich dieser kombinatorische Effekt möglicherweise vollständig auflösen. Auf diesem Weg kann es jedoch leicht passieren, dass Sie sich in Ihrem Design verlieren und ein noch größeres Durcheinander schaffen. Perfektionismus ist hier nicht unbedingt Ihr Freund.
Finite State Machines und Rule Engines sind nur ein Beispiel dafür, wie dieses Problem aufgeschlüsselt und besser beherrschbar gemacht werden kann. Die Hauptidee dabei ist, dass ein kombinatorisches Problem wie dieses häufig vermieden werden kann, indem ein Entwurf erstellt und in verschachtelten Abstraktionsebenen ad-nauseam wiederholt wird, bis die Leistung Ihres Systems zufriedenstellend ist. Ähnlich wie Fraktale verwendet werden, um komplizierte Muster zu erstellen. Die Regeln bleiben gleich, egal ob Sie Ihr System mit einem Mikroskop oder aus der Vogelperspektive betrachten.
Beispiel für die Anwendung auf Ihre Domain.
Sie versuchen zu modellieren, wie sich Kühe durch ein Gelände bewegen. Obwohl es Ihrer Frage an Details mangelt, würde ich vermuten, dass Ihre große Menge an Ifs Entscheidungsfragmente wie enthält, if cow.isStanding then cow.canRun = true
aber Sie geraten ins Stocken, wenn Sie beispielsweise Geländedetails hinzufügen. Für jede Aktion, die Sie durchführen möchten, müssen Sie alle Aspekte überprüfen, die Ihnen einfallen, und diese Überprüfungen für die nächste mögliche Aktion wiederholen.
Zuerst benötigen wir unser wiederholbares Design, das in diesem Fall ein FSM ist, um die sich ändernden Zustände der Simulation zu modellieren. Das erste , was ich tun würde , ist eine Referenz FSM zu implementieren, einen prägenden Zustand Schnittstelle, eine Übergangsschnittstelle und vielleicht einen Übergang KontextDies kann gemeinsame Informationen enthalten, die den beiden anderen zur Verfügung gestellt werden sollen. Eine grundlegende FSM-Implementierung wechselt unabhängig vom Kontext von einem Übergang zu einem anderen. Hier kommt eine Regelengine ins Spiel. Die Regelengine kapselt die Bedingungen, die erfüllt sein müssen, damit der Übergang stattfinden kann. Eine Regel-Engine kann hier so einfach sein wie eine Liste von Regeln, von denen jede eine Auswertungsfunktion hat, die einen Booleschen Wert zurückgibt. Um zu überprüfen, ob ein Übergang stattfinden soll, iterieren Sie die Liste der Regeln, und wenn eine davon als falsch bewertet wird, findet der Übergang nicht statt. Der Übergang selbst enthält den Verhaltenscode zum Ändern des aktuellen Status des FSM (und anderer möglicher Aufgaben).
Wenn ich jetzt beginne, die Simulation als eine einzelne große FSM auf GOD-Ebene zu implementieren, habe ich am Ende VIELE mögliche Zustände, Übergänge usw. Das If-else-Durcheinander sieht aus, als wäre es behoben, aber es ist eigentlich nur verteilt: Jedes IF ist Jetzt eine Regel, die einen Test mit einer bestimmten Information des Kontexts durchführt (die zu diesem Zeitpunkt so ziemlich alles enthält) und jeder WENN-Körper sich irgendwo im Übergangscode befindet.
Geben Sie die Aufschlüsselung der Fraktale ein: Der erste Schritt wäre, eine FSM für jede Kuh zu erstellen, bei der die Zustände die internen Zustände der Kuh sind (Stehen, Laufen, Gehen, Weiden usw.) und die Übergänge zwischen ihnen von der Umgebung beeinflusst würden. Es ist möglich, dass das Diagramm nicht vollständig ist, zum Beispiel, dass die Beweidung nur im stehenden Zustand zugänglich ist, und dass jeder andere Übergang nicht zulässig ist, weil er einfach nicht im Modell vorhanden ist. Hier trennen Sie die Daten effektiv in zwei verschiedene Modelle, die Kuh und das Gelände. Jedes mit eigenen Eigenschaften. Mit dieser Aufschlüsselung können Sie Ihr gesamtes Motordesign vereinfachen. Anstelle einer einzigen Regelengine, die über alles entscheidet, stehen jetzt mehrere einfachere Regelengines (eine für jeden Übergang) zur Verfügung, die über ganz bestimmte Details entscheiden.
Da ich denselben Code für die FSM wiederverwende, handelt es sich im Grunde genommen um eine Konfiguration der FSM. Erinnerst du dich, als wir DSL's erwähnt haben? Hier kann DSL viel Gutes bewirken, wenn Sie viele Regeln und Übergänge schreiben müssen.
Tiefer gehen
Jetzt muss GOTT nicht mehr mit der ganzen Komplexität im Umgang mit den inneren Zuständen der Kuh fertig werden, sondern wir können sie weiter vorantreiben. Zum Beispiel ist die Verwaltung des Geländes immer noch sehr komplex. Hier entscheiden Sie, wo die Aufteilung ausreicht. Wenn Sie beispielsweise in Ihrem GOTT die Geländedynamik (langes Gras, Schlamm, trockener Schlamm, kurzes Gras usw.) verwalten, können wir dasselbe Muster wiederholen. Nichts hindert Sie daran, eine solche Logik in das Gelände selbst einzubetten, indem Sie alle Geländezustände (langes Gras, kurzes Gras, matschig, trocken usw.) in ein neues Gelände-FSM mit Übergängen zwischen den Zuständen und möglicherweise einfachen Regeln extrahieren. Um zum Beispiel in den schlammigen Zustand zu gelangen, sollte die Regel-Engine den Kontext prüfen, um Flüssigkeiten zu finden, andernfalls ist dies nicht möglich. Jetzt wurde GOTT noch einfacher.
Sie können das FSM-System vervollständigen, indem Sie sie autonom machen und ihnen jeweils einen Thread zuweisen. Dieser letzte Schritt ist nicht erforderlich, ermöglicht es Ihnen jedoch, die Interaktion des Systems dynamisch zu ändern, indem Sie die Art und Weise anpassen, in der Sie Ihre Entscheidungen delegieren (einen speziellen FSM starten oder einfach einen festgelegten Status zurückgeben).
Erinnern Sie sich, wie wir erwähnt haben, dass Übergänge auch "andere mögliche Aufgaben" erfüllen können? Lassen Sie uns das untersuchen, indem wir die Möglichkeit hinzufügen, dass verschiedene Modelle (FSM) miteinander kommunizieren. Sie können eine Reihe von Ereignissen definieren und jedem FSM erlauben, Listener für diese Ereignisse zu registrieren. Betritt beispielsweise eine Kuh ein Geländefeld, kann das Feld Listener für Übergangsänderungen registrieren. Hier wird es etwas knifflig, da jeder FSM auf sehr hohem Niveau implementiert wird, ohne die spezifische Domäne zu kennen, die er besitzt. Sie können dies jedoch erreichen, indem die Kuh eine Liste von Ereignissen veröffentlicht und die Zelle registriert, wenn sie Ereignisse sieht, auf die sie reagieren kann. Eine gute Hierarchie der Veranstaltungsfamilie ist hier eine gute Investition.
Sie können noch tiefer gehen, indem Sie den Nährstoffgehalt und den Wachstumszyklus von Gras modellieren. Mit ... Sie haben es erraten ... einem Gras-FSM, das in das Modell des Terrain Patch eingebettet ist.
Wenn Sie die Idee weit genug treiben, hat GOTT sehr wenig zu tun, da alle Aspekte so gut wie selbst verwaltet werden und Zeit für göttlichere Dinge frei wird.
Rekapitulieren
Wie oben erwähnt, ist der FSM hier nicht die Lösung, sondern nur ein Mittel, um zu veranschaulichen, dass die Lösung für ein solches Problem nicht im Code per say zu finden ist, sondern wie Sie Ihr Problem modellieren. Es gibt höchstwahrscheinlich andere Lösungen, die möglich und höchstwahrscheinlich viel besser sind als mein FSM-Vorschlag. Der "Fraktal" -Ansatz bleibt jedoch ein guter Weg, um diese Schwierigkeit zu bewältigen. Bei korrekter Ausführung können Sie tiefere Ebenen dynamisch zuweisen, wenn es darauf ankommt, und einfachere Modelle angeben, wenn es darauf ankommt. Sie können Änderungen in die Warteschlange stellen und anwenden, wenn Ressourcen verfügbarer werden. In einer Aktionssequenz ist es möglicherweise nicht so wichtig, die Nährstoffübertragung von der Kuh auf die Rasenfläche zu berechnen. Sie können diese Übergänge jedoch aufzeichnen und die Änderungen zu einem späteren Zeitpunkt anwenden oder sie mit einer fundierten Vermutung nur annähern, indem Sie einfach die Regelengines ersetzen oder die FSM-Implementierung insgesamt durch eine einfachere, naive Version für die Elemente ersetzen, die sich nicht im direkten Bereich von befinden Interesse (diese Kuh am anderen Ende des Feldes), damit detailliertere Interaktionen den Fokus und einen größeren Anteil an Ressourcen erhalten. All dies, ohne jemals das System als Ganzes zu überdenken; Da jedes Teil gut isoliert ist, ist es einfacher, ein Drop-In-Ersatzelement zu erstellen, das die Tiefe Ihres Modells begrenzt oder erweitert. Durch die Verwendung eines Standarddesigns können Sie darauf aufbauen und die Investitionen in Ad-hoc-Tools wie DSL maximieren, um Regeln oder ein Standardvokabular für Ereignisse zu definieren. Beginnen Sie dabei erneut auf sehr hohem Niveau und erweitern Sie diese nach Bedarf. Da jedes Teil gut isoliert ist, ist es einfacher, ein Drop-In-Ersatzelement zu erstellen, das die Tiefe Ihres Modells begrenzt oder erweitert. Durch die Verwendung eines Standarddesigns können Sie darauf aufbauen und die Investitionen in Ad-hoc-Tools wie DSL maximieren, um Regeln oder ein Standardvokabular für Ereignisse zu definieren. Beginnen Sie dabei erneut auf sehr hohem Niveau und erweitern Sie diese nach Bedarf. Da jedes Teil gut isoliert ist, ist es einfacher, ein Drop-In-Ersatzelement zu erstellen, das die Tiefe Ihres Modells begrenzt oder erweitert. Durch die Verwendung eines Standarddesigns können Sie darauf aufbauen und die Investitionen in Ad-hoc-Tools wie DSL maximieren, um Regeln oder ein Standardvokabular für Ereignisse zu definieren. Beginnen Sie dabei erneut auf sehr hohem Niveau und erweitern Sie diese nach Bedarf.
Ich würde ein Codebeispiel angeben, aber das ist alles, was ich mir leisten kann, um es jetzt zu tun.