5

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!

2
  • Does it make sens to move the root element? Why don't the sun or background have a move method? Commented Jul 15, 2013 at 16:33
  • @Simon : For the context of the example, we will consider "sun" and "background" to be fixed. But this would actually make sense to make them movable. The root element itself can't move, but calling move() on him will ask him to call move() from each of its children. To be honest, the real function used is move_all(). Commented Jul 15, 2013 at 18:05

1 Answer 1

3

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.

3
  • Ok, so in this case, using inheritance on objects doesn't make sense because it would permit some objects to do more things than others, breaking the uniformity of the tree, am I right? If I give my objects all the possible methods they are supposed to be able to do, and then turn on/off (with attributes) some of these abilities for some objects that don't require them, (for example, I have invisible objects that trigger events, but don't have a body nor a spacial position), will I fix my design? I don't want to create a big "god-object". Commented Jul 16, 2013 at 8:28
  • 1
    You shouldn't bother so much about inheritance vs composition. Code consuming objects from the tree should only use a small well defined set of methods (render, move, ...). But each object may have other methods which can be used in other context. For exemple at creation time to help configuring them (ex: a house object can have a set_number_of_doors method). Inheritance vs composition is mainly a code reuse question in python. The main question here should be: which interface for which object in which context? Commented Jul 16, 2013 at 8:37
  • 1
    I get it! I just read this article and I understand that the key is to define a common denominator which will be my abstract class (to answer the question "which interface for which object?"). Thank you for your help! Commented Jul 16, 2013 at 8:50

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