Dakotah Lambert

collected musings

Object-Oriented Programming

2024 Feb 21 at 01:53

What is object-oriented programming? My position is that, fundamentally, it is a style of programming that revolves around structured data (objects) that dictate their own behaviors. That is, it has very little to do with any particular language features.

In particular, none of the following are necessary for OOP: special syntax for member functions, subclasses via inheritance, classes in general, or distinct public/private member variables. All of these are useful features in the appropriate context. Member function syntax is convenient. Classes with subclassing form one of many good ways to share functionality between related kinds of objects. Having a distinction between public data and private data helps to maintain a stable API. But none of these things is core to OOP, and all of them can be used to do decidedly non-object-oriented programming.

A command is given: 'Speak!' In response, a cat says 'Meow!'
 and a dog says 'Woof!'

Cats and dogs speak differently.

An object is just some data, structured to represent something real or conceptual. The core of object-oriented programming is that these objects dictate their own behavior. In the image above, someone has asked a cat and a dog to speak. They do so, but they do so differently. The cat says “meow”, while the dog says “woof”. We'll assume that they are not just shouting out some fixed statement, that the actual underlying activity might differ between them. This is the foundation upon which OOP is built.

A programmer familiar with C++ might leap into action, creating an Animal base class inherited by two subclasses: Cat and Dog. This base class essentially declares an interface through a virtual function “speak”, which the subclasses make concrete.

#include <iostream>
class Animal {
public:
    virtual void speak() = 0;
};
class Cat : public Animal {
public:
    void speak() {
        std::cout << "Meow!" << std::endl;
    }
};
class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!" << std::endl;
    }
};
void f(Animal &x) {
    x.speak();
}
int main() {
    Cat cat = Cat();
    Dog dog = Dog();
    f(cat);
    f(dog);
    return 0;
}

This is just about a minimal object-oriented implementation of this system. The function “f” takes an Animal as a parameter, and a Cat or a Dog will behave appropriately in context. Any individual object passed in will smuggle along with it some knowledge of how it should act. This is the fundamental aspect of OOP: you do not act on objects. The objects do the acting.

With that said, I want to emphasize that OOP is not a collection of language features. The same sort of thing can be done in plain C, writing object-oriented code in a language that many would not associate with the paradigm.

#include <stdio.h>
struct animal {
    void (*speak)(void);
};
void speak_cat(void) {
    printf("Meow!\n");
}
void speak_dog(void) {
    printf("Woof!\n");
}
void f(struct animal x) {
    x.speak();
}
int main(void) {
    struct animal cat = (struct animal){speak_cat};
    struct animal dog = (struct animal){speak_dog};
    f(cat);
    f(dog);
    return 0;
}

Here still, the individual objects are in charge of their own actions. This is still object-oriented programming. The mechanism is different. Rather than building a hierarchy of related classes, only the interface and functionality is specified. Creating objects is a matter of assembling functionalities into individuals, using function pointers. Yet still it is the individual objects that act. The functions into which they are passed have no way of knowing what they will do.

I would say that “object-oriented” is not a type of programming language. Rather it is a type of programming. You need only two things in order to do it. You need structure, and you need the ability to carry functionality within that structure. Think about Lua. In Lua, basically the only built-in data structure is the “table”, which we can think of as an associative array, a dictionary. Functions can be passed around. We have object-oriented code.

function speak_cat()
    print("Meow!")
end
function speak_dog()
    print("Woof!")
end
function f(x)
    x.speak()
end
cat = {["speak"]=speak_cat}
dog = {["speak"]=speak_dog}
f(cat)
f(dog)

Everything put on top of this is simply to make life easier. Sometimes it succeeds in that. Rather than getting hung up on the specific implementation details of a given language, I feel it is important to take a step back. Understand what it is you are trying to accomplish. Write code that solves the problem in a way that makes sense to you. Do not start with the mindset that you will be doing object-oriented programming and therefore need an “object-oriented programming language” like C++ or Java. Start with the problem. Solve the problem. If your data is structured and carrying around functionality, then congratulations, you have written object-oriented code.