How to change instance functionality based on a component of that instance?

Viewed 55

I have a function Character that is the visual and functional representation of a character on the map. This Character has a CharacterData component that is the actual character being represented. In some rare cases, the CharacterData component has a different idea about how some functionality of the Character should work. I was planning to have the CharacterData class have some variables with the same name as functions in Character, and usually have them set to None, but sometimes have them point to a replacement function. This works, but I feel like there must be a better way. Subclassing CharacterData does not seem like it fits because CharacterData rarely wants to change functionality, and does not need to know anything about Character. Subclassing Character does not seem like if fits because it doesn't know about the different functionality until it gets CharacterData. The class creating Character also doesn't necessarily know that Character's functionality needs changing. Below is an example.

class Character:
    def __init__(self, x: int, y: int, data: CharacterData):
        self.x = x
        self.y = y
        self.data = data
        self.move_action = self.move
        if self.data.move_action is not None:
            self.move_action = self.data.move_action  # replace function if component wants different functionality

    def move(self, *args):  # args catches extra selfs
        self.y += 1

    def perform_move(self):
        self.move_action(self)  # Needs to explicitly send self for replacement function

class CharacterData:
    def __init__(self, name, atk, move_action):
        self.name, self.atk = name, atk
        self.move_action = move_action

# Defined elsewhere and passed to CharacterData
def move_action_replacement(*args):
    args[0].x += 1
    args[0].y += 1
1 Answers

I'm not sure I completely understood your issue here, so please tell me if I missed the point.

class Character:
    def __init__(self, x: int, y: int, data: CharacterData):
        self.x = x
        self.y = y
        self.data = data

    def _default_move(self):
        self.y += 1

    def move(self):
        if self.data.move_action is not None:
            self.data.move_action(self)
        else:
            self._default_move()

class CharacterData:
    def __init__(self, name, atk, move_action=None):
        self.name, self.atk = name, atk
        self.move_action = move_action

# Defined elsewhere and passed to CharacterData
def move_action_replacement(*args):
    args[0].x += 1
    args[0].y += 1

I think the main issue with having "move_action", "perform_move", "move", etc, is that it makes the whole thing very hard to understand. Also with what you wrote if your CharacterData's move_action is changed after the Character's initialisation you won't be able to reflect this change.

Related