Page dedicated to knowledge related to component design pattern described by Robert Nystrom in the game programming patterns..

Intent

Allow a single entity to span multiple domains without coupling the domains to each other.

The pattern

A single entity spans multiple domains. To keep the domains isolated, the code for each is placed in its own component class. The entity is reduced to a simple container of components.

When to use it

This pattern can be put to good use when any of these are true:

  • A class touches multiple domains which needs to keep decoupled from each other.
  • A class is getting massive and hard to work with.
  • Define a variety of objects that share different capabilities, but using inheritance doesn’t let pick the parts that need to be reused precisely enough.

Keep in mind

The Component pattern adds a good bit of complexity over simply making a class and putting code in it. Each conceptual “object” becomes a cluster of objects that must be instantiated, initialized, and correctly wired together. Communication between the different components becomes more challenging, and controlling how they occupy memory is more complex.

For a large codebase, this complexity may be worth it for the decoupling and code reuse it enables, but take care to ensure you aren’t over-engineering a “solution” to a non-existent problem before applying this pattern.

How does the object get its components?

Answer: outside code provides the components.

  • If the object creates its own components:
    • object always has the components it needs.
    • harder to reconfigure the object.
  • If outside code provides the components:
    • object becomes more flexible.
    • object can be decoupled from the concrete component types.

How does component communicate with each other?

Answer: a mix of the three solutions.

  • use a state:
    • keeps the components decoupled.
    • it requires any information to be pushed to the container object.
    • it makes communication implicit and can mess the order of execution.
  • use references:
    • simple and fast.
    • components are coupled.
  • use events/messages:
    • components are decoupled.
    • the container object is simple.
    • more difficult to implement.

A mix of them for different purposes:

  • State: Shared state is useful for the really basic stuff that you can take for granted that every object has — things like position and size.

  • References: Some domains are distinct but still closely related: animation and rendering, user input and AI, physics and collision.

  • Messaging: useful for “less important” communication. Its fire-and-forget nature is a good fit for things like having an audio component play a sound when a physics component sends a message that the object has collided with something.