0

I'm currently working on platforms in a 2D game in Python. I created 1 superclass: Block. I also created 2 subclasses of Block: Solid (you can't jump through) & Semi_Solid (you can jump through from below). Now I would like to add a class Platform (to allow these blocks to move) that inherits from Solid or Semi_Solid, so its instances could have either the attributes of Solid or Semi_Solid.

So how can I create instances of Platform that can either inherit from Solid or Semi_Solid? Am I obliged to create 2 different classes ?

So I tried:

class Block:
    pass

class Solid(Block):
    pass

class Semi_Solid(Block):
    pass

x = "solid"

class Platform(Solid if x=="solid" else Semi_Solid):
    pass

obj1 = Platform()
x = "semi-solid"
obj2 = Platform()

But the change of x is not taken into account. I got 2 solid platforms

I made several others tests, but none of them was successful. Thank you for any help.

  • You define the class only once. It is decided in that line `class Platform(...)` whether `Platform` will inherit from `Solid` or `Semi_Solid`. That won't change later on when you change `x`. It would also be very confusing behaviour if `Platform()` sometimes resulted in a solid platform and other times in a semi solid platform. You should create two separate classes: `SolidPlatform` and `SemiSolidPlatform`. You can use inheritence (hint: abstract class) and multiple parent classes to *compose* such classes without code repetition. – deceze Apr 09 '20 at 09:19
  • 3
    You may want to consider a different class design. You could, for example, make `Platform` a [mixin class](https://stackoverflow.com/q/533631/1782792), which is a quite common way of modeling attachable behaviors in gameplay programming. You could also favor [composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance) and have `Platform` receive a `Block` instance in its constructor (instead of inheriting from any `Block` type). And you could also avoid `Solid` and `Semi_Solid` by having the traversability of the block as an attribute of `Block`. – jdehesa Apr 09 '20 at 09:20
  • I'll try to experiment with this, thank you. – Clément Darne Apr 09 '20 at 09:24

1 Answers1

3

Inheritance is probably the most overrated OO feature, and alas usually presented as the one-size-fits-all solution in most introductory OO literature. The truth is that the real main OO features are encapsulation (which is not data hiding per se, but the ability to define self-contained componants that group together state and behaviour and abstract implementation details from client code) and polymorphic dispatch (the ability to use objects from different types / with different implementation in a uniform manner by the use of a common API).

At the semantic level, inheritance describes a "is a" relationship - if B inherits from A, then B is a A (cf the liskov substitution principle). So, is your "Platform" a "Solid" or is it a "SemiSolid" ? (hint: this is of course a rethoretical question).

At the implementation level, inheritance is, mostly, a fixed (static) and somehow restricted form of composition / delegation - whatever is not implemented in the child class is delegated to its parent(s). Good OO design is based on separating responsabilites and separate invariants from variants, delegating the "variant" part to another object. This is examplified by the Strategy pattern and its close cousin the State pattern.

Chances are that the answer to your question is mostly to use the Strategy pattern to handle the behavioral difference between solid and semisolid blocks - in which case you'd only have one single Block class responsible of everything that's common to all kind of blocks, and strategies for the "jump thru" behaviour (and other eventual behavioral differences between blocks). Then your Platform class - which doesn't necessarily have to be Block subclass (might or not be the right design depending on the context) - would take either a "jump through" strategie as argument (if you decide to make it a Block) or a Block instance (created with the appropriate strategie) if you decide to not make it a Block subclass.

This not only solves your current problem, but also allow to add new strategies etc without touching existing code, and avoids combinatorial explosion of classes.

NB: you may want to get yourself a copy of the GOF "design patterns", not that much for the patterns catalog itself (however interesting it is), but mostly for the first (long) introductory part of the book which is so far the very best available text on proper OO design.

deceze
  • 510,633
  • 85
  • 743
  • 889
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118