IMHO if a programming philosophy leaves so much room for interpretation and misunderstanding, rants and counter-rants, needs evangelizing to the "dumb masses" what it "actually" means, and all of that hasn't been settled after more than half a century, then it has turned from a philosophy into a religion (or rather, a cult). Just let it rest and move on. It's just a waste of time for everyone involved.
I think it's telling that I'm not able to tell whether you're talking about OOP or FP in this comment.
The Case Against OOP Is Wildly Overstated
211–220 of 312 posts
Re: The Case Against OOP Is Wildly Overstated
#212Since I accepted I was no Einstein I embraced the KISS principle. My code became simpler, easier to understand. Of course it wouldn't get the approval of any software architect or any design pattern fanatic, but I can take any code I wrote 3 years ago, understand it on the fly and edit it with confidence. There are few bugs and they're never a nightmare to find. Now if I have to modify the code I was writing before m…
---
Modelling system state and effects (not data) is a good application. The article criticizes the following statement originally from the Oracle Java docs:
“Objects are key to understanding object-oriented technology. Look around right now and you’ll find many examples of real-world objects: your dog, your desk, your television set, your bicycle … Software objects are conceptually similar to real-world objects.”
I call this "Kindergarten-OO": It attempts to simulate things in the world as stateful Objects, which often should be modeled as plain data with generic data manipulation tools.
System state and effects however are _inherently_ stateful and effectful. File systems, DB/network connections, peripheral devices, that sort of thing. It makes sense to apply OOP here because you want to model these things with state-machine behavior and configuration/constructors in mind.
---
Also there is an interesting move away from "traditional" OOP by modern languages like Go and Rust. They emphasize set-like composition instead of hierarchical inheritance and implicit/user-defined interfaces/traits to both enable common abstractions/naming as well as increasing flexibility.
Both of these things essentially lead to simpler code and IMO address one of the original OO ideas of data-less programming (AKA I only care about this type of behaviour, not the whole thing) but improve the paradigm by rejecting inheritance and explicit (rigid) interface declaration.
Re: The Case Against OOP Is Wildly Overstated
#213The best thing I can say about OOP is that it allows for private state that only certain logic is allowed to know about. In some cases, you just really need that. But I think the biggest problem with OOP is that it encourages a spirit of "eager complication". Take getters/setters as a classic example. Occasionally that pattern can be useful, but standard practice in Java is to never ever create a simple public field.…
Re: The Case Against OOP Is Wildly Overstated
#214The best thing I can say about OOP is that it allows for private state that only certain logic is allowed to know about. In some cases, you just really need that. But I think the biggest problem with OOP is that it encourages a spirit of "eager complication". Take getters/setters as a classic example. Occasionally that pattern can be useful, but standard practice in Java is to never ever create a simple public field.…
After spending 6 months on a modern react project without any classes I had forgotten about Dependency Injection™ in OOP land and gotten used to just doing dependency injection by passing in parameters in a function.
I'm really happy React is introducing functional programming principals to new engineers. Obviously though we still find ways to over complicate things (aka using Redux in a 4 file project with only 5-6 pieces of state to manage) but humans will always find a way to do that :)
Re: The Case Against OOP Is Wildly Overstated
#215Earlier quoted context omitted.
More concretely, for every bit of state in the program I'm interested in two big questions, each with two subquestions: 1.) Who can access this state, and which of those accesses allow writes vs. which are read-only? 2.) What is the lifetime of that state, and what portion of that lifetime is mutable vs. read-only? Java-style OOP can control #1, but makes no distinction between reads & writes. C++ const correctness a…
That would be really cool, but I think enforcing that is undecidable. My gut tells me that having that language feature at compile time is the same as the Halting Problem.
class Symbol {
@init(constructor) originalToken: Token
@init(constructor) position: SourceLocation
@init(Parser.parse) scope: SymbolTable
@init(Parser.parse) declaredType: Type
@init(TypeChecker.typecheck) inferredType: Type
@init(DataFlowAnalyzer.computeLiveness) usages: List
}
And then the type system carries an extra bit of information around for whether a reference is fully-constructed or not, much like const-correctness. Fields marked with @init can only be written on a reference that's not fully-constructed. There's no compile-time enforcement for initialization order, though it'd be pretty easy to do this at runtime (convert them to null-checks or special not-initialized sentinel values). Newly-created references start out with the "initializing" bit set, but once they're returned from a function or passed into a function not in the list of legal accessors, they lose this bit unless explicitly declared.It's basically the same way "mutable" works (in languages that support it), but with a separate state bit in the type system and extra access checks within the mutating functions to make sure they only touch the fields they're declared to touch. You can fake this now by passing mutable references into initialization functions and then only using const, but it's a bit less specific because many classes are designed to be long-term mutable through 1-2 specific public APIs but also need to be mutated internally for deferred initialization, and lumping these use-cases together means that deferred fields can be touched by the public API.
Re: The Case Against OOP Is Wildly Overstated
#216Earlier quoted context omitted.
> "Tightly binding data and code" allows you to maintain complex invariants via code Not sure I understand what this means. Could you give an example?
I'm not the one you're asking. But... Here's a data structure that represents a bill of materials. It has a list of components, and a total cost, which is the sum of the costs of the components on the list. That's the invariant - that the total cost is the sum of the costs of the components in the list. If it's just a structure, then someone can add or remove a component, and forget to update the total cost, and the…
All this said, this is basically OOP in C, and the code is still bound to the data, only it is bound by naming (point_add()) not by a scope (point.add()).
Re: The Case Against OOP Is Wildly Overstated
#217Earlier quoted context omitted.
More concretely, for every bit of state in the program I'm interested in two big questions, each with two subquestions: 1.) Who can access this state, and which of those accesses allow writes vs. which are read-only? 2.) What is the lifetime of that state, and what portion of that lifetime is mutable vs. read-only? Java-style OOP can control #1, but makes no distinction between reads & writes. C++ const correctness a…
Pure functional languages are capable of this, although they likely aren't modeling it as you envision. Pure record constructors let you do perform arbitrary mutation on the stack (e.g. calling other functions to build field values), but once the constructor is called the returned reference is immutable. This is likely cleaner than sharing a reference which is sometimes mutable and sometimes not. Rust of course allow…
Re: The Case Against OOP Is Wildly Overstated
#218The author flatly says "do not use inheritance." Without inheritance and polymorphism, what is left in OOP? If you look at the author's 4 pillars, the only thing left is encapsulation (and "Oversimplified Hot Takes") It seems like the author himself has proven why OOP is bad. You can have encapsulation without OOP. People were doing it in C 40 years ago. And people are doing it in Go right now. No one would call Go a…
Re: The Case Against OOP Is Wildly Overstated
#219Since I accepted I was no Einstein I embraced the KISS principle. My code became simpler, easier to understand. Of course it wouldn't get the approval of any software architect or any design pattern fanatic, but I can take any code I wrote 3 years ago, understand it on the fly and edit it with confidence. There are few bugs and they're never a nightmare to find. Now if I have to modify the code I was writing before m…
Einstein himself would approve this philosophy. As he once said, "Everything should be made as simple as possible, but no simpler." Still, finding the simplest-possible solution to complex problems is often challenging in itself, especially considering what is "simple" or "intuitive" can be highly subjective. Engineers are often uncomfortable thinking about these problems because the domain is shifted somewhat away f…
Re: The Case Against OOP Is Wildly Overstated
#220That said, there are some general policies that seem to make sense, namely the alignment of your object models with the shape of the business problem you are trying to solve. Perhaps your problem domain has the idea of a Customer, but there are really 3 flavors of customer. Many developers would automatically do base/derived here, but in some cases it makes sense to have these as entirely distinct types. Making this determination one way or another is at the very core of the art of software engineering. These decisions have higher order impacts that are impossible to anticipate or understand until you personally go through hell a few times.
Proper management of state is another major concern. In context of OOP, I find it best to centralize state along independent verticals of business functionality. Models like CustomerCheckoutState, UserSignupState, etc. When you centralize all of the state you wish to mutate as part of one business activity into one object instance, it becomes really easy to manage. E.g. serialize your state to database and back every time the user takes an action. You can also leverage the scoped injection functionality available in many DI frameworks to pass a per-user-action state instance into all relevant business services and UI components.
If you can keep your business fact modeling & stateful domains under control, the rest will likely fall into place.