Composite
You have objects consisting of many element objects. These container objects can themselves be contained as elements in their children. This hierarchy defines collections which should behave as elements. Tools in your program presently have to check whether an object is a container or is one of the value or leaf objects before acting upon them.
def recurse_apply(node, operation):
if isinstance(node, Node):
for child in node.children:
recurse_apply(child, operation)
else:
operation(node)
Use the Render
method on a root class (called component)
for all elements and override rendering for each relevant concrete object.
In computer game engines of the past, it was common to use the Render
method and an Update
method. In many engines,
every node in the scene graph would potentially have children, and so every node
was a composite whether or not it was a leaf. Every frame, the render and
update calls would cascade down from the root1 doing what they needed to
do.
Scene graphs have become something of an anti-pattern in game development now, with the hierarchy of objects in a scene graph seen as a performance problem. As the API calls run across a collection of unrelated instances it nearly guarantees the worst possible cache utilisation. In addition, loading and saving out these hierarchies is non-trivial. Rendering more than once per frame, which can apply to split-screen multi-player games and VR, can become complicated. Adding and removing objects from such a hierarchy often breaks optimisations. Introducing bullets, coins, smoke, and other momentary effects is expensive because they make structural adjustments in almost every frame. If you are culling your render using the hierarchy, it can cause all sorts of complications there as well.
GoF suggests:
- Components should maintain pointers to their parents, meaning the structure is built into the type.
- Use a
GetComposite
call to avoid dynamic casting for composite only methods.
I still have one more issue with the composite pattern. Even without the issues mentioned in the games-scene-graph section, there’s another problem. Maintaining membership in multiple hierarchies or graphs at the same time is tricky. Because parents and children are part of the objects as an invasive element, the hierarchy is something the composite is. You may want this for simplicity’s sake, but when you need representation in multiple structures, it becomes a huge chore to strip it out, or you end up handling it as additional complexity by having one intrusive hierarchy and further hierarchies as externalised structures.
For one example, consider the hierarchy of objects in a scene. Their relationships define where they are. We typically position a character in a vehicle relative to the vehicle. Forget about the human perception of the objects in the hierarchy for a moment. There’s also the hierarchy of meshes and materials. For optimal rendering, it used to be the case that you would batch up renders so expensive context switches were fewer and the more lightweight ones were allowed to happen more frequently. A good rendering hierarchy would be one where the most deleterious procedures were in the first layer, the next most time-consuming in the following layer, and so on. We would collect them into alternating clumps. We had to create an entirely different order from a typical hierarchy traversal, whether depth or breadth. We needed a second hierarchy, but usually, we just kept a list.
“I am one; I may be many, but I act as one all the same.”
Or roots if you separated out and had different scene graphs for UI or other environments to be rendered in a different way.