Python OOP – Deciding Which Class Methods Should Affect Multiple Classes

methodsobject-orientedpython

I have a question about object oriented design that is not specific to Python but since my code is in Python, I tagged it as such. How do I decide which of my classes should be responsible for implementing certain functionality that may affect more than one class? What are some good guidelines >(if any) or considerations?

In the below example of a Town class, the behavior of moving to/from a town is split up among the Town class and Inhabitant class. The code works. But I am wondering if there are compelling reasons to assign this behavior to only one class, e.g. allow the Town method addpeople to update the Inhabitant attribute town so it's possible to track each inhabitant's place of residence.

Just trying to get a sense of what would be good practice or smart object oriented design.

class Town():

    def __init__(self,name):
        self.name = name
        self.inhab = []

    def addpeople(self,person):
        self.inhab.append(person)

    def removepeople(self, person):
        self.inhab.remove(person)


class Inhabitant():

    def __init__(self,name):
        self.name = name
        self.town = ""

    def move(self,town):
        if self.town:
            print self.name,"is moving from",self.town.name, "to",town.name
            self.town.removepeople(self)
        else:
            print self.name,"is moving to",town.name
        self.town = town
        town.addpeople(self)

Best Answer

In the below example of a Town class, the behavior of moving to/from a town is split up among the Town class and Inhabitant class. The code works. But I am wondering if there are compelling reasons to assign this behavior to only one class, e.g. allow the Town method addpeople to update the Inhabitant attribute town so it's possible to track each inhabitant's place of residence.

There is actually a good reason to not assign this behavior to only one class and keep it similar to what you have - in object-oriented programming you really want to have each object maintain control over its own data. Your code can easily become very complicated and hard to debug if you change variables outside of the object that the variable belongs to. This is part of the concept of encapsulation that is central to object-oriented programming.

Another part of OOP that is relevant here is abstraction, i.e you want the interaction between objects in your code to make sense at a higher level. Just from looking at this sample code, I would guess that the inhabitants of the towns are more the focus of your program than the towns themselves. If the user is interacting more with the town, I would expect the town to be doing more of the work (Town.addpeople calling Inhabitant.move, for example).

So as you are writing your program, think about which entities will be the ones doing the work. When your code matches your intuitive abstraction of what is happening, it will be much easier for you to keep track of what is going on and not introduce bugs into your code.

One other piece of advice I would give you may or may not be helpful, depending on what this project is for. If this is a program you are going to be actually using, maintaining, and upgrading, write a throwaway prototype first. As Joel Cornett mentioned in the comment, "It depends on the use case", meaning that in order to know the best way to write the code you have to understand the situation. Some of this comes from experience, but unless you are perpetually writing pretty much the same program time and again, you will run into a situation that is not entirely familiar. Writing a prototype is an excellent way to get some experience with the specific problem that you are facing, because you often will not be able to see why one way or another will work better until you are fairly far into writing the program.

One thing that prevents people from creating a good system is that they get attached to their code. So if you choose to write a prototype, realize that it is a throwaway - something you write rapidly just for the purpose of getting a good feel for the problem. It can also help if you realize while you're writing it that it doesn't even have to work, as long as it helps you figure out what it would take for it to work well. Once you've written your prototype, you will have a much better feel for how you should have written your code. Then you can start over and write it correctly much faster.

In short, "It depends on the use case", so make sure you understand the use case and have the code match your understanding.