If that's what people think OOP is supposed to look like, no wonder they don't like it.
If that's what people think OOP is supposed to look like, no wonder they don't like it.
I've yet to find a non boring, yet descriptive, consensual alternate definition of OOP; the first thought people have is more Java than Smalltalk, and if you exclude inheritance and everything virtual, then it boils down to syntactic sugar for single non-dynamic dispatch, and encapsulation. On the other (but still very sweet) hand, is Ruby OOP when you write 10.times { puts "hello" }? I don't know. Or rather: it's completely arbitrary. Even what is the most useful, when used cleanly is not exclusive of "OOP": invariants are also most important, and arguably way more well known by practitioners, in FP.
Natural language is inherently descriptive. If there is a better "OOP", you just have to fight to make it prevail, so that people think of it when they hear "OOP". Maybe we can make the bad teachers stop in other ways too. Like not even using the word, and making "alternate" approaches (maybe even what you are calling OOP) fashionable.
My problem with the example is that Book should not have a sell() method. Instead, BookStore should have a sell(Book) method. The machinery for how to send in a credit card transaction does not belong in Book. You still need it - it has to live somewhere - but BookStore is the place, not Book.
Ugh.
Polymorphism is far from unique to OO, and predates it.
If by encapsulation you mean information hiding, that's as old as the birth of OO. I've also sometimes encountered an alternate definition of encapsulation that more or less means "information hiding in OO", which seems a bit circular to me?
That doesn't mean OO can't have those features, but you didn't say "OOP usually includes loops and conditionals", because it's too obvious to mention.
You even know what the problems are with the design - you described them quite well. But it's not showing that OOP is horribly flawed. It's just showing you that you need a different design.
Off the top of my head you could survey the landscape of OOP books, blog posts, and code bases throughout history. But it doesn’t really matter—even if you don’t believe me and all OOP programmers believe these are design flaws, then my question remains: what distinguishes “good OO design” from data oriented design?
If you're having a hierarchy just to have a hierarchy, that's about as wise as having gotos just to have gotos. Nobody sane would do that today; maybe we'll get there with hierarchy, too.
I'm not sure what your definition of "data-oriented design" is, so it's hard for me to say how OO is different. I'll take a stab at it anyway, but know in advance that my response may be orthogonal to your question.
You've got data - say, data about a book, in the original example. In the structured programming days, that would be an "entity"; now it's an "object". The difference is that, with private methods, nobody can modify the book's data just by having a reference or a pointer to it. (You could kind of do this in C with a source file that would operate on the structure, and some of the methods being file static. But that doesn't keep any other code that has a .h file from modifying the structure without using the functions.)
The philosophical difference with OO might be that OO uses the compiler to enforce that data is only accessed in approved ways. This is a logical extension of static typing. (Of course, for non-static-typed OO languages, it doesn't happen that way. They enforce it at runtime.)
Or is OO encapsulation + inheritance? One problem is that the OOP community seems split about whether or not inheritance is a defining feature of OOP. And since encapsulation isn't a defining feature, surely it's inheritance. Unfortunately, there's no clear consensus, so OO as a term is not very useful.
> The philosophical difference with OO might be that OO uses the compiler to enforce that data is only accessed in approved ways. This is a logical extension of static typing. (Of course, for non-static-typed OO languages, it doesn't happen that way. They enforce it at runtime.)
This is an interesting distinction; ironically C compilers support encapsulation via opaque pointers while Python doesn't enforce encapsulation even at runtime (it's all based on convention--"consenting adults" and all that).
> I didn't say that inheritance hierarchies are bad. They're great... if you've got entities that are actually in a hierarchy.
I think there are cases where inheritance doesn't overtly bite you, but I've never seen a case where inheritance is actually cleaner than composition (with the exception that in many languages, inheritance is the only way to automatically delegate--they lack something like Go's struct embedding). Specifically in your Statement example, you could have gotten your polymorphism from a Statement interface instead of a base class.
Inheritance seems like just a particular subset of composition and polymorphism--there are cases for which it is appropriate, but why does it need to exist at all when you can get the same benefits from composing the constituent components (components being polymorphism and composition via interfaces/first-class-functions and object-/functional-composition, respectively)? It feels like having an add4() function--it's not always bad (sometimes you really need to add 4 to another number), but the more general 2-argument add() function works just as well in the cases where add4() doesn't overtly bite you, and it's much less likely to be abused.