2

I'm having problems designing a (Python) switch pattern that works well with object composition. More specifically I want to create a function that gets an 'entity_id' as argument (+other relevant arguments), creates an object and matching components for it (possibly using the additional arguments). Here is a toy example

class Entity:
    def __init__(self,name,animal=None,item=None):
    self.name = name

class Animal: # Animal component
    def __init__(self,legs):
    self.legs = legs

class Item: # Item component
    def __init__(self,quantity):
    self.quantity = quantity

I'd like to have something like:

def place_entity(entity_id,quantity=1):

switch (entity_id):
case 'emu':
    animal_component = Animal(2)
    ent = Entity('Emu', animal_component)
    break

case 'apple':
    item_component = Item(quantity)
    ent = Entity('Apple(s)', item_component )
    break

return(ent)

It would be easy to produce the above using a for loop and if statements, but is there a better way? It should be easy to

  1. add new types of entities (bananas, nails, sharks, etc.),
  2. add new components (Edible for instance, which tells if entity is question is edible and how many calories it contains),

without having to change the code in too many places. Note that components sometimes require additional arguments (that are given in the input of the function).

I have seen switch statements replaced by dictionaries, but my implementation (below) of it turned out horrid. Adding another component requires adding code to every entity function!

Also I don't know how to pass arguments to components in an elegant way. Additional arguments do not work in this implementation. That is if I wanted to create an entity (a batch) of apples (let's say quantity=5) I would have to modify every type of entity function to accept a quantity argument (even if it doesn't use it), or modify the quantity after the entity is created (this is not smart since if one uses if statements then one might as well use for loop+if statements).

def create_entity(entity_id,quantity=None):

    def emu():
        animal_component = Animal(2)
        entity_data = {'name':'Emu','animal_component':animal_component,
                   'item_component':None}
        return(entity_data)

    def apple(quantity=1):
        item_component = Item(quantity)
        entity_data = {'name':'Apple(s)','animal_component':None,
                   'item_component':item_component}
        return(entity_data)

entity_dict = {'emu':emu,'apple':apple}

entity_data = entity_dict[entity_id]()

ent = Entity(entity_data['name'], animal=entity_data['animal_component'],
             item=entity_data['item_component'])
return(ent)
  • 1
    Why don't you just use inheritance like so: `Entity` -> `Animal` (add legs field here) -> `Emu` (define name here)? It looks very weird to create parameters for all the supported entities in the base `Entity` class. – Rusty Dec 15 '15 at 12:50
  • I want to use composition rather than inheritance. I believe it is better (certainly for what I want to do, if not generally). For instance if I want to create a component 'Edible' which tells that the entity in question is edible and holds a variable that tells the amount of calories gained when eating the entity. Emu and apple are both edible, but a chair wouldn't etc. This should be cleaner than multiple inheritance. As in the Zen of Python: 'Flat is better than nested.' – Teece the Wise One Dec 15 '15 at 13:11
  • I still think it's better to use inheritance for entities themselves, but for properties like `Edible` you could have a filed that contains all the properties that an `Entity` has. – Rusty Dec 15 '15 at 13:26
  • The toy example in my post is not that important. What I am actually working on is a roguelike framework for Python 3 with Pygame. So for instance if monsters are defined by their attributes (HP, Strength, etc) and special abilities (dragon breathes fire), why create a subclass for each monster type (like goblin or orc) if what differentiates them is the statistics (and in the case of the dragon a special ability of Fire Breathing that can be grafted on top of the monster entity)? – Teece the Wise One Dec 15 '15 at 13:39

1 Answers1

0

You can simulate a switch statement using this function definition:

def switch(v): yield lambda *c: v in c

Usage would be very close to what you're looking for:

for case in switch (entity_id):

    if case('emu'):
        animal_component = Animal(2)
        ent = Entity('Emu', animal_component)
        break

    if case('apple'):
        item_component = Item(quantity)
        ent = Entity('Apple(s)', item_component )
        break
Alain T.
  • 40,517
  • 4
  • 31
  • 51