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, which "knows" everything about the model
# most likely will have just one instance
# contains (e.g., in a dictionary) references to all the instances of class Agent
class Agent:
# each instance represents an agent
# an agent can, among other things, move around according to certain rules
# movement depends on the internal state of the agent,
# but also on the terrain and other information not stored in the Agent instance
Question: where should I put the move instance method?
I thought I’m supposed to limit the dependency of class Agent to classes lower in the hierarchy than itself (i.e., classes whose instances are contained in Agent instances). 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 – so I might as well add to Agent a reference to (and hence a dependency on) World. Is this ok from software design perspective?
The alternative is to put method move in class World, where it won’t cause any additional dependencies. However, class World would then be doing almost all the work, and it seems to me that it would go against the main idea of OOP (which I understand as not to pile all the functionality into one place, but rather contain it in the relevant classes).
Performance considerations are only of minor concern (and I don’t think performance would differ between the two approaches anyway).
EDIT: I misused the words “class hierarchy” above. I wasn’t referring to inheritance hierarchy, just to a bunch of classes whose instances contain each other.
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:
Obviously this is pseudocode, but it demonstrates what’s happening.
Now, where would you add a
move()method? We could add it toPoliceOfficer, but then we would break the encapsulation ofPersonsince a person can also move.But, where would we add an
issueTicket()method? The generalizedPersoncannot issue a ticket, so if we added that to thePersonclass, we’d be breaking the responsibility of it. So instead, we’d add it toPoliceOfficer, 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 ofworld(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.