It seems like the main takeaway is that many textbook OO paradigms aren't the most optimized representations of the code. In this case, the cost is dynamic dispatch and pointer-chasing. This is a function of the Shape abstraction, but not the abstraction itself.

But the argument is you're trading some of that performance optimization for maintainability. None of this is exactly news. And while I'm here ranting: I never understood why shapes are the canonical OOP example. Shapes are a closed set of types (yes I'm sure GPT-324 invented a new one) with an open set of operations. There's always going to be one more thing you need to do with those shapes, but you'll never be adding new shapes down the road unless you are still in Kindergarten. OOP is useful for the exact opposite case, where there is a relatively fixed set of operations and you routinely introduce a new subtype that needs to perform all or most of those operations.

I've noticed that most courses that introduce the concept of OOP do so in a way that (perhaps unintentionally) emphasizes the false notion that everything should have an 'x-is-a-y' taxonomy before actually asking the question if that is appropriate. Putting the Cart extends Vehicle before the Horse extends Animal.

> In this case, the cost is dynamic dispatch and pointer-chasing.

To sharpen your statement, the cost is missing the CPU caches, which is often caused by failing to pool allocations and reading indirectly.

> But the argument is you're trading some of that performance optimization for maintainability.

Right, but exactly how much? I would argue "very OOP" design styles neuter your ability to optimize the system, and sometimes necessitate that you are kept at arms-length from the system, only capable of "customizing" it via more abstract API layers. I do believe certain OOP practices can make maintaining software easier, but I also believe we have not figured out how to retain control over the computer in the face of these abstractions.

As an example, Clean Coders advocate for "separation of responsibilities" and often speak in terms like "ownership" or what a function/class "knows about" or "should have to know about." When different classes are given different data-fields in the pursuit of making it clearer (what should exist in that scope,) you are creating a constraint which is virally spread through the codebase which runs counter to what the CPU wants. The CPU wants an array, but you can't have an array because the FileManagerFile can't "know about" the FileManagerFileCache, and the FileManagerFileCache can't known about the FileCache, so now each FileManager "owns" its own cache, which is an entirely separate heap allocation.