Skip to content Skip to sidebar Skip to footer

Where In Class Hierarchy Should Instance Methods Be Written?

Here's a part of a class 'hierarchy' that I use for a simulation model (my code is in Python, but I think my question isn't language dependent): class World: # highest-level class,

Solution 1:

The thing that you need to take into account is the Single Responsibility Principle. Basically, each class should be responsible for one "thing", and should completely encapsulate that one responsibility. And you should only inherit where the responsibility is extended. You should always be able to say that the extending class is 100% of the parent and more (more in a specific sense). You should never have a situation where the child is a subset of the parent and is "less". So a person extending a world is not a good design since there are aspects of the world that do not relate to a person.

So, if we look at an example, you would put the instance methods on the level that is dictated by the role of that particular class. So, let's take a more defined look at an example:

classPerson:
    name: ""
    birthDate: ""classPoliceOfficerextendsPerson:
    badgeNumber: ""

Obviously this is pseudocode, but it demonstrates what's happening.

Now, where would you add a move() method? We could add it to PoliceOfficer, but then we would break the encapsulation of Person since a person can also move.

classPerson:
    defmove(world):

But, where would we add an issueTicket() method? The generalized Person cannot issue a ticket, so if we added that to the Person class, we'd be breaking the responsibility of it. So instead, we'd add it to PoliceOfficer, since that's where it makes sense.

As far as creating dependency, you should always favor composition over inheritance. So in that sense, there can be as many dependencies as you'd like since they are all soft-dependencies (well, kind of). Since move() takes an instance of world (or an object with the world interface), the dependency is pushed out of the class and into the calling code. So that lets your class's code remain pretty open and dependency-free while still being productive.

It's generally seen as bad practice to hard-code dependencies. But injecting them (through Dependency Injection or Composition) is typically seen as a good thing.

In summary: Put instance methods where it makes logical sense to put them.

Solution 2:

I would put move into the class Agent. If not needed, the Agent shouldn't know the whole world, but only the relevant information he needs to move. If he does know the whole world, however, that is not too bad either. Here are some reasons:

  1. How would you move a single agent if you would put the move method in the World class? Do you want to pass the instance of Agent to be moved to that method? That seems quite ugly.
  2. If you want a single agent to do something, it is nicer to do that in an instance method from an OOP perspective.
  3. You can also call the move method from an instance of another class that does not know the world, but the specific agent.

However, if all your agents move simultaneously and you don't want single agents to move, you could just put one method moveAllAgents into the world class and then iterate through the list of agents and move all of these agents. You then don't need a move method in the Agent class.

Solution 3:

Put the move method where it makes sense, the World can't move, the Agent can.

If you're looking to be able to access world-level functionality, give your constructor method a parameter and pass the world instance in.

world = World()
agent = Agent(world)

This give explicit access to the World from your agent, rather than assuming some sort of hierarchy.

You can take this a step further and require that all game objects within the world take the world as a parameter. You could enforce this by creating a base GameObject class that your Agent, and other game objects, inherit.

classGameObject:
    def__init__(self, world):
        self.world = world

classAgent(GameObject):
    def__init__(self, world, startX, startY):
        # don't forget to call the super and pass the world to itsuper(Agent, self).__init__(world)
        self.startX = startX
        self.startY = startY

    defmove(self):
        print'I can see the world'print self.world

EDIT: To extend my explanation further, if you had an Enemy class and the enemy had a move() method as well, chances are good you may want the enemy to move towards the agent. However, you don't want the enemy asking the world for the position of the agent, instead you can just store a reference to the agent inside the enemy as it's "target" and check it's position any time you need.

classEnemy(GameObject):def__init__(self, world, target):
        super(Agent, self).__init__(world)
        self.target = target

    defmove(self):
        ifself.target.x > self.x:self.x += 5

Solution 4:

But that means move method cannot be in class Agent since it creates a dependency on (at least the interface of) classes that describe terrain, etc

The agent must know about his surroundings. That means that the Agent must use the interfaces that describe the terrain, yes. I wouldn't call that a "dependency", though. There is nothing wrong with assuming that the class that implements ITerrain actually follows the ITerrain interface. :-)

So you put .move() on the agent. The move() method then would check the surroundings and try to move through them according to the rules of movement.

Solution 5:

Not knowing much about your application, you could also allow an Agent to "know" about a certain portion of your world (possibly defined as the maximum area within which movement can be executed, constrained by some rules you implement about how far the Agent can move in any one call to .Move). So the agent might contain a reference to a "Clip Region" of the greater world (this concept stolen from the "Clip Rectangle" used in .net GDI+ Graphics Object).

In a more general sense, I agree with the others: it makes complete sense for the Move method to be defined on the Agent Class, and that it is acceptable for the agent Class to posses an awareness of it's surroundings.

While OOP tends towards minimizing unecessary dependencies, when it makes sense, it makes sense. A person in the real world is aware of their surroundings, and is able to initiat the action required to move within those surroundings from one location to another.

Post a Comment for "Where In Class Hierarchy Should Instance Methods Be Written?"