Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Was object-oriented programming a failure? (2015) (quora.com)
94 points by open-source-ux on March 12, 2017 | hide | past | favorite | 119 comments


Why are there still those who think any advocate of OOP is an advocate of "OOP or Nothing! All things are Objects! All things are an instantiation of a class! All things must derive from a root class! etc."?

OOP is a tool. And generally a useful one. Every new programming paradigm at its inception was going to Save The World and make programming A Better Place. Turns out, every one if these paradigms is a tool with uses at various times.

So OOP might only be useful in a limited number of cases. I find it comfortable for categorizing functions: "Here's all the stuff you can do with a String" or "All windows behave like this..." And if your language is dynamic enough, you can add functionality to all Strings, not just your String subclass.

I don't typically find OOP useful for processing many records of data. Perhaps I might compose an object from a record of data so that a user might edit their profile in a GUI, but batch processing doesn't usually lend itself to OOP. But that's my experience and opinion; I bet someone has a good use case for using OOP while dealing with copious amounts of data.

Back to the topic at hand: Was OOP a failure? No more than lathes are failures. Not every handyman needs a lathe; not every programmer needs OOP. But when you do need it, it's a useful tool.


> Why are there still those who think any advocate of OOP is an advocate of "OOP or Nothing! All things are Objects! All things are an instantiation of a class! All things must derive from a root class! etc."?

Because there are many low skill, poorly-informed people who write code still believe these things. In particular that "OOP" is automatically equivalent to "better quality code." It’s a sort of cargo cultism that is present in a few places.


The irony is that it probably does mean better quality code for most people.

Quality in practical outcomes for building projects, that is, not code asthetics.

The problem is that 'functional code' means 'spagetti code' for many 'low skill' people, and 'write once maintain never' perl style code for many other 'smart' people.

Both are rubbish, terrible low quality useless code outcomes.

OOP doesn't fix everything, but its a great lowest common demoninator for code that does the job. Its not exciting. It has stateful bugs. ...but anyone can do it and it gets the job done at scale; orders of magnitude better than any other coding paradigm I'm aware of.

You want a hard problem to solve?

Go figure out a way to make functional programming scale the same way, you'll start a revolution.

...but in my experience, its the frustrated 'smart' folk who want to write their maintain-never code who push FP as a replacement for OOP.


> The problem is that 'functional code' means 'spagetti code' for many 'low skill' people

I don't see how that is the case. Even in the most poorly written function you have code that's working mostly on it's arguments (and maybe some global or something, since it is poorly written) and then returns a value. How is this function connected to the surrounding code? We know the answer, it's called in a single expression and given arguments. You have a single isolated call for a single run of the function.

In OOP you have to both instantiate a class and then call it's methods. By simple line count it is twice as complicated to call as a function, and it is twice as coupled to the code that calls it.

When I look into the internals of functional code I often find myself in a single isolated function and the functions tend to be on the short side. When I look into the internals of OOP code I often find myself in a large method that is part of a class in an inheritance hierarchy and the method uses a bunch of globals (err, I mean "object variables") and I have to read several other methods to understand what's happening, because the method I'm interested in depends on the state left over from other method calls.

In short, objects are a lot more powerful than functions, and thus "low skill" developers have a lot more rope to make spaghetti with.


  In OOP you have to both instantiate a class and then call 
  it's methods. By simple line count it is twice as 
  complicated to call as a function, and it is twice as 
  coupled to the code that calls it.
And in FP a state falls out of the sky?


It's not about hidden state; that's just bad OOP. You shouldn't be looking at the internals, that's the point. It's about modular encapsulation and reusable components.

> a single isolated function and the functions tend to be on the short side

I can almost guarantee that code was not written by an inexperienced or low skill coder.

If it was, you'd have a large complex function that took large complex state as its input, and probably maintained some kind of hidden internal state in some kind of async deferred loop after it returned or some combination of the above.

Have you worked with inexperienced FP programmers?

It's not fun.

Inexperienced OOP is a lot easier to deal with, because the entity level API validation is all you care about; you don't care about how hideously the internals were implemented as long as the tests pass. There can be any number of bugs and spaghetti inside the object, but you don't care, because you don't see it (until it manifests as bugs; but then you just add more tests and make them go away until all the tests pass).

I think there's a pragmatic aspect to OOP that involves large scale organizations collaborating on projects that you're not taking into account.

You could argue that a test suite for FP does the same job, and maybe it does... but the point here is that someone has to define the module level interface, and how it behaves as a state machine (regardless of if the state comes in 'pure' and is passed around or is encapsulated internally); and that's OOP, not an inheritance hierarchy.


> I can almost guarantee that code was not written by an inexperienced or low skill coder. > If it was, you'd have a large complex function that took large complex state as its input, and probably maintained some kind of hidden internal state in some kind of async deferred loop after it returned or some combination of the above. > Have you worked with inexperienced FP programmers? > It's not fun.

No pure FP language will allow you to do that without the function's type signature reflecting that - it would need to return its value in some kind of state/async monad.

Now, if you're talking about FP in a traditionally OOP language, then I agree with you, but that's an apples to oranges comparison.

> Inexperienced OOP is a lot easier to deal with, because the entity level API validation is all you care about; you don't care about how hideously the internals were implemented as long as the tests pass. There can be any number of bugs and spaghetti inside the object, but you don't care, because you don't see it (until it manifests as bugs; but then you just add more tests and make them go away until all the tests pass).

This approach doesn't work for things that are difficult to test. e.g. you can only ensure your types are thread safe by reasoning about their use of locks, volatile, etc., because any attempt to test this is dependent on the implementation details of your runtime (e.g. the JVM) and the amount of load its under. Best case you get a flaky test, worst case you miss a bug that blows up in production.

But this is a tangent, because TDD is not specific to OOP - it works just as well with FP.

> but the point here is that someone has to define the module level interface, and how it behaves as a state machine (regardless of if the state comes in 'pure' and is passed around or is encapsulated internally); and that's OOP, not an inheritance hierarchy.

