Eines der Dinge, an denen ich gerade arbeite, hat einige Ähnlichkeiten mit einem Spiel. Zur Veranschaulichung werde ich mein Problem anhand eines Beispiels erläutern, das aus einem fiktiven hypothetischen Spiel stammt.
Nennen wir es DeathBlaster 4: Die Deathening . In DB4 haben Sie eine Reihe von Ship
-Objekten, die periodisch und zufällig auf Phenomena
treffen, während sie reisen. Ein gegebenes Phenomenon
kann null, eins oder mehr Effects
auf einem Ship
haben, das darauf trifft. Zum Beispiel könnten wir vier Arten von Ships
und drei Arten von Phenomena
haben.
Zusätzlich kann Effects
miteinander interagieren. Zum Beispiel kann ein GreenShip
, das sowohl in GravityWell
als auch in NebulaField
liegt, eine Art Synergie zwischen den generierten SpeedEffect
und ShieldEffect
ableiten. In solchen Fällen ist der synergistische Effekt selbst ein Effect
- zum Beispiel könnte ein PowerLevelSynergyEffect
aus dieser Interaktion resultieren. Keine andere Information als die Menge von Effects
, die auf eine Ship
wirkt, wird benötigt, um zu klären, was das Endergebnis sein soll.
Sie sehen vielleicht ein Problem, das hier auftaucht. Als naive erste Herangehensweise muss jeder Ship
wissen, wie er mit jedem Phenomenon
umgehen kann, oder jeder Phenomenon
muss über jeden Ship
Bescheid wissen. Dies ist offensichtlich inakzeptabel, daher möchten wir diese Verantwortung anderswo verschieben. Offensichtlich gibt es hier mindestens eine externe Klasse, vielleicht eine Mediator
oder Visitor
irgendeiner Art.
Aber was ist der beste Weg, das zu tun? Die ideale Lösung wird wahrscheinlich diese Eigenschaften haben:
Ship
hinzuzufügen, wie es ist, ein neues Phenomenon
hinzuzufügen. Effects
miteinander interagieren und diese Interaktionen verwalten können, um zu entscheiden, was das Endergebnis sein wird. Ich habe bereits entschieden, wie mein Ansatz aussehen wird, denke ich, aber ich bin interessiert zu hören, was der best-design-Konsens ist. Wo würdest du anfangen? Welche Wege würden Sie erkunden?
Folge-Update: Vielen Dank für Ihre Antworten. Hier ist, was ich getan habe. Meine Hauptbeobachtung war, dass die Anzahl der verschiedenen Effects
im Verhältnis zur Anzahl der möglichen% Co_de% × Phenomena
Wechselwirkungen klein zu sein scheint. Das heißt, obwohl es viele mögliche Kombinationen von Interaktionen gibt, ist die Anzahl der Arten von Ergebnissen dieser Interaktionen eine kleinere Zahl.
Sie können sehen, dass zum Beispiel, obwohl es 12 Interaktionskombinationen in der Tabelle gibt, nur fünf Arten von Effekten gibt: Modifikationen an Geschwindigkeit, Modifikationen an der Macht, Modifikationen an Schild, Unverwundbarkeit, Tod.
Ich habe eine dritte Klasse eingeführt, die Ships
, um das Ergebnis von Interaktionen zu bestimmen. Es enthält ein Dictionary, das InteractionResolver
-Paare auf Ship-Phenomenon
abbildet (was im Prinzip ein Delegat ist, der den Effekt und einige Metadaten ausführt). Jedes Effects
erhält ein Ship
, das dem EffectStack
entspricht, das auftritt, wenn das Ergebnis der Berechnung der Interaktion abgeschlossen ist.
Effects
Verwenden Sie dann Ships
, um das tatsächliche Ergebnis von EffectStack
zu ermitteln, indem Sie den vorhandenen Attributen und Eigenschaften Modifikatoren hinzufügen.
Ich mag das, weil:
Effects
abstrahiert. Schiffe müssen die Effekte nur bei Bedarf anwenden. InteractionResolver
unterschieden werden. Der Standard könnte sein, alle Effekte zu verarbeiten, aber ein EffectProcessorStrategy
könnte kleinere Effekte ignorieren, indem er ein anderes BossShip
. Eine interessante mögliche Option wäre die Verwendung einer Variante des Besuchermusters .
Judith Bishop und R. Nigel Horspool haben einen Artikel über Entwurfsmustereffizienz geschrieben, in dem Sie erklärten verschiedene Varianten des klassischen Besuchermusters mit C # 3-Funktionen.
Insbesondere würde ich mir ansehen, wie sie mit Delegierten zusammenarbeiten, um das Besuchermuster zu handhaben. Das Verwenden einer Liste oder eines Stapels von Delegaten könnte Ihnen eine interessante Möglichkeit geben, mehrere Effekte von mehreren Objekten zu handhaben und es ist viel einfacher, beide Seiten der Klassenhierarchie zu erweitern (Schiffe hinzufügen oder Effekte hinzufügen), ohne große Änderungen am Code vorzunehmen.
Nicht ganz eine Antwort, aber für mich schreit das ziemlich "Eigenschaftsmuster". Es gibt ein bekanntes Geschwätz darüber, ich denke, es wird dir ein paar anständige Hinweise geben.
Das sieht nach dem klassischen OOP-Polymorphismus / Besucher-Dilemma aus. Ihre Anforderungen machen es jedoch einfacher.
Grundsätzlich würde ich eine Basisklasse Ship
erstellen, von der alle konkreten Schiffe stammen. Diese Klasse hätte Methoden:
mit leeren Standardkörpern. Wenn Sie ein neues Phänomen hinzufügen, fügen Sie einfach eine neue Methode mit leerem Körper hinzu. (Die Methoden können Argumente haben, wie Koordinaten oder das Gewicht des Schwarzen Lochs etc.)
Für die Effekte und ihre Interaktion - ich denke, du solltest mehr Informationen darüber hinzufügen, wie du es willst, z. Sind die Wechselwirkungen selten oder üblich, sind sie kumulativ, sind sie auf eine begrenzte Anzahl von Variablen beschränkt, die sie kontrollieren ...
Klingt wie ein klassisches Problem mit mehreren Zustellungen für mich.
Interessante Frage
Auf die eine oder andere Weise muss ein Schiff wissen, welche Phänomene es beeinflussen können und welche Auswirkungen es auf welchem Schiff hat.
Dies könnte in einer XML-Datei gespeichert werden, die zur Laufzeit geparst wird.
Vielleicht könnten Sie das Dekorationsmuster verwenden, um die Effekte zu berechnen. Sie generieren verschiedene Phänomene zur Laufzeit.
Angenommen, Ihre Schiffe implementieren ein Interface IShip und überall in Ihrem Code verwenden Sie IShips.
Nehmen Sie jetzt an, dass alle Ihre Phänomene auch das Interface IShip (das vom Dekorator-Designmuster benötigt wird) implementieren.
%Vor%Bei den Phänomenen werden die Methoden vom ursprünglichen Schiff umschlossen, sodass Sie die Eigenschaften und alles ändern können.
Wenn Sie das Strategie-Muster verwenden, können Sie auch beliebige andere Phänomene erzeugen.
>Das Entfernen eines Phänomens kann verwendet werden, indem man den Stapel dekorierter Schiffe, die man hat, durchwandert und umwickelt, so dass ich dort kein Problem sehe.
Was die Synergien angeht, denke ich, dass ich ein leicht modifiziertes Phänomen verwenden würde, das nur funktioniert, wenn das Zielschiff alle Phänomene auf sich hat.
muss jedes Schiff wissen wie man mit jedem Phänomen umgeht, oder jedes Phänomen muss es wissen über jedes Schiff.
Ich denke, das ist der Schlüssel zu Ihrem Problem und es ist wahr, wenn jede Schiffsphänomen-Interaktion einzigartig ist. In der Tabelle, die Sie erstellt haben, scheint dies der Fall zu sein, also müssen Sie für n Schiffe und m Phänomene n * m Interaktion angeben. Sie haben Recht, ein Wartungsproblem zu riechen.
Der einzige Ausweg besteht darin, Schiffsphänomenwechselwirkungen nicht so einzigartig zu machen, indem man den Effekt von Phänomenen von den Eigenschaften des Schiffes abhängig macht. Zum Beispiel könnten wir sagen, dass nur Schiffe, die mit merkwürdigem Aluminiumoxid gebaut sind, ein schwarzes Loch überleben werden.
Aber wenn Sie nur eine Eigenschaft haben, kaufen Sie nichts, Sie könnten genauso gut angeben, welche Schiffe von schwarzen Löchern betroffen sind. Der Schlüssel ist, dass mehrere Eigenschaften die gleiche kombinatorische Explosion aufweisen.
So werden Schiffe, die mit merkwürdigem Aluminiumoxid gebaut sind, schwarze Löcher überleben, und Schiffe, die ohne gebaut werden, können schneller gehen. 1 Eigenschaft erlaubt Ihnen, 2 Dinge zu spezifizieren (1 Bit = 2 Möglichkeiten). Schiffe mit Corbite-Triebwerken werden in einer Warp-Zone schneller gehen. Schiffe mit Corbite-Triebwerken und eigenartigem Aluminiumoxid werden 50% Schild in einem Nebelfeld usw. erhalten.
Das Hinzufügen von Eigenschaften zu Schiffen ermöglicht es Ihnen, zu vermeiden, wie jedes Phänomen mit jedem Schiff interagiert, und dennoch haben jedes Phänomen und jedes Schiff ein angemessenes Verhalten.
Wenn es M Schiffe gibt, dann brauchen Sie nur log2 (M) Eigenschaften, um jedem Schiff ein einzigartiges Verhalten zu geben.
Ich denke, die Antwort eines Problems hängt davon ab, wie gut die Frage ist.
Ich denke, der Weg zum Design hängt davon ab, was die Frage ist (oder die Frage wird in der Zukunft gehen)
Sie geben eine Tabelle, dann denke ich, dass die Lösung eine Tabelle pflegen und abfragen soll.
Python-Code hier: (nicht getestet und zeigt nur für ein Beispiel)
%Vor%Wenn die Prozessmethode geändert wurde, ändern Sie einfach die Prozessmethode in der Phenomena-Klasse.
Wenn Sie denken, dass das Schiff wissen muss, wie man Phänomene verarbeitet, dann ändern Sie die Prozessmethode in die Schiffsklasse.
oder du denkst, es gibt andere Dinge, nicht nur Phänomene müssen den Status eines Schiffes ändern (wie andere Schiffe, Untiefen), Sie müssen eine Prozesstabelle in der Schiffsklasse pflegen und Phänomene dazu machen,
Sprechen Sie noch einmal, wie Design ist abhängig von der Frage selbst.
Tags und Links c# design-patterns software-design