refactoring so that multiple changes can be made with one variable?

Steven D'Aprano steve at REMOVEME.cybersource.com.au
Wed Nov 15 02:57:39 EST 2006


On Tue, 14 Nov 2006 10:41:53 -0500, John Salerno wrote:

> My code is below. For now I'm focusing on the lines where health (and 
> armor) are increased in each character class. Let's say I decided to 
> change the amount of increase in the future. As it is now, I'd have to 
> go to each character class and change the number so that each is still 
> in a good relation to the other (right now: 3, 2, 1; later: perhaps 4, 
> 3, 2, 1, if I added a new class -- i.e., change Fighter from 3 to 4, 
> Thief from 2 to 3, in other words increase them all by 1). So instead of 
> changing each one, is there a simple, clean way of just changing a 
> single number so that this change is reflected in all classes? Hope that 
> makes sense.

Cutting your code down to the minimum that exhibits the behaviour you want:

class Character(object):
    def __init__(self, name, strength, dexterity, intelligence):
        self.name = name
        self.health = 10
        # and so on...
        self.adjust_attributes()
    def adjust_attributes(self):
        pass


class Fighter(Character):
    def adjust_attributes(self):
        self.health += 3

class Thief(Character):
    def adjust_attributes(self):
        self.health += 2

etc.

Sounds like you want some sort of factory function that returns a class:

# WARNING: untested
def make_character_class(name, adjustments):
    class klass(Character):
        _adjustments = {}
        def adjust_attributes(self):
            for key, value in self._adjustments.items():
                x = getattr(self, key)
                setattr(self, key, x + item)
 
    setattr(klass, klass.__name__, name)
    setattr(klass, klass._adjustments, adjustments)
    return klass


And now you use it like this:

Fighter = make_character_class('Fighter', {'health': 3})
Thief = make_character_class('Thief', {'health': 2})

Now you can easily change the adjustments, all in just a few lines.

Here's another idea:

character_adjustments = { 'Fighter': {'health': 3}, 
    'Thief': {'health': 2}, 
    'Mage': {'intelligence': 3, 'strength': -1}
    }

and change the make_character_class factory function above to only take a
single argument, name. Now if you decide you want to *programmatically*
adjust the adjustments, you can do this:

# still untested...
for key in character_adjustments:
    for attribute, value in key.items():
        # adjust the values to make up for my poor choices
        character_adjustments[key][attribute] = value + 1

(but of course you must do this BEFORE creating your character classes!)


And last but most certainly not least, you can separate the adjustment
values into (say) an INI file, read them in at run-time and pass those
values to the factory function above. Then write another function which
walks through the INI file, adjusting the values in place as needed. This
is obviously going to take the most work, so I strongly suggest you don't
go down this path unless you really have to.


-- 
Steven D'Aprano 




More information about the Python-list mailing list