Pure state is an oxymoron. The idea is not that you pass a mutable argument in, but that you don't have the ability to mutate data at all. At best, you can return some instructions on how to mutate (e.g. the state monad), but its the consumer of your code who decides what to do with that, which prevents you from hiding side-effects, and simplifies testing.

Defining a module level interface and behaviour is just API design, not OOP.


Indeed, but what is more disappointing are the high-skilled, well-informed people who believe these things. In particular that "functional" is automatically equivalent to "better quality code".

It's dogma either way. There is no single true religion just advantages and disadvantages


> Because there are many low skill, poorly-informed people who write code still believe these things. In particular that "OOP" is automatically equivalent to "better quality code."

This about sums up my experience with these discussions, too. I know a lot of people who are absolutely convinced that C++ "is better" than C because you can write object-oriented code in it -- you have native support for "classes and stuff".

The whole "is better than" thing is a little childish to begin with, but this argument is on the thin and confusing border between "meaningless" and "but why?"


> So OOP might only be useful in a limited number of cases. I find it comfortable for categorizing functions: "Here's all the stuff you can do with a String" or "All windows behave like this..."

That's a module. This scope creep is part of the problem with OOP, as it slowly encompasses features that are orthogonal to their main purpose.

OOP is mainly about procedural abstraction: any type implementing a protocol (set of methods) should be indistinguishable from any other type that implements that same protocol. This enables the open extension that characterizes OOP.

So amending your statement to make it truly about objects, "here's all the stuff you can do with String-like values".


One of the core principles of the OOP is encapsulation - your object does not appear in inconsistent state to the outside world. So in sense of the OOP, object methods are all the functions that can have access to the internal state.

Naturally you can do the same in FP with modules, but honestly, how different it is from OOP then (considering that you can use some form of polymorphism)?

For me OOP and FP stand for abstract concepts, not as specific languages.


> One of the core principles of the OOP is encapsulation - your object does not appear in inconsistent state to the outside world.

This is not a defining feature of OOP, because it's a characteristic of abstract data types which predate OO. William Cook managed to distill the essence quite clearly I think [1].

[1] http://www.cs.utexas.edu/~wcook/Drafts/2009/essay.pdf


