News for hackers. Stuff that matters.
Re:Pretending OOP Never Happened by zozbot234 on Friday May 15, 2020 @06:13PM (#23198139)
It has little to do with subtyping per se and everything with the way that implementation inheritance, specifically, breaks modularity. The whole idea of inheritance contra composition is that every call to a virtual method-- including calls internal to the class-- goes through a dispatch step so that the actual code that gets executed depends on whether the object is one of a "derived" class, where that method might have been changed in ways that might break any amount of expected invariants. This introduces brittleness both in base class code (which generally has to call methods that might get overridden in derived classes) and in the derived class itself (which has no way of knowing what invariants might be expected of it as part of base class code).

Implementation inheritance is often justified as a way of "reusing" code, but as it turns out, we've merely introduced undesired coupling instead of the seamless reuse we might have expected.

Re:Pretending OOP Never Happened by arc776 on Friday May 15, 2020 @07:47PM (#23198884)
> every call to a virtual method-- including calls internal to the class-- goes through a dispatch step

You've touched on a whole host of performance issues here too. Not only are we making indirection after indirection when accessing virtual methods and data (my poor cache), but invariably these are all stored in heap memory, so we get the worst possible performance profile baked in.

> the actual code that gets executed depends on whether the object is one of a "derived" class, where that method might have been changed in ways that might break any amount of expected invariants.

On top of that inheritance makes the concrete type unknowable at compile time just to add to the pain.

> Implementation inheritance is often justified as a way of "reusing" code, but as it turns out, we've merely introduced undesired coupling instead of the seamless reuse we might have expected.

Yes this 100%. Inheritance actually stands in the way of code reuse. If I have a class that is kind of similar to another, but different enough to "require" being another class, oh dear they're now incompatible. So you try to kind of fit it into a subclass, but it makes more sense in another, and now you're spending mental effort on a problem created by architecture and not the actual thing you're trying to solve.

This is really clear in the game development world, where game objects often just don't fit into the inheritance format, even with multiple inheritance. This is why most complex games are built with the entity component system pattern as it focuses entirely on composition.

Perhaps the crux of the issue is that inheritance encourages design by "identity" (mental concept) rather than "attribute" (data). Thus to solve problems we must mentally classify them into some taxonomy as if they are animal species before we even write any code. This is just extra work in exchange for all the issues mentioned.

Re:Pretending OOP Never Happened by temac on Friday May 15, 2020 @08:42PM (#23199289)
> where that method might have been changed in ways that might break any amount of expected invariants

It's not supposed to, and if it does, the design is clearly broken. Now the (rhetorical) question is: does it happens often. The not so rhetorical question is: is it possible to make it happen rarely. The even more interesting question is: is it easy / more practical compared to alternative approaches, and if not then what is the point.

My opinion on SOLID is that there is precisely one hard "principle", and it is worthwhile: the LSP (it derives directly from logic, that's why). I believe that Open-close is even borderline insane, and that if there is a crazy way to make it not insane, this way is probably applied by so few people that it may well be irrelevant -- most people will try to apply it in ways that will quickly put them at risk for LSP violation, but LSP is far more important (or they will try to apply it in bad places, but that is another story). Plus programs designed with complex hierarchies are often missing the point of execution contexts, and then understanding them is absolute hell -- their original authors sometimes do not understand them themselves. (The rest of SOLID are soft attempts at fixing self-inflicted wounds, sometimes even reasonable if you insist on doing Javaesque / old-C++-esque OOO, but I digress.)

I'm not sure why anybody thought that kind of OOO was a good idea, or that the main characteristic in interesting big programs was the usage of "classes". I even find the suggestion of causation instead of mere correlation dubious; they were already quite a good number of big programs before, and what permitted the explosion of program size was more the ever growing capabilities of computers, that happened, in affordable versions, during the Java-like OOO hype. Besides the simplistic reductionism (we can start with: which classes model entities, which classes model values, which classes are controllers, etc.) which is not too much a big deal in practice, modeling with class diagrams is often missing the river in the middle of the forest for some groups of intertwined trees.

Re:Pretending OOP Never Happened by kragen on Saturday May 16, 2020 @12:01AM (#23200382)
What does "OOO" stand for? Usually it means "out-of-order [execution]" but that doesn't make sense here.

The Smalltalk-80 container hierarchy demonstrates that you can get a lot of mileage out of simple single-dispatch virtual methods with inheritance. The Smalltalk-78 system, which you can try at https://lively-web.org/users/bert/Smalltalk-78.html (although it's not working for me at the moment), got a multiwindow GUI with an IDE running usably on an Intel 8086 with 256KiB of RAM, in only about 100 classes and 2000 methods totaling 200 KiB of code. This is not what I would describe as a "big program", but it is a fairly impressive program nonetheless. Seeing that kind of thing is what led people to adopt object-oriented programming.

Re:Pretending OOP Never Happened by xkriva11 on Saturday May 16, 2020 @04:00AM (#23201366)
Re:Pretending OOP Never Happened by temac on Saturday May 16, 2020 @03:55AM (#23201342)
Yeah I meant OOP but mechanically wrote OOO.
Re:Pretending OOP Never Happened by dragonwriter on Friday May 15, 2020 @06:27PM (#23198260)
> the derived class itself (which has no way of knowing what invariants might be expected of it as part of base class code).

In principle, with a sufficiently-robust type system, the invariants expected by the base class would be encoded in the typing, and the derived class would only be able to narrow them.

(Of course, OOP languages tend to either be untyped or have insufficiently robust type systems for this; robust type systems are more frequently associated with functional languages.)

Re:Pretending OOP Never Happened by temac on Friday May 15, 2020 @09:24PM (#23199571)
If the type system already trivially enforces your custom invariants, aren't the types defined by your classes already in it?
Re:Pretending OOP Never Happened by zokier on Friday May 15, 2020 @07:04PM (#23198537)
> every call to a virtual method-- including calls internal to the class-- goes through a dispatch step

why would you make internal methods virtual though?

Re:Pretending OOP Never Happened by bregma on Friday May 15, 2020 @08:08PM (#23199028)
Virtual methods should only be internal methods. There's no point in making the public API have virtual methods. That would be a bass-ackwards parody of proper OO design.
Re:Pretending OOP Never Happened by kragen on Saturday May 16, 2020 @12:08AM (#23200418)
It's common to put virtual methods in a public API; indeed, in Java we have the "interface" construct consisting entirely of public virtual methods and, occasionally, named constants. (In more recent versions it can also include default implementations.) In Golang the only virtual-method mechanism works by means of a similar interface construct, which also only contains public virtual methods.

So, for example, Java's Map is an interface type with, for example, .get() and .put() methods; TreeMap and HashMap implement those methods differently. There are similar things in Smalltalk-80 and the C++ STL.

So clearly you think Java, Smalltalk, and C++ are "a bass-ackward parody of proper OO design." I'm interested to hear what systems you consider paragons of proper OO design.

Re:Pretending OOP Never Happened by zozbot234 on Saturday May 16, 2020 @08:20AM (#23202800)
Interface inheritance is broadly fine though, because it specifically lacks the conflation of "base class" and "derived class" code. The interface can be assumed to directly imply some invariants, and it's easy to see of any implementation whether it respects them. Everything is nicely localized.
Re:Pretending OOP Never Happened by kragen on Sunday May 17, 2020 @03:44AM (#23210412)
I agree, but I didn't interpret the comment I was responding to as objecting to implementation inheritance as such, rather the contrary: they're objecting to an enormously wide category of OO designs which includes not only interface inheritance but also most, but not all, uses of implementation inheritance; for example, the Smalltalk-80 design that lumps inject and select into some abstract base class for containers.
Re:Pretending OOP Never Happened by anonymoushn on Saturday May 16, 2020 @01:18AM (#23200703)
Wait, really? I don't know much about "proper OO design" but am forced to use Java at work. I commonly use classes that have e.g. a virtual public getFoo, then someone's made a RealFooGetter that actually talks to the network and a CachingFooGetter that delegates to another FooGetter and maintains a cache. Should I ask my coworkers to stop doing such things?
Re:Pretending OOP Never Happened by rramadass on Monday May 18, 2020 @12:49AM (#23218474)
No.

You might find the book Program Development in Java: Abstraction, Specification, and Object-Oriented Design by Barbara Liskov and John Guttag very useful for understanding OOD/OOP.

Re:Pretending OOP Never Happened by rramadass on Monday May 18, 2020 @12:43AM (#23218450)
What in the world are you talking about? The whole idea of a Type/Class Hierarchy is based on publicly accessible virtual methods or more precisely, on dynamic dispatch.
Re:Pretending OOP Never Happened by waltpad on Saturday May 16, 2020 @01:27PM (#23205369)
You didn't motivate your claim. Could you elaborate?