Python – How to Design a Composite Pattern in Python

Architecturedesigndesign-patternsobject-orientedpython

The concept

I'm programming an interface over pygame as a personal project, to make the creation of games easier for me.

So far I managed to design an architecture that behaves like this :

  • Objects are displayable components that can appear and move on the screen
  • Objects can have children objects
  • When an object displays itself, it ask all his children to display themselves on the parent's surface.
  • Objects have three important elements : a callback system, a graphics system and a physics system to respectively act, display and move.

Then, when I want to create a game "scene", I create a "root" object that contains other objects like the player, the mouse, the ground, monsters…

Then I just have to ask the root to display itself, and every object appears recursively.

I designed this without knowing about the composite pattern at first, only the basics of OOP.

My main issue was to make the substitutability property of objects that comes from inheritance to work well with the recursive composition I made.

I mean that I have an "abstract" class called "Object" (I put abstract into quotes because Python doesn't really have such concept) that is inherited by classes like "Image" (to be able to display) or "MovingObject" (to be able to move).
Here, inheritance is meant to extend my object abilities.

But my composite pattern requires that "groups of objects must be considered the same as single objects".

So when I call recursively a method from an object, it calls that method from every child of the object, regardless of the fact that some of them may not have that method.

Example

For instance, let's use this root element :

  • root (Image)
    • player (MovingObject)
    • cloud (MovingObject)
    • background (Image)
    • sun (Image)

Now let's suppose we want to call the method move() on the root element, to make every child move : First, we cannot because root is an Image instance, so it doesn't know the move() method. But even if it was the case, the children "background" and "sun" would not know it.

So I decided to put an empty method move() inside my "abstract" Object class, so every object knows it, even if it doesn't do anything.

The issue is that my Object class is now containing empty methods that it doesn't understand nor needs, only to permit the recursive behavior.

Possible solution

Then I heard about all the "inheritance vs composition" fuss and one of the solutions that came to my mind was to stop using inheritance for Object abilities and use composition instead. That means I would create, for example, a "Body" class, an "Image" class and a "Callback" class that would represent different actions, and then plug these into an Object instance to "equip" it and give it more power.

But then I thought that this will barely change something because I will have to call move(), and then the object will check if it has the Body plug-in, and use it. But it still requires the move() method to be present inside the Object class.

Questions

So I'm turning to you guys to gave me advices about my pattern :

  • Did I understand well how the composite pattern works ?
  • Is my approach correct ?
  • Does the use of "plug-in" classes will help me ?
  • Is the recursive behavior a good idea ?
  • Is there other patterns that are more fitting to my needs ?

I hope you can give me some hints!

Best Answer

In a clean design object in a given tree should be used with an uniform interface. Your doubts reflects a design smell.

The goal of having object trees in scenes is to be able to apply geometric transformations to a group of objects. So if you have objects that can't move, they don't belong to the tree. It does not make sense.

Here is a possible solution. You can have a render class which can have background objects, and the root of the movable objects. All the movable objects can move. A transformation applied to an object is propagated to its children.

To render the scene, you have to render both the unmovable background objects, and the actual scene by calling a render function on the root.