Exactly. "Everything is an object" was an extremism (today's extremism is the crusade against 'mutable state'), but otherwise OoO has been useful. For example it has seen great success in GUIs.


If you're creating final immutable everything in Java using effective Java chapter 1 immutable objects, sorry but you have the wrong language for the job. Even with Java 8 Optional and Lombok annotations, it opens a can of worms. What is optional and what are sensible defaults for non optional value? These are often non trivial questions in legacy code bases.


And yet Clojure immutable datatypes were written in Java.


I can definitely state that OOP allows to implement GUI. But "great success" I can see only for vendors selling OOP tools for GUI development.

Consider web frameworks. Given a choice I select React and similar functional frameworks than OOP based ones. They are just more productive and scale better.

Or consider QT. My colleagues with a lot of QT experience use QML to create GUI. They avoid any C++ GUI code unless it is really a must.


>Consider web frameworks. Given a choice I select React and similar functional frameworks than OOP based ones. They are just more productive and scale better.

That's because there are no decent OO based web UIs, because the web development experience and stack is a total mess to begin with.


The web UI objects, with their collection of global namespaces for IDs, Classes, JavaScript functions, etc., are aggressively resistant to compontentization.

You can see the disastrous implications of this feature with the ID mangling needed for Asp.net webforms, and the utterly miserable workflow that creates.


ASP.Net Web Controls was an attempt at bringing the desktop technologies like MFC to the web, and it was a horrible horrible idea.

The worst part is that everything else about the original ASP.Net was mostly really good ... but Web Controls completely destroyed it's reputation.


Well, I used to bash C++ and OOP programming till not a long time ago, but I am slowly reconsidering my ideas.

I am now learning Rust, and for this purpose I am trying to implement a small TUI (Text User Interface) framework. In order to get some ideas, I dug in my library and found the Turbo Vision User's Manual that was bundled with Borland Pascal 7.0 [1]. Reading it after so many years made me wonder how could they create such a complex and well-thought framework that was truly usable for your own stuff, given the technology and knowledge available 30 years ago. The text often underlines that a lot of features were possible thanks to the OOP constructs implemented in Turbo Pascal, and I believe them. I think that OOP importance might have faded today, but its historical significance for the development of GUIs and TUIs should not be underestimated.

P.S. I am trying to implement my library in Rust, which is not an OOP language, and so far have found no blocking point: traits and containers are more than adequate replacements for the kind of polymorphism and inheritance used in libraries like Turbo Vision. But my point still stands: once we learned that polymorphism and inheritance were useful to solve some specific problems, we have derived better ways to achieve the same purpose.

[1] https://archive.org/details/bitsavers_borlandTurrogrammingGu...


Ex-Turbo Vision user here .. I continue to be disappointed by modern GUI technologies, by comparison. Every time I pick up some React tutorial, it makes me sad as hell that we've come so far, and yet have to deal with such errant crap as React and the Web itself as a whole.

Its gotten so bad that I feel strongly about killing the browser-based ideology and just returning to native clients for everything .. I mean, its really not an easy decision to "do the whole GUI in Web", even though by now it should be. It just seems like such a mess by comparison with where we should be by now, given the work done decades ago ..


React itself does much less then what component oriented oop frameworks used to do. Achieving same functionality with react is more work. They were not on the Web tho, so the comparison is a bit weird.


"achieving same functionality is more work" this is part of the advantage of react (and fp in general over oo). you don't need to incorporate an entire object to use a single extra function. The increased modularity also allows you to spread the maintenance burden of the project over a much larger community.


People sometimes think that OOP means "OOP or Nothing!" probably because of the popularity of languages that enforce such a mentality (i.e. basically because Java).


> OOP is a tool. And generally a useful one.

OOP is also a tool that others have used to build things that many of us use all the time (I'm looking at you DOM API and CSS) so it's difficult to truly escape from OOP. You may never like it, but learning it will deepen your understanding of many other tools.


> Why are there still those who think any advocate of OOP is an advocate of "OOP or Nothing! All things are Objects!

Well, that's what the OP in OOP means. It means you are oriented towards objects, and away from everything else.

What's the difference between writing a test and doing TDD? In TDD the tests are the center of your universe.

Plus there's the fact that "Everything is an Object" was a very common slogan for OOP.

Of course you can write non-objects in your object oriented code... An object is made of non-objects! But OOP means when you are asking architecture questions you sort out your names and your methods first, and functions fill in the gaps.

Compare to (old school) JavaScript where everything is a function to start, and you add prototypes and methods only when you really need them. (RIP JavaScript, long live OOP/ES6)


Old school JavaScript was already OOP, given that prototype inheritance comes from Self, which was OOP.


Because straw men make the best targets


I view OO as analogous to the earliest control structures added to programming languages. We all remember "GOTO considered harmful", including generations of programmers who weren't alive when the letter was written, and may not have read it. Around that time, language designers realized that GOTOs were used to implement the same patterns over and over: Conditional execution, looping of various kinds. And we ended up with if statements, while loops, for loops, and so on.

Similarly, unfettered access to state was seen to be problematic. If you are implementing a skiplist, you don't want users of the list to be manipulating the links directly. You want to expose only the operations, and have the state accessible only to the implementation. That's encapsulation and modularization. And then inheritance provides a way to modify modularization slightly.

OO is really just a few ideas about organizing code and data, and you are free to use them or not, just as you are still free to use gotos. The hype got out of hand. I think OO madness was the first vastly overhyped software trend. Things got bad with all the Janitor ISA Employee examples got trotted out to explain OO to managers, and then some people believed OO was claiming to excuse programmers from actually developing efficient code. Anyone using, say, Java to build a complex software system realizes that OO is used to model internal things that never get anywhere near a customer-facing API.

There were zealots, there were AbstractFactoryFactoryAdapterFactory nuts, and the backlash against them is totally justified. But the core ideas of OO are just obviously good ones, and usable in non-OO languages too.


I agree with your view. Most of the programming language constructs and ideologies are simply design patterns that been extracted into a single unit and given a particular name.


I find goto useful when writing windows batch files. :-)


>Was object-oriented programming a failure?

Only ignoring the fact that almost ALL programs we care about, from OSes (Windows, OS X) to Games, and from Office applications (MS Office, Open Office) to graphics apps (Photoshop, etc) to NLEs (Premiere, FCPX, Avid), to IDEs (Eclipse, IDEA, Visual Studio), to major compilers, JITs and runtimes (LLVM, Oracle HotSpot, MS CLR) etc, including all major browsers people browse the web through (Explorer, Safari, Chrome, Firefox) and all major Javascript engines that power the web are written in an Object-Oriented way, and usually in the way C++ defines it.


True, kernels create resource abstractions, which are basically objects. But they are written using a variety of approaches, and mostly in C.

Also true, graphical applications have elements that lend themselves to "object" abstractions. But that's not all that goes on under the hood.

I have watched numerous talks from game architects (John Carmack, Jonathan Blow, Casey Muratori, Mike Acton, and many less well-known) and OO just isn't a big concern. "Data-oriented Design" is (to offer a different buzzword).


OO has been so incredibly successful that we now take what it got us for granted as we use it every day. It's been backgrounded.

Here's what Fred Brooks (Mythical Man Month and, more pertinently No Silver Bullet) said at a panel 20 years after NSB:

A burst of papers came and said “Oh yes, there is a silver bullet, and here it is, this is mine!” When ‘96 came and I was writing the new edition of The Mythical Man-Month and did a chapter on that, there was not yet a 10 fold improvement in productivity in software engineering. I would question whether there has been any single technique that has done so since. If so, I would guess it is object oriented programming, as a matter of fact.

https://www.infoq.com/articles/No-Silver-Bullet-Summary


Having been a programmer before OO was invented (or at least available to all) I would have to say no. Was LISP a failure since generations of descendent languages have replaced it? Was Fortran a failure because I don't write iOS apps in it today? Of course not, all of these things are tools you can choose to use or not. Whether something is a failure is up to the programmer and how you wield them. If there was one single language/framework/concept/idea/tool that was perfect we'd all use it - there isn't so we pick something and make use of it.


Just want to add that Lisp is still famous in symbolic AI and Fortran is great for numerical computing (it’s a dependency of numpy)


There's people here claiming that the problem is OOP done wrong. Let's talk about stuff that pretty much every OO language shares:

* syntax that favours inheritance over composition * inability to specify polymorphism over two input types * inability to specify polymorphism over return type (Both of these are symptoms of close identification of type and table) * Syntax that makes generics unwieldy to the point that many think they're just for collections * Inability to make third-party types expose your own interfaces (Clojure manages this. Some languages manage this through monkey-patching, but that's a cure worse than the disease.)

OO is broken. The biggest problem is that we're all so invested in it we have trouble seeing there's another way.


Started programming in 1982. Object oriented programming was a huge advance - especially in developer productivity. A side effect was the dramatic improvement in developer tools: gui builders, IDEs, etc all improved dramatically. Probably the watershed moment for OO was this NeXT video that showed the difference: https://www.youtube.com/watch?v=UGhfB-NICzg


going from emacs to eclipse hardly seems like an improvement.


emacs to vim, however ... ;-)


I think one major problem with OO was that many people did not really get object oriented thinking (and I was probably among them). It is very easy to do objects that are just dummy data container instead of smart things that interact with each other.

Just think how many Java tutorials started with the procedural "Hello World" example and then proceeded to introduce objects and taught the idea of encapsulation by reminding how important it is to protect the private members by creating a list of get/set methods that are used to access them.

This leads to objects with interface like this:

  class Person {
    public void setStomachContents(...);
    public void getStomachContents(...);
    public void setThoughts(...);
  }
