Discussion about this post

User's avatar
nope's avatar

I should write an article on this someday but I think it's important to emphasize that objects in programming are very much about providing features for programming and shouldn't be thought of as something to model real world phenomena. Whether something inherits from a base class should depend on whether you're trying to reuse or extend previously written code and it makes sense to do so. But even that's an oversimplification because the decision on whether to reuse code and how difficult in general.

Part of the issue is that inheritance functions like a "module system", and most object oriented languages have module systems that aren't object oriented (C++ has header files, in addition to OOP inheritance) and the differences between these two systems add up to complicated tradeoffs. Sometimes it's obvious which one to use and sometimes it isn't.

Expand full comment
Hirako San's avatar

Square and rectangles, even the taxonomy example, while compelling in your writing, could be defended as great use cases for OOP — the article merely expose naïve implementation, not misapplication

Let's illustrate with moderate depth the taxonomy of life, a great use case for OOP.

Hierarchy

Life

├── Plantae

└── Animal

└── Mammal

├── Dog

├── Cat

└── Dolphin

- Core traits (e.g., warm-bloodedness) propagate upward via inheritance

- Shared behaviors (e.g mammal.eat()) are defined at highest applicable level

- Behaviors can be override

These are basically what inheritance gives us. But inheritance != OOP (that's more than that)

So how to deal with Cross-Cutting Concerns (e.g Carnivore, Aquatic, younameit)?

**Composition**

-> Attach traits like Carnivore or Aquatic as components (or properties, if you like, properties can be classes.

Or

**Interfaces**

-> I favour composing, but interfaces can be implemented by classes. Or the interfaces provides the implementation.

See the Serializable interface in Java, for a beautiful abdtraction that saved the need for millions of developers to have a single clue how that works, yet their stated does serialize just fine.

Back to our taxonomy of life:

A Diet component could drive hunt() logic, with context (e.g., "walk" vs. "dive") determined by other traits.

Interfaces: Use ICarnivore or IAquatic to enforce capabilities without inheritance. A dolphin implements both, inheriting swim() from IAquatic and overriding hunt() via Diet.

Flexibility Without Chaos

Common DNA/gene logic lives in Life, inherited by all.

Niche behaviors (e.g., hunting style) are encapsulated in composable modules, avoiding brittle hierarchies.

I would suggested exploring how OOP can address all the concerns your article discussed. It's not to say OOP is a magic bullet everyone should force into everything, I agree with many times it is forced in, induces complexity, bugs for no benefits. But the examples you used could well be turned around with "design patterns", but also good judgement.

Expand full comment
6 more comments...

No posts