In terms of real programs in the real world, OOP in the form of Java/C++/C# is by far the most successful programming paradigm ever. It's sort of amazing that anyone would call it's track record 'abysmal'
In terms of real programs in the real world, OOP in the form of Java/C++/C# is by far the most successful programming paradigm ever. It's sort of amazing that anyone would call it's track record 'abysmal'
Beyond that, failures all around. On top of my head:
* The insistence of designing systems fragmented in tiny state machines instead of systems processing immutable values passed around wholly and processed by pure functions. In the UI domain we finally got back to our senses with React.
* The insistence that values do not exist, but rather objects = data + code. Leading to brittle abominations like CORBA, agents, etc. For distributed systems, a humble RPC passing values [NOT objects] back and forth is infinitely more valuable.
* The insistence that inheritance is a useful design mechanism, leading to highly coupled codebases. Also, a prime cause for object-relational impedance mismatch.
* The insistence that one should overengineer code for extension without modification at every turn. How about just write the simplest version of the code for today, and refactor it if new requirements pop up.
* The insistence on messaging as fire-and-forget [aka goto] instead of structured function calls. Decades after we've learned why goto-oriented programming is a poor idea.
* The insistence on overusing polymorphism to mock everything in testing. Making it hard to trace how the codebase actually operates, and making it expensive to change the codebase because tests and code are tightly coupled.
* The insistence on overusing polymorphism for all type-based dispatch. Try writing a compiler in a purist OOP style, with a visitor for every type-based switch. Maybe it works in Smalltalk, 'cause in Java/C++/C# it gets boilerplaty really fast. Or even better, "encapsulate" your AST data into "objects hiding their internals" and do all type-based dispatch using vanilla polymorphism. Nothing screams "fun" like wading through tens of files to piece together what should have been a single switch block.
As for UIs, React is OOP. It borrows many ideas from FRP, but at its core are objects that have behaviour and state (components), and a system to orchestrate them. It might not be OO from 1990s Java, but it's definitely OO.
As for the rest, well:
* The concept of "values" form the basis for several important OOP design patterns - such as Value Objects and DTOs.
* Anything but very lightweight inheritance hasn't been in favour for many years, and most good OO developers favour composition.
* Writing the simplest version of code for today is a key tenet of TDD as practiced by several OOP leaders such as Bob Martin.
* GOTO is a permanent and nonreturnable change in program flow with little boundaries. Message passing is entirely different, very much bounded, and executed within control flows.
* Poorly designed tests are coupled heavily to code. Good tests are not. Test fakes can actually simplify debugging by removing and isolating large portions of the system.
* Most OO languages have a switch case that's perfectly acceptable to use. Many real-world applications that have never-ending spec changes (compilers work to a pretty limited and well-defined spec) benefit from using polymorphism to allow easier extensibility.
As a paradigm OOP encompasses a particularly wide spectrum, so while you can of course practice the worst OO has to offer in the form of deep inheritance hierarchies and overuse of design patterns "because we'll need them later", none of this is endemic to OO. In fact there's quite a happy medium combining OO with FP concepts (such as React) - or using OO at the boundaries to help assemble and mutate data before it gets passed to a core of pure functions.
As with everything, it's all about picking the right tool for the job, and getting the most out of the tools you are using. Steak knives and bread knives look almost exactly the same, but one is a lot better for tearing through meat.
Historical OOP orthodoxy took great lengths to contrast objects to values. For example, support for values in OOP mainstream languages has been tepid. With Java still failing to fully support value objects at the language level, project Lombok notwithstanding. With Python adding dataclasses support as late as 2017. There is a reason for decades of dragging the foot: OOP orthodoxy shuns values in favor of objects, defined as "identity, [...] state (data) and behavior (code)" [0]. Insisting on "objects" as the only right way to structure programming _is_ the historical failure of OOP. No amount of late attempts at co-opting other paradigms under the OOP umbrella is going to fix that.
I for one welcome the quiet morphing of OOP into VOP/FP. Makes life as a programmer sane again.