While I believe a "proper" object would have interface more like this:

  class Person {
    public void eat(...);
    public void listen(...);
    public Event<ProcessedFoodItem> output;
  }


You are glossing over a lot of design decisions by excluding method arguments.

Whether or not that is a proper solution depends upon the requirements.

A more useful example for beginners would be showing when you want an "eat" on a person class vs an "eaten by" on a food class vs when you need to pull out something more heavy handed.

Eg if your only axis of change is the foods you eat, and behavior is based primarily upon food, person as a dumb bag of data is likely the correct answer. Your behavior should probably be in a food class.


One of my complaints on the subject is that academia is so fascinated with OOP. I took multiple courses on the subject, and the issue I'm hitting is that your "proper" example would probably have failed me a course, because the "bad" example you've given is exactly what we were taught.


Two books that affected my thinking on the subject were Domain Driven Design[1] by Eric Evans and Object Thinking[2] by David West. Many years since I read the books and I don't claim to have studied them in detail so I'm not saying if they were good or bad, but at least I got some ideas out of them.

[1] https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...

[2] https://www.microsoftpressstore.com/store/object-thinking-97...


Was the word 'was' intended to suggest that OO has died off in some way?

Last I checked, nearly everyone is doing OOP.


Even the FP languages that managed industry adoption, support some form of OOP.


Funny how this subject has progressed. I recall writing perl code and being pretty happy with the results until I heard that "functional" programming was crap. Soon after Perl added a "OOPs" layer that coders either rejoiced over or complained about and I couldn't help but think "Do I really need to write code this way?"

After a couple decades of coding I've come to the conclusion that "easy" is almost always better. There isn't much code I wrote 20 years ago that anyone is using today but where it is in use it runs without needing to be updated or maintained.

Most of my apps are now mostly written in Javascript, and I write them very much the same as I did in Perl. I use a mix of both "functional" and "object oriented" methods and don't worry about what others think of it.

Of course, I'm not doing "AI" or anything close to that. I make apps for business management and they care a whole lot more about the UI, reliability, and security, than what the code looks like. The only thing I ever get when showing them code is a glassy eyed look of despair and worry that I won't stop.


I don't think that OO is wrong for all problems, but I do take issue with the fact that it almost always inherently means distributed state. If you have multiple objects, then you probably have distributed state.

Sometimes your problem is inherently a distributed state problem, and that's fine. But OO doesn't care if this is sometimes true. OO pushes people toward distributed state architecture, and it unnecessarily encourages excess state splitting even if the problem inherencies don't warrant it.


One of the common nice features in OOP is polymorphism. You have a list/set/collection of objects which are of the same type, but also of different subtypes. They have the same behavior when calling some method and different behaviors when calling another method.

