@ Loup's Impossible? Like that would stop me.

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.

Edsger D. Dijkstra

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:

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:

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:

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.