January 2015
Anthropomorphism Gone Wrong: Poor Motivating Example for OOP
I think anthropomorphism is worst of all. I have now seen programs “trying to do things”, “wanting to do things”, “believing things to be true”, “knowing things” etc. Don’t be so naive as to believe that this use of language is harmless. It invites the programmer to identify himself with the execution of the program and almost forces upon him the use of operational semantics.
I’d like to show an example of anthropomorphism gone wrong. It was given to me as a classic justification of why so called “Object Oriented Programming” is better than procedural programming. You may have learned it in your first lesson about OOP.
(Note: I’m not disparaging OOP here, just the example. For genuine OOP bashing, see here.)
Get the students in their classrooms
The school principal is standing in front of fresh students, who need to go to their respective classrooms. Only the students don’t know which classroom yet. How can we get each student to their assigned classroom?
The school principal have two obvious solutions:
Walking each student to their destination one by one. This analogous to the procedural solution, where the students are just a bag of data to be moved by a
move_students_to_classrooms()
procedure.Telling each student where their assigned classroom is. This analogous to the OOP solution, where the students are objects in their own right and have their own
move_to_classroom()
method. The principal object will just call this method once for each student.
In the real world, the second solution is obviously better. Why walking the students to their classroom when you can just tell them where to go? Moreover, the analogy means this is also true in the programming world: just as obviously, OOP is better than procedural programming.
There is just one little snag…
The limits of analogies
…When pushed too far, analogies break down. And we have pushed this one way too far. The reasons why it is better for the principal to just tell students where to go are rather specific:
First, it makes the whole process a lot faster: walking each student to their classroom while the others wait in the main hall would take forever.
Second, this is much less work for the principal, while not making much of a difference for the students —they still have to walk even if the principal accompanies them.
Third, the students can handle themselves. Whether we like it or not, most are able to follow instructions and go where they are told. We are unlikely to change that fact, so we might as well use it to our advantage. (Think of what it would take to render someone incapable of following instructions…)
As obvious as they are, those reasons do not necessarily carry over to the programming world: program objects are nothing like human beings. They are designed. They’re not sentient. They don’t even do their own work —the CPU does.
It is not at all obvious that the OOP way makes the process faster. We don’t care about which object does what piece of work, since it all goes through the CPU anyway. And if we want the student objects to be dumb, there are little moral or practical barriers to do so. We don’t care about the well being of our programs, only their correctness, performance, and clarity.
Back to first principles
Let’s see how this all works out in (pseudo)code. We’ll explore both the procedural way and the OO way.
The procedural way
Here, the focus is on the action. Namely, moving the students to their classroom. There is no principal to speak of, and the students themselves are just a bag of data: name, age… whatever’s useful for your software. Finally, there’s a registry, which tells where each student should go. Basically an association table between student names and classrooms —your standard library probably provides such a facility.
The (pseudo)code itself may look like this:
struct Student {
String name;
Date birth_date;
// etc.
};
void move_student(Student, Classroom*); // implementation not shown
void move_students_to_classrooms(Registry reg, List<Student> students)
{
for_each (student in students)
{
Classroom* cl = reg[student.name];
move_student(student, cl);
}
}
Note that I just slapped down the code together here. In production code, I would split it in several files as appropriate. Or not. It doesn’t really matter. What does matter is that we can easily find those procedures whenever we want to inspect or modify them.
The OO way
Here, the focus is on the entities: the students and the principal. The classroom I just gloss over, like I did in the procedural example. As objects, the students knows how to handle themselves. They can speak their own name, and move by themselves:
class Student {
public:
String name() { return name; }
void move(Classroom*); // implementation not shown
private:
String name;
Date birth_date;
// etc.
};
The school principal on the other hand, holds the registry and tells each student where to go (instead of moving them himself):
class Principal {
public:
void move_students_to_classrooms(List<Student> students)
{
for_each (student in students)
{
Classroom* cl = reg[student.name()];
student.move(cl);
}
}
private:
Registry reg;
};
Note that contrary to the procedural example, it is pretty clear where each piece of code belongs. It was fixed as soon as we modelled the students and the principal.
Death match!
Beyond the surface syntax, the procedural and OO listings are eerily similar: they use the same procedures, and operate on the same data. We could argue that the OO example is just a coat of syntax sugar over the procedural cake. Which I did, by the way. Overall, it is not at all obvious that the OO code is better than the procedural code:
It is not faster. In both cases, the call to
move_student()
(orstudent.move()
), is synchronous. Both examples are sequential and neither is harder to parallelise than the other (the simplest solution basically boils down to a parallelfor_each
).It is not simpler. Just organised in a more systematic way. Even then, the students could read the registry themselves, reducing the role of the principal further. OOP doesn’t exempt you from thinking your modelling through.
Overall, this is a tie. While I’m sure there are stronger motivating examples for OOP, this one just doesn’t work.
Bottom line
The anthropomorphic analogy is misleading. A computer program is a formal system, not a collection of living beings. Forget that at your peril.