OOP is not a failure. But overengineering is another thing. (Sometimes I heard "Never write static function/method! Do it in OOP way!", though it doesn't make the code simpler or cleaner)


A counter-point to this is that OOP languages lack support for ad-hoc polymorphism. i.e. the ability for third-party code to define an implementation of an interface for a pre-existing type. The two approaches used to work around this are the adapter pattern, and explicitly taking an object implementing the desired behaviour as a second argument (e.g. Comparators in Java).

In contrast, most (typed) FP languages support typeclasses / traits, which makes a lot of code considerably simpler.


The examples you mention are very good examples where there is much less benefit in polymorphism (ADTs) than you think.

For one, many implementations have actually different names (such as append/add/__setitem__ in python, or push_back/insert/operator[] in C++).

For another, data structures are important for efficiency. It doesn't make sense to abstract those. You shouldn't use an array where a tree or hashmap is more efficient, etc.

By contrast, look at how polymorphism in datastructures is actually done in C++, i.e. STL. That's not your typical OO approach.


It seems like he is ranting about the properties that allows us to keep controll over larger codebase.

Data hiding (and immutability) are good thing even as they sometimes force you duplicate data. So is separation between public and private, no matter what exact syntax is used to achieve it. Interfaces when done right allows you to reduce cognitive overload.

I could go on, but really oop was huge improvement over whar existed before - especially when used where it fits well. Functional programming is improvement too - especially when used where it fits - which is often place where oop fits less. The problem is programmers who don't bother to learn them and then they complain about not understanding them.


As somebody who works on many different OOP-projects, could somebody tell me how I should structure my software if not using OOP?


This is the basic stategy that I try to follow as much as possible:

Classes that hold data have no methods (beyond getters/setters). Classes that implement behavior have no mutable state; they exist as namespaces for the functionality that they provide.

I've been quite successful with this approach. It leads to code that is easier to understand than a big ball of stateful objects.


Someone did this on a project I work on (C#).

Utter nightmare. Loads of repeated code as new programmers didn't know methods existed, loads of extraneous DTO objects, lots of nested single line methods, massive code bloat. Hard to find what actually does stuff, hard to use built in editor functionality.

It's really not a good tactic imo.

I'm undoing it as I go, at one point after having worked on it nearly 4 months according to git I'd still had a net negative on total lines of code, having added a ton of new functionality.

Admittedly, it's one of those projects which has had a bunch of freelancers/contractors work on it, but I personally really don't see what it added to move the methods off the classes apart from confusion, code duplication and code bloat.

I see the need for something, on the startup + enterprisey projects I work on you always seem to end up with at least one mega class that ends up being a beast, the Order, Person, Customer, Job or Project objects are usual culprits, but most other classes don't need much past a few basic methods. I'm just not convinced this programming tactic is it having now seen it in the wild.


>Admittedly, it's one of those projects which has had a bunch of freelancers/contractors work on it, but I personally really don't see what it added to move the methods off the classes apart from confusion, code duplication and code bloat.

Moving the methods off the classes would actually lead to less code duplication -- as now different data that need the same treatment can be handled with the same methods.

"Loads of repeated code as new programmers didn't know methods existed" seems like a bigger issue here...


as now different data that need the same treatment can be handled with the same methods.

That just doesn't happen enough in real life to design around.

In the rare instances when you do need to that, it's trivial to handle with interfaces and a wrapped method that calls a shared function.


>That just doesn't happen enough in real life to design around.

On the contrary, I think it does. Especially for web, enterprise and application programming, 90% of the logic is the same tired data transformations.


It'd be nice if you told us what code you're referring to. I've been giving examples and rough technical outlines how I'd handle problems that needed code sharing. So far you've given us zilch but naysaying.

Have you got any examples?

I've never had to write identical code on an Order class as a Person class. Can you give me an example of 90% of the logic being the same on an Order and a Person class?

Like in C#, the entity framework has mainly done away with all the "tired data transformations". The equivalents like Hibernate, ActiveRecord, etc. have done the same in other languages (and were the trailblazers). When I dip into a data transformation these days it's complex, hand-coded SQL, the tricky bit that takes time, with a simple data class to handle strong typing, trivial to write in a minute or two with snippets and auto-completion.


>It'd be nice if you told us what code you're referring to. I've been giving examples and rough technical outlines how I'd handle problems that needed code sharing. So far you've given us zilch but naysaying.

Sorry, why the complaining? We were talking on a more abstract level. Yourself just gave some anecdote about "Someone did this on a project I work on (C#). Utter nightmare. Loads of repeated code as new programmers didn't know methods existed, loads of extraneous DTO objects, lots of nested single line methods, massive code bloat", that's hardly an example either.

>I've never had to write identical code on an Order class as a Person class. Can you give me an example of 90% of the logic being the same on an Order and a Person class?

Apart from the data contained without, which process would not be the same? Most high level operations would be exactly the same: filtering would be the same (it just needs to accept a predicate), ordering would be the same, serialization would be the same, etc etc.

For a very simple example, you don't need:

  persons.sort("age", sort.DESC)
  orders.sort("created_at", sort.DESC)
etc, you just need:

  sortedPersons = sort(persons, cur -> cur.age, sort.DESC)
  sortedOrders = sort(orders, cur -> cur.created_at, sort.DESC)
And the same function can work with 100s of other classes and containers.


A better solution than that has been in heavy use for a decade in C#. Works on anything that implements `IEnumerable`.

    db.Orders.Sort(o => o.StartDate)
I've also never found ordering code to be the bulk of my logic, but whatever.

Anyway, generics. Without having to muck around with funky designs for your classes.


A bigger issue indeed, and one I've also seen in fully OOP code bases. Entire parallel bits of functionality as developer A has no idea that developer B already made the thing they want


A class that holds data and has no methods other than get/set is basically a struct. Why even bother with the get/set methods?

You assert that code based on this approach is easier to understand, but you don't give a reason why this is so.

This sounds a lot like a pre-OO approach to organizing code and data, and I don't understand the benefits. To use the tried-and-true example: I want my stack state and code to go together. Why would I want to expose the stack state, and keep the code manipulating it separate?


I second this approach. The only state my behavior classes hold are injected dependencies.

The only caveat to this approach is that for some cases you will need a state accumulator of some sort. But those state manipulations are isolated and easy to deal with.


Funny, I tend to go the opposite way (immutable value types, stateful-as-needed behavior classes). I wonder how you argue that you have less stateful objects considering data objects are usually instantiated more than logic objects?


You can apply principles like functional decomposition and modular design without necessarily having an object/class system. Instead of storing some state implicitly within objects and having some sort of this/self available when calling a method on an object, you create data-only variables to hold the state and then pass extra data into and out of your functions as needed instead of calling methods “on” some object.

Traditional procedural, structured programming works this way. You see it at a small scale in countless scripts written in languages like Perl or Python, and at a larger scale in big C programs.

Functional programming also works this way, but typically is even more explicit about it.

The key point is that usually you still want to separate the responsibilities of your program in a modular way, with clean interfaces and hidden implementations. You just use other tools to do it, typically by defining your data structures and algorithms directly, instead of bundling them together in a class or your language’s equivalent.

At first glance that might seem restrictive or less powerful, but in fact it’s the opposite, because it removes OO’s inherent bias towards algorithms where one object is special. I commented about this last point once before, if you’re interested: https://news.ycombinator.com/item?id=8690746

If it helps, you might consider that when you call a method on an object in a typical class-based language, what’s really happening under the hood is that (a) you’re passing an implicit pointer/reference to the object as an extra parameter to the function you call, and (b) which function you call may itself be determined by a look-up process based on the type of the object. If your language didn’t support those functions as built-in features, you could just as well pass any required data from the “object” in and back out again explicitly through function parameters and return values, just as you do for any other data you’re processing or generating in that function, and you could use some sort of look-up table to decide which function to call based on some form of tagging each object with its “type”. Indeed, plenty of people were doing this in C, before what we now call C++ came along and made it more convenient by building the pattern into the language.


I started programming before OOP was widely adopted (C++ was available on some platforms but costly). The C code we worked on wasn't structured very rigorously. There were really no rules or guidelines. I worked on telecommunications software, compilers, and databases.

C structs were used obviously to hold data. Structs were created after analyzing the data to be stored, determining multiplicities, lifetime, etc.

C functions were frequently large and handled numerous concerns. Programmers usually started with one function and split off other functions as needed mostly to take advantage of commonality and to avoid deep nesting.


Consider compression oriented programming: https://mollyrocket.com/casey/stream_0019.html


You can transform:

    o.f(a,b,c)
to

    f(o,a,b,c)
Remove methods and use the object as a data structure.


In my opinion, the main problem with "OOP", and other contemporary religions, e.g. "design patterns", "TDD", etc., is that often you face arguments like "with me, or against me", like if there were no other ways for abstraction, design, QA, etc. Nowdays the buzzword focus moved to "deep learning" and "cloud", our new BDD (buzzword driven development).

P.S. Link to the original interview: http://www.stlport.org/resources/StepanovUSA.html


It's hard to categorize exactly what OOP is exactly, though somehow we are able to discuss this and mostly agree about it's scope.

It's more encompassing today than the traditional model of sending messages to objects to make them change their internal state. This model doesn't exactly lend itself to being great in managing complexity either.

What about the concepts of Encapsulation, Inheritance And Polymorphism? In the form that they are often employed create more complexity and problems than they solve in any non-trivial scope.

It's often said that naming things is one of the hardest problems in computer science. This is particularly applicable to OO style of programming, where the available code reuse capabilities are so limited as to force a programmer to write the same blocks of code over, and over and over again in an increasingly complex codebases, stitching up names along. Seriously, just grep an OO codebase for terms such as "Adapter", or "Strategy".

You should also find it ironic that the only semblance of progress in the OOP landscape in the past decade had nothing at all to do with OOP concepts. But rather it's the incorporation of ideas from Functional Programming into mainstream OO languages that have led to the biggest productivity gains.

Maybe it's a good tool in some contexts, fine. But as the de facto standard across an entire industry of solving problems with Software? A mediocre solution at best!

Object-oriented programming wasn't a failure.

It's an ongoing one.


For computers built in the late 80's and subsequent couple of decades, OOP was a fine abstraction that furthered software development in many ways. When you had one thread running your whole program, it was okay to have mutable data all over the place with complex interactions between parts of your program. Developers could keep it all straight in their heads, so OOP added some structure that was very helpful.

Now that computer hardware manufacturers are moving toward providing more CPU cores that allow for higher degrees of parallelization within single applications - development strategies that make it easier to keep parallel processes from working against each other are becoming more preferable.

Looking at OOP's success as binary is inappropriate. OOP was the right tool for the time and hardware that was available. It served its purpose, but unless its shortcomings on modern and near-future hardware can be addressed... it will fall out of vogue and be regarded as the wrong choice to make.


Ehh, even nowadays, lots of programs aren't CPU bound or for whatever other reason can't benefit from using more than one core per process.


But by the time you are CPU-bound, it's too late to change your programming language and your whole development methodology.

There's an easy case to be made that starting with a functional programming, easily parallelizable language from the beginning helps to mitigate the growing pains that happen later when you discover that you need to take better advantage of the hardware.


"OOP" means, in practice, two very different concepts.

It was probably C++ (and later Java) that interpreted OOP as a mandate that your types should fit in some sort of hierarchy. Inheritance (usually with polymorphism) was seen as a primary way to reuse common code. Fortunately, more and more people are realizing the problems that arise when the real world doesn't map easily to the overly-simple inheritance hierarchy. This type of OOP failed a long time ago.

On the other hand, OOP in the original Alan Kay sense[1][2] is about message passing. This happily lives as a key part of languages such as Ruby.

[1] "I invented the term Object-Oriented and I can tell you I did not have C++ in mind."

[2] http://wiki.c2.com/?AlanKayOnObjects


Part of the problem with OO is the other stuff we heaped on it, while still calling it OO. So, we're not always assessing "pure" OO when we talk about it. For instance, immutability and OO are diametrically opposed concepts.

Objects were supposed to encapsulate data (state) and expose operations that would mutate that state in a controlled fashion. But, we then imposed this immutability requirement, whereby we create these objects very explicitly with long-winded builder patterns, then make copies of the data when necessary,etc. At this point, we have effectively externalized the logic around the object's state and the object itself is nothing more than a C-style struct.

Sure, immutability has its advantages, but we should acknowledge that it's not a OO-friendly concept.


Inheritance is arguably one of those things. It certainly goes against Kay's original scheme, which was more like actors.

To me OOP winds up looking a lot better without the inheritance


Yeah, there are probably degrees of purity. With inheritance, I can at least see how it tries to respect the OO paradigm, by giving you ways to extend without breaking some of the core concepts like encapsulation.

While, on the other end, immutability fundamentally undermines the very premise of OO, which is to model the domain as state and behavior vs treating objects as data wrappers to be manipulated externally.


Fair point. In a true, classic Kay OO model the object is providing a well known interface to guard the mutation of the state. While I am definitely in the "prefer immutability" camp in the FP sense I'd argue that this push in the OO world is to cover up people's bad habits.


Your comment reminds me of the class responsibility discussions we used to have. Should we have gasStation->fillUp(vehicle) or vehicle->fillUp(gasStation)? There is no right answer and depends on the situation.


What seems most natural to me is that the gas station would have a fillUp(IFillableWithGas) method that takes an object with the IFillableWithGas interface, which has a method that returns the amount it was able to take and a method to put that much in. The gas station is the one "doing" the action "to" things that are able to take gas, and it's the gas station's responsibility to know how to pump up the gas from the tank and calculate the price. gasStation->fillUp could look like (leaving it at a gas station with only one pump for simplicity)

    float price = 0;
    while (!vehicle.gasIsFull()){
        Gas gas = this.undergroundGasTank.getGas();
        vehicle.depositGas(gas);
        price += gas.getAmount() * this.gasPricePerGallon;
        this.display(price);
    }
It would be out of place for a car or gas canister class to know how to use the undergroundGasTank object and to calculate the price themselves.


LOL. Who said anything about an underground gas tank, pricing, or a display? If you're adding pricing, you have to add a payment system. It'd be out of place for a Vehicle to know anything about paying a bill. Objects just model reality; they don't have to mirror reality.


It's just a demonstration of how the hierarchy works in the abstract. And it's just displaying the price on the meter, not saying the "car" has to deal with that. The driver would, or the player, if you look at this like game code, which would make sense.


Dude, it was just an example for a style discussion. You're missing the point of the discussion and way over-engineering the example.


I'm just saying I don't think the "There is no right answer" statement works here because there seems to be one right answer to me.


It's weird how inexperienced people are the most rigid and dogmatic. Experienced people know that there are always multiple ways of doing everything.


If you have to resort to implying lack of experience is the only reason someone wouldn't like your example, that doesn't reflect well.


Definitely remember those discussions. Enter the rise of the manager class!


> immutability and OO are diametrically opposed concepts.

Why do you say that? It is completely reasonable to define objects whose state is fixed at creation, hide a representation, and have methods that implement some abstraction. E.g., strings, complex numbers, points in a space, and possibly even more complex things like sets. All the advantages of OO apply, even for objects whose interface does not provide a way to mutate the object.


It may be reasonable, but it's not OO.

There are those who believe that all objects should be immutable, particularly at the domain level. In my view, that defeats the purpose of OO. There really is no state in this case in any meaningful sense because to get a new state, you must create a new object. So, the object is simply a collection of data.

And, even in limited use it's not strictly OO, which is a separate question from whether it can be useful. The entire purpose of OO was to model the real world. In the real world, you have a bus with passengers. When one passenger gets off, you have the same bus with one fewer passengers, not a completely new bus with all but one passenger The turn signal goes on and off vs. having a new bus with a different turn signal state.

So, at the domain level, it completely breaks the OO concept. And, even at the utility level with language constructs like Strings, immutability tends to oppose OO in the strictest sense. In fact, in many languages (like Java) the String is immutable for technical reasons (security, etc.), not because it's particularly good OO design. To wit, you now need a completely parallel "real" class (StringBuilder) to do the real "object work" of the String (append, delete, etc.) without creating object copies everywhere.

Where OO is concerned, immutability is generally an encumberance.


> It may be reasonable, but it's not OO.

Says who? I.e., do you have any references?

It is easy to give examples arguing either side. I agree that your bus example is best modeled as a mutable object. The same bus can have different passengers over time. But a complex number is an example that I think is better thought of as immutable.

You really lost me with your discussion of Strings. Why does "immutability tend to oppose OO in the strictest sense"? I like the distinction between String and StringBuilder, because I want to be able to share a String without worrying about the possibility of an update through some other reference. (This is not an argument about whether String/StringBuilder is OO or not, just a comment on the design of the classes.) Just because it is possible to define mutable objects, it does not follow that all objects should be mutable. By your logic, Integer should also be mutable?


>do you have any references?

My statement rested on the original intent of the OO paradigm, which I described in the subsequent paragraph, including the bus example.

And, the reason you agree that the bus is best modeled as mutable is because it represents this original intent!

You can (and did) make an argument for why you like immutability. There are valid reasons to want to be assured that the data doesn't change, but that doesn't make it good OO design vs just being overlaid to achieve some other purpose. On a side note, it also introduces the question of which copy of the object/state is referenced elsewhere and what the possible inconsistency means. That again points to the tension between OO and immutability.

So, never say never, but the more immutability you have, the more difficult it is to pull off a good OO design overall. If you imagine that every object is immutable, then you really won't have an OO design at all.

>Integer should also be mutable?

I see no inherent reason number objects must be immutable. In fact, if you've ever done math with Java's related BigDecimal class, you quickly realize how much cleaner and less error-prone it would be if that class was mutable, especially when performing a series of operations. That thing has internal state, so why am I forced to continuously assign the results of each operation to a completely new object vs simply having those operations update state?


Because numbers are, by their nature, immutable. I think an object should reflect the externally visible properties of the thing being modeled. To me, that is the whole point of OO. If I have two variables pointing to the same mutable Number, then some very non-intuitive things can happen when the value of the Number is changed through one variable.

> That thing has internal state, so why am I forced to continuously assign the results of each operation to a completely new object vs simply having those operations update state?

The abstract idea of an integer is immutable. Changing 3 to 4 is nonsensical. (It is not analogous to modeling a bus by a Bus object with operations reflecting the coming and going of passengers.) That's why the Java object named Integer should be immutable, and similarly for all the other Number classes, including BigDecimal.

Now if there were an IntegerBuffer class, then sure, there should be methods to change state.

Your argument is that there is internal state, state can be mutable, therefore every object should be mutable. My argument is that the nature of some objects is immutable, and designing a class to reflect that nature is preferable, and not at all at odds with OO.


Well, we basically agree. I grant that there's a better case for numbers being immutable than buses. However, I would say that number "objects" are not meant to model real world numbers as objects per se. Because, sure, you don't morph a 3 into a 4 in the real world, but in code you don't generally require numbers as objects for the purpose of representing such constants. Rather, they are variables with some meaning.

In other words, you don't generally need:

BigDecimal three = new BigDecimal(3);

but

BigDecimal sum = new BigDecimal(0);

Followed by some operation(s).

Sure, depending on naming, you can create confusion with mutable numbers, but that's true for immutable objects as well.

At the end of the day, I am not saying that immutables have no use and one immutable class outright destroys your OO design. I am saying that there is a tension that exists between the two that can easily be seen if you envision a design with 100% immutable classes in the context of OO's raison d'etre.

Bringing it back to the OP, my bigger point was that all of the work involved and "side effects" of layering on immutability are an example of how people assess OO's success through the lens of things that were later added, but were not an intrinsic part of the OO paradigm in the first place.


I started off a recent project in Java (heavy use of RxJava). Over time it migrated to a largely Kotlin codebase, which lends itself well to either OOP or functional.

I realized, so many of my method calls on my objects were only ever used once. Sometimes I'd create a class which just had one method. I had to assign a good name to this class, and it's method. I've since switched to heavy use of extension methods in Kotlin, and found I was mocking less for many tests, and writing less code that accomplished more. And variable naming was simpler.

At the end of the day, a good blend of OOP and functional programming are where it's at


His points aren't OO. He claims overengineering. That can happen in any setting.


True, but it's more likely to happen in an OOP setting. Especially if functions are not first class objects. Then you start wrapping things in classes and turning them into methods when you otherwise wouldn't, and that's how you eventually go down the road of FactoryFactoryFactoryFactories if you're not careful.


It's just likely to happen in a functional setting. E.g. That quadruple indirect function applied via some obscure algebraic pattern can be just as bad as an FactoryFactoryFactoryFactories.

We just don't see it because FP is not as common, but spend any amount of time with some Haskell programmers and you'll see the hazard is just as real.


It's not a failure, and it was an improvement over some paradigms. But it's been oversold and parts of it are terrible. It was sold on the promise of reuse but accomplishes it at the expense of understandability and maintainability. As such it's a huge foot gun: it's very easy to solve complex problems with complex solutions, and that's not a feature it's a bug.

The right level of OO is "very little". Some mostly-FP languages like F# are closer to the ideal level of OO than the mostly-OO languages.

We learned a lot from the Java age, and new languages show it (Swift, Rust, ...)


>It was sold on the promise of reuse but accomplishes it at the expense of understandability and maintainability.

I have never dealt with code in practice that was less maintainable and understandable because of OO. Do you have a specific example?


I think it's usually the case that mutability and inheritance is overused leading to deep and/or unnatural abstractions and code that is hard to reason about. Working with a hierarchy many levels deep with overrides on every level means it's very hard to reason about the code as the call stacks ride an elevator up and down the class hierarchy.

Don't get me wrong - this is not an inherent drawback with OO, this is because it's applied incorrectly. Making bad abstractions or a class hierarchy 10 levels deep is bad, and it's possible that the same inexperienced developer would just make another mistake in another paradigm, leading to some other problem instead. My issue is perhaps more with the design of popular OO languages (Java, C#, C++) than with OO as a concept.

A very good OO language could be made by simply encouraging best practices and removing the biggest warts (no null, immutability by default, support for sum types, encourage pure functions, discourage deep class hierarchies...).

Experienced programmers can write maintainable code in any paradigm, but I think the paradigms should be judged by how well it helps average programmers write maintainable code.


A codebase I work on at the moment has some hierarchies go to at least 6 or 7 levels, and I haven't had a problem with it. The way it's used doesn't hurt the ability to reason about it, it's just a logical progression of categories of objects getting more specific as they branch out. This is at the same time that a lot of composition and components are used.

I'd say excessively deep class hierarchy or bad abstractions are still a lot better than similarly "bad" imperative or other code, not sure about functional though.


> it's just a logical progression of categories of objects getting more specific as they branch out.

That's the benefit of it. On a type abstraction level it's perfect to write Organism > Animal > Dog. That's the benefit of inheritance. The drawbacks are more subtle though and manifest like mad stacktraces that are very brittle to change (what happens when I change what I return from this method? Who are the downstream and upstream users? What does this stacktrace mean? When it becomes difficult to reason about is when you have all these kinds of abstractions executing at the same time - which easily happens.

at Dog.CreateName at Animal.CreateName at Dog.SetNewName at Animal.ResetName at Animal.Create at Organism.Create...


Oortmessen/Aardappel's point here isn't that OOP is dead. I've seen his code (he runs the OSS Cube engine games and has made multiple hobby languages), he writes in C++ and uses objects where needed.

His point is in slavish love of OOP concepts. Of excessive abstraction and storing state everywhere. I've worked on multiple projects where I can only describe the code as "bureaucratic". Oodles of little classes imperiously demanding you work through them to get anywhere inspite of terrible mismatches and the overcomplication it creates.


Pure OOP programming (as Alan Kay intented) never took off in the large scale.

The good thing about these OO-inspired class based languages (C++, Java, etc) is that it has forced people to write their spaghetti code inside modules (classes) with documented interface. They also made using algebraic data types common.


A must read, from Alan Kay himself: http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-...

"The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be."


When using any methodology one could either be successful or fail. OOP, in particular, has been very successful as a user interface engineering methodology. Windows API is one example. Even when taken to the extreme (Smalltalk, Ruby), it can be relatively successful. It is just that as everything else it is only an approximation to the sheer complexity of the world we are trying to model, and so one has to be careful enough not to end up following the road to complexity hell - the one that is paved with the good intentions to try and use the OOP as a way of managing complexity in the first place.


Adding my two cents :

To some point I can follow the argumentation of the author. However, I do not see any superior alternative to the OO paradigm especially combined with Unit testing. Thus, HN, what are the alternatives to OO ?


I think there's a sentiment that OO means deep type hierarchies and using inheritance a lot. Instead of an alternative, per se, people prefer to keep type hierarchies shallow and preferring composition to inheritance to prevent overspecification of types and retain flexibility in the architecture.

In this sense, I think there's a distinction between textbook OO and cultural OO. One is a benign tool in a toolbox that you can reach for and the other is <strawman>when people reject merge requests because 'it looks like type A 'is-a' type B. So make it inherit </strawman>'.


procedural / functional / declarative / concatenative

I would not call these alternatives, as others have commented these are all tools, no one is superior. In my opinion, C++ is a failure. I wouldn't call Object Oriented a failure. I do think OO could be way better.


I've been studying about C++ lately. It has proven to be a lot harder to learn than PHP, JavaScript, Java, C# and Haskell. It's one of the most powerful and widely used languages around however, so calling it a failure seems rather unfair.


Objects are useful. But shouldn't define an entire application unless it's useful to do it that way. In an enormous number of cases it's a better idea to use something else.

So, yes. OOP, being that absolutely everything needs to be an object is absurd and failed. But the people who take it the other direction and say nothing should be an object are just writing even more complicated, more difficult to reason code.

Use tools as tools and drop the ideologies, for a happier and brighter world.


> Underneath all of this lies the fallacy of thinking that you can predict the future needs of your code

The point of OOP isn't to predict the future. You should be actively aware that you cannot know the future and leave youself room for change.


The idea is good, but the wide spread implementations are rather bad.

Funny that with JavaScript a rather undervalued OOP model (prototype based) got such a spread :)


Sturgeon's Law:

"Sure 90% of OO is crap. That's because 90% of everything is crap"

https://en.wikipedia.org/wiki/Sturgeon%27s_law


There are laws for everything :D


Or more precisely, 90% of everything.


> It is as if mathematicians would start with axioms. You do not start with axioms - you start with proofs.

Oh?


No.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: