Cela se fait généralement à l'aide de messages. Vous pouvez trouver beaucoup de détails dans d'autres questions sur ce site, comme ici ou là - bas .
Pour répondre à votre exemple spécifique, une solution consiste à définir une petite Message
classe que vos objets peuvent traiter, par exemple:
struct Message
{
Message(const Objt& sender, const std::string& msg)
: m_sender(&sender)
, m_msg(msg) {}
const Obj* m_sender;
std::string m_msg;
};
void Obj::Process(const Message& msg)
{
for (int i=0; i<m_components.size(); ++i)
{
// let components do some stuff with msg
m_components[i].Process(msg);
}
}
De cette façon, vous ne "polluez" pas votre Obj
interface de classe avec les méthodes liées aux composants. Certains composants peuvent choisir de traiter le message, d'autres peuvent simplement l'ignorer.
Vous pouvez commencer par appeler cette méthode directement à partir d'un autre objet:
Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);
Dans ce cas, obj2
's Physics
choisira le message et effectuera le traitement nécessaire. Une fois terminé, il sera soit:
- Envoyez un message "SetPosition" à vous-même, que le
Position
composant choisira;
- Ou accédez directement au
Position
composant pour les modifications (ce qui est tout à fait faux pour une conception basée uniquement sur des composants, car vous ne pouvez pas supposer que chaque objet a un Position
composant, mais le Position
composant peut être une exigence de Physics
).
C'est généralement une bonne idée de retarder le traitement réel du message à la mise à jour du composant suivant . Le traiter immédiatement pourrait signifier l'envoi de messages à d'autres composants d'autres objets, donc l'envoi d'un seul message pourrait rapidement signifier une pile de spaghetti inextricable.
Vous devrez probablement opter pour un système plus avancé plus tard: files d'attente de messages asynchrones, envoi de messages à un groupe d'objets, enregistrement / désinscription par composant des messages, etc.
La Message
classe peut être un conteneur générique pour une chaîne simple comme indiqué ci-dessus, mais le traitement des chaînes au moment de l'exécution n'est pas vraiment efficace. Vous pouvez opter pour un conteneur de valeurs génériques: chaînes, entiers, flottants ... Avec un nom ou mieux encore, un identifiant, pour distinguer différents types de messages. Ou vous pouvez également dériver une classe de base pour répondre à des besoins spécifiques. Dans votre cas, vous pouvez imaginer un EmitForceMessage
qui dérive Message
et ajoute le vecteur de force souhaité, mais méfiez-vous du coût d' exécution de RTTI si vous le faites.