The faster you unlearn OOP, the better for you and your software
dpc.pw
dpc.pw
However, as everyone knows, even a functionally inclined guy like me, there are great OOP codebases all around us.
I wonder if the problem is how OOP modeling is taught. It's easy to project all sorts of everyday instincts onto an object graph that have nothing to do with software design.
I certainly feel that there is something deeply and irredeemably wrong with every learning journey that begins with class Train overriding Vehicle::Move to print "choo choo".
Why? How should it begin?
I recognized this pattern of solving some ridicilous non-problem to exhibit inheritance very early on. The message readers get from that is that inheritance is OOP, and the main thing is to first think what could inherit from what. This is not a fault in OOP itself, just a very bad approach to teaching it.
In my teaching I always wanted to start from a problem that required single dispatch runtime polymorphism and introduced pure abstract classes (interface and implementations) as the means of accomplishing that. I felt that if there was just one C++ class programming pattern they ought to pick up, that was it.
Next step is to replace those few classes with interfaces and test with multiple implementations of these interfaces.
Class done.
Edit: another important part is to separate your classes into two different types so you don’t end up as described in the article.
One type of class is data only, like old style struct.
The other type of class is a repository type, class for working with data classes. Thus you never ask a data class for more data, you ask a repository class for that.
That eliminates the problem that the article describes as complex relations between data classes.
Benefit of this is that you can create data classes from different sources, thus it becomes easier to gradually rewrite your storage as long as the data classes stays the same.
Edit 2: the author of the article seem to describe something what I wrote, a datastore that exposes plain data objects.
Using objects in your design makes your code easier to understand. Using objects does not prevent you from using data oriented architecture.
OOP is better than having lots of global functions that operate on data. It organises your data and code together. It makes APIs easier to use. Call a function, get a response, call functions on response object. It layers your API ordering and makes them inherently understandable. At least good OOP does.
Nowhere in this article prevents you from using objects. https://en.wikipedia.org/wiki/AoS_and_SoA
"Lots of global functions" is not the only alternative though.
To a point.
Past a certain tipping point I find OOP programs become extremely over-abstracted and bloated with way-too-much boilerplate, to the point where it becomes very difficult to work on.
I don't think there's a silver bullet,
so I'm going to just describe how it tends
to work in my code nowadays.
"Having just explained how everything in OOP is _definitely_ wrong, I will now offer my personal philosophy as a replacement"an ORM will slowly infect every part of your code base and it is really hard to refactor when it has. Typically the most common ORM class is an active record (or similar) thingie that encapsulates multiple different steps into one, eg,
* connection handling
* transactions handling
* querying data
* writing data
* hydration of data
* relationship handling
* schema awareness
* data validation
* injection prevention
* cache creation & invalidation (data, query, statement caches etc)
* and more
Basically it is a gigantic GOD class. Of course your code base will suffer.I agree with the article authors solution to use some sort of DataStore that only talks plain objects, however I'm not sure I agree with the statement that it is OOP to be blamed or needs to be unlearned. Author mentions Java, Java community's take on OOP, with de facto standard of using Spring + Hibernate is more likely to be the underlying problem. Just like how ORM's infects everything, Java's take on OOP has unfortunately infected other communities too.
The answer is no. My yardstick is: when it becomes challenging, be pragmatic about it. We write code. We're not etching letters into concrete here. This stuff can change. Make a decision, move forward. When you know more, make a change.
Every system can trigger indecision paralysis - OOP is no better or worse than any other at that, just different.
But when people criticise OOP, I tend to believe the criticism is really about a thoughtless application of design patterns. Just using them because they're patterns, and putting those front and centre, rather than considering what's appropriate within the problem space.
*This does not include hobby projects by FP nerds, but that's not what I get to deal with at a day job now is it?
Also it's sad how often people think they're arguing against OOP while they're actually arguing against Java.
I like Ruby and Ruby's kind of OOP. The kind without ContextFactoryFactories.
And this applies, in software engineering, not only to programming paradigms but also to analysis and design processes and strategies (and while described as being about “OOP”, this is largely about analysis/design process.)
What about Norway?
OOP is how I think. There is no reasoned argument one could make to undo that. No amount of rightness will change my nature, which leaves this as a boastful display of ones ego and not any meaningful movement in computer science or engineering.
Instead: I would love for the author, if they truly know better, to take those specific situations they highlighted and detail ways those could be improved or done differently. Because they are hard problems, and it would be meaningful & educational to explore refactors to improve.
Cause when you say "OOP is how I think. There is no reasoned argument one could make to undo that", it isn't clear what is the chicken and what is the egg. Do you think naturally in a way where OOP is compatible with your thinking process, or did you learn to think that way?
I learned programming first with BASIC, then with C, without any object-oriented trappings. One of my earlier computer science classes was a survey of programming languages of various types, including procedural languages, functional languages and Smalltalk, the grandaddy of object-oriented languages.
Programming has always felt most natural to me in an object-oriented paradigm. My brain categorizes things in the world into object hierarchies very intuitively. While I have no scientific evidence that this is the case, I suspect that it is one of the programming modalities that our brains are best-adapted to naturally—or at least, are likely to be, as everyone's brain is different.
So my take on the OOP debate is that is depended on how you brain works whether you'd consider OOP as being awesome or awful. I for myself hate OOP. I find non OOP Program always more easy to understand, having a better organized code base, and are more easy to handle complex requirements because you can modularize you functions more easily, and by far less boilerplate code.
I really enjoyd LISP. I liked C for it's roughness.
In many ways I believe ABAP is one of the most misunderstood und underestimated language in this list. SAP, which uses ABAP as codebase, is a integrated software system based on >500k database tables, has >5.5 M programs and about 100k "Apps", all within the same system and available on your fingertips. You have dozens of programs working in parallel on the codebase.
In ABAP you have header files, no manifests, no dependencies, no git, no malloc, no gc, no lambdas, no promises, on monads, no clojures and until recently no pointers. Never missed anything of it. The type definitions are stored on the database. You temporal sturctures tables instead of arrays. SQL statements are natively embedded in the language. It supports native rollback an commit functions. The debugger is a dream.
You can understand programming in the most simple way: Processing of data. Development times are short due to the simplicity of the concept.
But the main point is: In spite of it enormous size the system is simple and easily maintainable. Even I as not being a professional programmer have no issue to understand some 30 year old programming code without documentation.
ABAP is procedural.
I actually feel some of the points in the article here. Often you have behavior that spans multiple objects worth of data. And you also often end up with needing to model relations and deep hierarchies with which Objects can become quite verbose for.
Then I learned about Functional Programming, and at first I just couldn't wrap my brain around it at all. Where OOP felt way more intuitive, FP felt foreign. But I persevered, and eventually FP started to really click, and my brain started to be able to now think through problems in an FP way, I started to understand how I could model my business problems with FP, where before I could only imagine how to do so in OOP.
At this point, I find FP much more natural, but I had to train my brain for it. Now that I can figure out how to model things in both OOP and FP, it often seems so much more straightforward to use FP for a lot of problems, over using OOP.
That's why I was interested in asking parent about this. Because I can't tell if my OOP thinking just came from the fact I first learned to model programs using OOP, or simply because OOP is more natural.
Either way, I believe learning to model things using FP is really worth it, at least in my case, now that I can do it, I much prefer it.
The biggest AhHa! moment with FP for me, was actually how similar to OOP it can be in practice, except it's much more flexible. Which is why I now just find it is almost always better for my problems.
Instead of thinking that a Car has methods Drive, OpenDoor, OpenTrunk, AddPassenger, etc. I see that a Car is really just data about a Car's current state. It has a position, a number of passenger, a doorState, a trunkState.
So Car goes from being a remote control for controlling a Car, into data about the current Car state.
And now when you want to change the state of the car, you just go and change it using whatever function you want to manipulate it with. And this function can go anywhere it makes most sense. If the function needs to change the state of the Car and the Driver at the same time, there's no confusion here, Driver is also just data about a driver's state, and you can just have some external function that easily takes a Car and a Driver and just change their state together.