Wie kann ich eine Gruppe von abgeleiteten, aber ansonsten nicht verwandten Klassen verwalten?
-
19-09-2019 - |
Frage
Je mehr ich über dieses Problem spreche, desto besser verstehe ich es. Ich denke, meine vorherige Frage hat nicht vermittelt, was ich richtig versuche. Ich entschuldige mich dafür.
In meinem Design habe ich GameObjects, die im Wesentlichen eine Aggregationsklasse sind. Alle Funktionen in einem GameObject werden implementiert, indem verschiedene "Funktionen" hinzugefügt werden. Eine Funktion ist eine Unterklasse der Feature -Klasse mit eigenen Mitgliedern und Funktionen. Alle Funktionen können Nachrichten empfangen
class Feature
{
public:
virtual void takeMessage(Message& message) = 0;
};
class VisualFeature : public Feature
{
public:
void takeMessage(Message& message);
private:
RenderContext m_renderer;
};
... Additional Features ...
Funktionen sind Objekte, die für die Koordinierung der verschiedenen Funktionen verantwortlich sind. GameObjects können Funktionen abonnieren, um Nachrichten von ihnen zu empfangen, und Funktionen können GameObjects abonnieren, um die Nachrichten zu verarbeiten, an denen sie interessiert sind.
Also zum Beispiel in diesem Code:
GameObject Square;
VisualFeature* SquareSprite = new VisualFeature();
Square.subscribe(SquareSprite, "MESSAGE_RENDER");
Square.addFeature(SquareSprite);
m_VisualFeatureServer.subscribe(Square, "MESSAGE_RENDER");
Der VisualFeatureServer sendet die Nachricht, die mit "message_render" gebunden ist, die möglicherweise so aussehen kann
class Message
{
public:
std::string getID() {return m_id;}
bool isConsumed() {return m_consumed;}
void consume() {m_consumed = true;}
protected:
bool isConsumed;
std::string m_id;
}
class Message_Render : public Message
{
public:
Message_Render() : m_id("MESSAGE_RENDER"), m_consumed(false) {}
RenderTarget& getRenderTarget() {return m_target;}
private:
RenderTarget& m_target;
};
Wenn der VisualFeatureServer die Message_Render -Klasse an das Square GameObject sendet, leitet sie sie an alle Featurecomponenten weiter, die abonniert sind, um diese bestimmte Nachricht zu erhalten. In diesem Fall empfängt die VisualFeature -Klasse die Nachricht von Message_Render. Hier ist mein Problem, die VisualFeature -Klasse wird eine Nachricht erhalten und dass sie feststellen kann, dass es sich um einen Message_Render von seiner ID handelt. Ich möchte sie in der Lage sein, sie eher als message_render zu behandeln, dann eher eine Nachricht wie SO:
void VisualFeature::takeMessage(Message& message)
{
//Here's the problem, I need a pattern to handle this elegantly
derivedMessage = convertMessageToDerivedType(message);
this->handleDerivedMessageType(derivedMessage);
}
void VisualFeature::handleDerivedMessageType(Message_Render& message)
{
message.getRenderTarget().render(m_renderer);
message.consume();
}
Gibt es eine Möglichkeit, sich elegant mit dem Takemessage -Teil dieses Designs zu befassen?
Lösung
Die andere Antwort war zu aufbläht mit Bearbeitungen, also habe ich eine neue gestartet.
Das Casting, das Sie in der machen receiveMessage()
Funktionen sind definitiv ein Codegeruch.
Ich denke, Sie müssen eine Kombination aus verwenden:
- Abstraktes Fabrikmuster So instanziieren Sie Ihre Objekte (Nachrichten und Komponenten)
- Beobachtermuster auf Nachrichten zu antworten
Die Idee ist, dass jeder Komponententyp nur Nachrichten seines eigenen Typs abonniert und daher nur Nachrichten empfängt, die dafür bestimmt sind. Dies sollte die Notwendigkeit des Gießens beseitigen.
Das Benachrichtigungsobjekt könnte als Beispiel einen Vektor von Notifierobjekten verwenden, die von der Nachrichten -ID indiziert sind. Das Beobachtungsobjekt (die abgeleitete Komponentenklasse) könnte den jeweiligen Notifier abonnieren, der durch seine eigene Nachrichten -ID indiziert ist.
Denken Sie, dass dieses Designmuster helfen würde?
Andere Tipps
Ich bin mir nicht sicher, ob ich Ihre Frage wirklich verstehe, und ich denke, Sie müssen klarstellen, was Sie versuchen, mehr zu erreichen.
Nur ein paar andere Kommentare.
Ich denke nicht, dass die öffentliche Erbschaft (wie Sie implementiert haben) das beste Designmuster hier ist. Die goldene Regel mit öffentlichem Erbe ist, dass sie nur verwendet werden sollte, wenn die abgeleitet Klasse wirklich "ist ein" Objekt der Base Klasse.
Einer der Hauptvorteile der Verwendung der Vererbung in C ++ ist die Implementierung Polymorphismus wo (zum Beispiel) Sie eine Liste von Zeigern haben Base
Objekte und Sie rufen Methoden auf diese Objekte auf, und sie werden in die Relevanten entsandt VisualComponent
und PhysicsComponent
Objektmethoden gegebenenfalls.
Da (in Ihren Worten) sie "nicht verwandte Klassenschnittstellen" haben, werden Sie keinen der Vorteile des Polymorphismus erhalten.
Es hört sich so an, als würden Sie wirklich von der erben Base
Klasse, um die implementieren Mischen Muster.
Vielleicht ist Komposition der bessere Ansatz, bei dem Sie eine Kopie des Base
Klasse (die Sie umbenennen müssen) in der VisualComponent
oder PhysicsComponent
Klasse.
Basierend auf der folgenden Frage:
Wenn ich nur einen Referenz oder einen Zeiger habe, um zu basieren, welche Designoptionen muss ich die Schnittstelle von Visualcomponent oder PhysicsComponent freilegen?
Ist nicht das GameObject
Klasse (in dem Sie instanziieren main()
) Tun Sie das schon für Sie?
Bearbeiten:
Okay, ich denke, ich verstehe jetzt besser, dass die Frage bearbeitet wurde.
Aber ich brauche einen Weg, um alle Komponenten dynamisch im GameObject zu speichern, kann aber dennoch ihre individuellen Schnittstellen verwenden.
Der einzig einfache Weg, den ich sehen kann, ist das Erstellen von a virtual
Methode in Base
Dies wird in jeder abgeleiteten Klasse überschrieben und implementiert das klassenspezifische Verhalten. GameObject
könnte einfach einen Behälter von speichern Base
Zeiger und rufen die an virtual
Methoden (en), die in die abgeleiteten Klassen versandt werden.
Ich würde auch empfehlen, machen Render()
, Move()
und alle nicht virtuellen Methoden private
so, dass die GameObject
Klasse kann nur auf die Öffentlichkeit zugreifen (virtual
) Methoden. Das hilft, die öffentliche Schnittstelle sauber zu halten.
Ich bin mir nicht sicher, ob das hilft.
Bearbeiten 2:
Nach weiteren Diskussion in den Kommentaren klingt es nach dem Fabrikmuster oder der abstraktes Fabrikmuster ist was du brauchst.
Besuchermuster. Wenn ich verstehe, was Sie fragen.
Ich muss zwar wirklich mehr Kontext wissen!
Sich ansehen Boost.Signals
Sie können ein Signal für jeden Nachrichtentyp definieren und zulassen, dass Funktionen Slots (Empfänger) zu ihm hinzufügen. Dies kann ihre Mitgliederfunktionen eines beliebigen Namens oder andere aufrufbare Dinge einer ordnungsgemäßen Signatur sein.