0
\$\begingroup\$

Let us say that I have a simple architecture, and I have implemented event-based communication between the objects and the components. In this architecture, Game has GameObjects, and the GameObjects have Components. GameObjects have reference to Game and Components have reference to the GameObject.

Game (Update(), ReceiveMessage() -> GameObject (Update(), ReceiveMessage()) -> Component (Update(), ReceiveMessage()).

There is no shared interface between these objects. Is it worth making an interface to implement Update() and ReceiveMessage(), and then only store these interface pointers?

For example, should I store Component pointers in a GameObject, or I should make an EventListener interface with Update() and ReceiveMessage(), and store EventListener pointers? I think this is a good way to force modularity, but maybe I am over complicating it.

\$\endgroup\$
2
\$\begingroup\$

I'd say that if you do indeed want anything that implements EventListener to be eligible for inclusion as a part of GameObject (or Game), then sure, that's a good way to go. However, that also means you could accidentally hook up a GameObject as a Component, or Component as a GameObject of Game. That's probably not what you want. Keep in mind that good design is not infinitely flexible, but imposes restrictions to keep the programming errors uncompilable.

If it were me, I would keep your current architecture, and use an EventListener interface that the base Component and GameObject classes implement (virtually). This will ensure your event handling is consistent while keeping your type safety. It also makes it so that your listeners can subscribe to other publishers than their container with their interface, if you need that functionality. For example, a Component that receives most events from GameObject could directly subscribe to some event from Game if it made sense to do so.

\$\endgroup\$
1
\$\begingroup\$

Two classes should implement the same interface when it makes sense to use both of these classes in the same context.

Also, when a method accepts an EventListener as an argument, that implies that it accepts any EventListener, not just those of a specific type (see Liskov Substitution Principle for more information). So when a GameObject has a List<EventListener> components, it implies that it can have Components as components, but also other GameObjects and even other Games as components. Does that make sense in your software architecture? Maybe it does. But when it does not and you only want to allow components to be components, you should use the correct type and declare it as a List<Component>.

\$\endgroup\$
0
\$\begingroup\$

Is it worth making an interface to implement Update() and ReceiveMessage(), and then only store these interface pointers?

No.

The question is not about worth here, it's really about the design intents.

Although the three classes implement both Update() and ReceiveMessage(), these two method are not linked together conceptually. One is about communication between different objects, while the other is about changing the state on a regular basis. It could make sense to make two interfaces:

class IUpdatable
{
public:
  void Update() = 0;
}

class IEventListener
{
public:
  void ReceiveMessage() = 0;
}

but then you'd have to wonder, can any IEventListener be put in place of another? Most likely. Can any IUpdatable be put in place of another? Most unlikely. The other answers cover that.

So in your situation, I suggest you create the IEventListener interface for inter-object communication, but that you store Component pointers in your GameObjects, and GameObject pointers in your Game.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.