Object-Oriented Programming Sucks

by Nox

on 10.19.12

Critiques of object-oriented programming are rather in vogue. OOP hides state and makes programs difficult to reason about. OOP is not performant. Object-oriented programs lack flexibility and reusability. In sum, OOP does not deliver. These things are true enough, but I've yet to see them particularly well elaborated upon. On both sides of the aisle we have evangelists who want nothing more than to stroke themselves in regard to their enlightened state of mind, and of course to rebuke those in disagreement with a barely coherent soup of acronyms and theoretical bullshit—functional weenies to the left, object weenies to the right, and not a shred of pragmatism between them.

As a formerly dedicated OOP practitioner who has spent countless hours architecting tearjearkingly beautiful object-oriented systems; I understand the appeal. The rigorous structure and purported benefits of OOP bring comfort and familiarity to a world of code that is otherwise so far removed from reality that it is very hard to understand or identify with. It certainly could be a silver bullet, if it were to actually deliver on any of the benefits that it claimed to. I'll grant that OOP may be a good fit for some problems, the C++ standard containers are certainly nice. You may have noticed, however, that the standard algorithms are implemented in terms of free functions, not member functions. Why is that?

Suppose we can agree that the number of verbs that can act on any given noun is effectively infinite. Context can determine a subset of appropriate verbs for a given scenario, but this subset is still effectively infinite. Jack, a human boy, can, among an infinitude of other things, run, jump, slide, shit, smoke, and cough. If we say that Jack can only run and jump, he'll make a decent olympic hurdler, but a piss-poor dolphin cosmetologist. That’s OK, if Jack doesn’t mind running and jumping for the remainder of eternity, but this does severely limit Jack’s future outside of various track and field events. In much the same way, let’s stop pretending that binding a small subset of possible verbs (functions), to our nouns (data), creates more modular, flexible, or reusable code. It does not.

Data defines a potential for action.

Data is not action in and of itself, but does provide for an infinitude of potentialities to be achieved via functions acting across that data. Binding a finite set of functions to the data that it is intended to operate on is the antithesis of reusability. In light of the direction that most languages in widespread use have taken, this philosophy is admittedly somewhat anachronistic.

As a concrete example, consider some representation of geometry in a simulation environment:

struct Geometry{
    ShaderID shader;
    Texture texture;
    std::vector<Vertex> vertices;

This definition of Geometry is purely data, unencumbered by the strictures of any particular implementation of the functions that we currently intend to operate across this data.

We can just as easily pass this data to some OpenGL function that prepares it for rendering as we can pass it to an analogous DirectX function. We can also, without regard for the rendering backend at all, pass it to some function that creates axis aligned bounding boxes for the set of vertices held by the Geometry.

AABB aabb = GenerateAABB(Geometry geom);
RenderBatch gl_batch = OpenGL::BuildRenderBatch(Geometry geom, AABB aabb);
RenderBatch dx_batch = DirectX::BuildRenderBatch(Geometry geom, AABB aabb);

Now say that we want to parallelize our AABB generation across all known instances of geometry. That’s easy enough:

std::vector<AABB> aabbs = GenerateAABBs(std::vector<Geometry> geoms);

We can also use our generic Geometry data in some way completely unrelated to either OpenGL, DirectX, or even rendering in general—automatic convex hull generation for example—in a completely natural way, without the baggage of accompanying member functions. Well-designed data doesn’t care.

Had we instead defined GLGeometry and DXGeometry in a typically object-oriented manner, we would have had something like this:

class Geometry{
        virtual RenderBatch BuildRenderBatch();
        virtual AABB GenerateAABB();

class GLGeometry : public Geometry{
        RenderBatch BuildRenderBatch();
        AABB GenerateAABB();

        //members are irrelevant

class DXGeometry : public Geometry{
        RenderBatch BuildRenderBatch();
        AABB GenerateAABB();

        //members are irrelevant 

Now how do we parallelize AABB generation? Whoops! We don’t, unless we had that in mind from the beginning. This is where OOP falls apart.

Object-oriented programming provides a subjective syntactic improvement over the syntax afforded by a more functional apprach, while additionally limiting the potentialities of a given set of data, and obfuscating the true nature of that data, by design. I don’t like it.

Observe that the object-oriented syntax for a method call:


is just syntactic sugar for this:

Verb(Noun, argument);

While this is readily apparent to anyone who has worked in a non-OO language, it is not incredibly obvious, or apparently relevant if you were to figure it out on your own. The object-oriented style conveys no discernible benefits, while the functional style provides several:

  1. Apply the same action to disparate objects, without sharing an inheritance hierarchy or passing by reference:

    template<typename T>
    Verb(T Noun, argument); 
  2. Apply the same action to a group of objects in an optimal manner:

    OptimalGroupVerb(Container<Noun> objects, argument(s));
  3. Apply an alternatively implemented action to an object, without being granted access to private members:

    Verb2(Noun, argument);

I have not nearly exhausted the disadvantages of OOP. The problems with inheritance are manifest, leading to the traditional virtue to favor composition over inheritance. Self-mutation will dig its own grave, moreover, it’s already been addressed. I think these notions get in the way of the more important issue—that OOP is fundamentally flawed at an even deeper level. How OOP proponents succeeded in convincing the world that object-oriented programming provided anything more than a new set of rules and regulations for the bureaucrats to wank over is beyond me.