Hacker News new | ask | show | jobs
by andai 903 days ago
>Object oriented programming, polymorphism in particular, is essential to nearly any large, complex software system. Without it, decoupling different system components is difficult. (Update in 2017: I no longer agree with this statement.)

The author doesn't seem to elaborate on this. I was taught OOP in university and then promptly learned that it's frowned upon in performance sensitive code, which is my main interest in programming.

(And that it apparently doesn't even achieve its stated goal of making the code easier to understand -- I've certainly had the experience of wading through a deep inheritance hierarchy (or call stack) looking for the "actual code that actually runs"...)

I'd love to hear an elaboration on that idea (OOP is essential for decoupling components) and its counterargument (decoupling can apparently be done just fine without OOP?).

6 comments

Central to OOP is message passing between objects. Few languages utilize message passing, so it seems you can decouple components without OOP just fine.
>Few languages utilize message passing

Yet most of them call themselves object-oriented!

I'm reminded of the Alan Kay quote, "I invented the term object-oriented, and I can tell you that C++ wasn't what I had in mind."

Unfortunately, 30+ years on he's never managed to explain to more than a handful of people what it was that he did have in mind.
Is Smalltalk’s message passing really all that different from methods from other OOP languages? Seems like quite an arbitrary distinction to me.
All OOP languages use message passing, but perhaps you mean languages with objects that are not oriented?

While not completely identical to Smalltalk's message passing design, the Qt project once went to all the trouble of building their own compiler just to be able to graft message passing onto C++. I think that goes to show that there really is a difference – otherwise, why not use the standard constructs C++ offered?

Whether or not that difference makes for better software is debatable. It does seem that at one time it did lend itself exceptionally well to GUI programming. NeXTSTEP/macOS/iOS also would never have been what they are without OOP. But we've also learned some programming tricks along the way, so it may not even shine there anymore. Swift, for example, has given up on OOP (except where @objc mode is enabled) and it seems like it manages to do quite well with GUIs (granted, having @objc mode to fall back on clouds that somewhat).

I mean that, to me, the difference between message passing and method calling is not significant enough to say that these languages are following different programming paradigms - like you say OOP vs languages with objects.

Afaict the difference between Smalltalk and Objective-C style message passing and Java and C# style method calling is purely syntactic.

> Afaict the difference between Smalltalk and Objective-C style message passing and Java and C# style method calling is purely syntactic.

doesNotUnderstand:/forwardInvocation: isn't different? That is not just syntactical. How would you even begin to orient your objects without like functionality?

I agree that if you squint really hard they look the same. But if we say "they are all the same", what are you trying to communicate when you say OOP? Virtually all programming languages we use have objects. You may as well drop the OO and just use "programming". It would communicate the same intent.

Yeah, the callee doesn’t have to have the method defined for it to be called and Smalltalk objects have a default you can use to do things with messages you don’t handle for example forward them on to another object.
You can definitely do that in Java too. This is how, e.g., Spring framework handles transactional behavior. It injects a proxy for each bean that is marked as @Transactional and the proxy object handles the coordination with the transaction manager and passes all the arguments to the real object.
Right but it’s not a core part of the language, anything Turing complete can implement this sort of thing. The comment I was replying to was asking about the difference between method calls and message passing.
It's a little different, and a little better. But not that much. In fact, right after that famous quote, Alan goes on to say: "I have many of the same feelings about Smalltalk".

https://www.youtube.com/watch?v=oKg1hTOQXoY&t=653s

(go back a little for the original quote)

For me, the problem with OO as is is that it really is just a Better Old Thing, not an actual New Thing, as the "object" part is quite underdeveloped.

When you have an object-oriented system, it is objects that are connected (somehow) and that then communicate (somehow).

But our object-oriented languages really only still have algorithms (procedures) and data structures. We do get to group and scope those procedures with the data structures, but that's not really too different from what we do in procedural coding.

So when we build a true OO system, we don't have any language support with it, so the program we write is a meta program that constructs the OO system procedurally. The system is not visible in the program text, it is created as a side effect of running the procedures and remains invisible, unless we develop tooling to make it visible.

And when that OO system runs, after you've built it procedurally, how is the communication between object mediate? Also procedures. Again, if there are other communication patterns, they can only be implemented using procedures in the language, they cannot be expressed in the language.

So with current programming languages (even OO ones), a good OO system will, by necessity, be highly indirect compared to the program text. A good OO system will also have sufficient benefits that this trade-off is very much worthwhile, but it is a significant trade-off. And when systems are not good the trade-off is not worth it. What's worse, people get confused and see the indirection not as the trade-off, but as the point of OO. I think those are the examples that people who are extremely jaded by OO have been exposed to: layers upon layers of indirection without a point. Indirection for indirection's sake.

And so they say that this is all BS and you should just not use OO. And they have a point, though they are not correct. Good OO developers handle this tradeoff by getting the benefits of OO with the minimum amount of indirection needed. Tooling like that found in modern Smalltalk systems can help you interact with the OO system that is not visible in the program text.

My approach to the tradeoff is to remove the indirection in the program text by making it possible to directly express components, connectors and systems in the program text, rather than having to build them all procedurally.

https://objective.st

> so the program we write is a meta program that constructs the OO system procedurally. The system is not visible in the program text, it is created as a side effect of running the procedures and remains invisible,

Great way of stating it! Most people who cargo-cult "OO is bad" don't get this.

> a good OO system will, by necessity, be highly indirect compared to the program text. A good OO system will also have sufficient benefits that this trade-off is very much worthwhile,

Very true. This is the reason OOD/OOP has been a great success that has led to the explosion of software that we take for granted today.

> My approach to the tradeoff is to remove the indirection in the program text by making it possible to directly express components, connectors and systems in the program text, rather than having to build them all procedurally.

At language source level (eg. DSL) or binary component level (needs runtime support a la COM) ?

> Most people who cargo-cult "OO is bad" don't get this [that OO programs are meta programs that build the system].

Alas, many people who advocate OO don't get this either, and in particular they don't see this as a problem to be solved.

Having to do things indirectly is not a good state of affairs. See "goto statement considered harmful". [1]

It is similar to the way we had to work with text editors made for printing terminals: "...requires a mental skill like that of blindfold chess; the user must keep a mental image of the text he is editing, which he cannot easily see, and calculate how each of his editing command `moves' changes it." [2]

> OOD/OOP has been a great success that has led to the explosion of software that we take for granted today.

Yes, people forget that the problems we are now having are the ones that are due to OO success.

> At language source level (eg. DSL) or binary component level (needs runtime support a la COM)

Language level. At the systems level we know how to build these types of system (COM, Smalltalk, Objective-C, Unix pipes and filter, REST, notification systems, ...), what we lack is the ability to express them in the program text: https://objective.st/

[1] https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.p...

[2] https://www.gnu.org/software/emacs/emacs-paper.html

So what are the benefits? (I'm assuming you mean benefits to Smalltalk, not Java.)
This video has a pretty good breakdown of how the "encapsulation" and "decoupling" promised by OOP is mostly a mirage:

https://youtu.be/QM1iUe6IofM?si=4GrOhZ31esd7rDSN&t=1089

> I'd love to hear an elaboration on that idea (OOP is essential for decoupling components) and its counterargument (decoupling can apparently be done just fine without OOP?).

When programmers faced the "Software Crisis" in the 1960s (https://en.wikipedia.org/wiki/Software_crisis) the idea of "Software as Components" was born as the solution (https://en.wikipedia.org/wiki/Component-based_software_engin...). This was a direct result of research on "Separation of Concerns/Information Hiding/Modularization/Structured Programming" design requirements. The idea was to make it similar to how "Components" were used in the Hardware Industry where you could substitute different components across different products/product families and all can be developed independently but used in a drop-in/plug-and-play manner to build up an entire System.

OOD/OOP turned out to be a natural architecture for Components since they provided the necessary support for the above-mentioned design requirements. There are two aspects to this architecture a) Source/Language level b) Binary/Usage level. The latter was the "holy grail" and people invented complete binary runtime architectures like COM/DCOM/CORBA/etc.(eg. https://en.wikipedia.org/wiki/Component_Object_Model) with language-neutral interfaces defined via a IDL (https://en.wikipedia.org/wiki/Interface_description_language). You could now have binary software components publish well defined interfaces which clients could call at runtime to discover and use services. The OO idea of encapsulation of data+procedures in a single "Object" keeps the model clean. But note that OO itself is merely a way of composing/structuring procedural code with a strong emphasis on a certain method of architecting the whole System.

Thus OOD/OOP helps greatly in designing systems where components can be kept well decoupled and extensible. The same can be done in a non-OO way (depending on your definition of OO) but is much harder. The design requirements mentioned above have to be satisfied whichever path you take. The role of a OO language merely facilitates the ease with which you can/cannot architect such a System.

I've found OOP can increase coupling. I actually have this problem at work. Say I have class A and B. I could write `int x = A:FunctionOfAandB(B b)`, but in this case. So now A depends on B.

It would be much preferable to have free function of A and B. A and B are no longer dependent on each other, just the function to compute the result dependent on A and B.

IMO: The real thing of value OOP provides is calling with object.method. A lot of OOP code looks like `void obj.mutate()`. If you pulled that to what's really happening in "procedural" syntax, `mutate(obj)`, that exposes it for the bad code it is.

>The real thing of value OOP provides is calling with object.method. A lot of OOP code looks like `void obj.mutate()`. If you pulled that to what's really happening in "procedural" syntax, `mutate(obj)`, that exposes it for the bad code it is.

I'm confused here, are you saying there is value in the object.method() syntax? D language for example has Uniform Function Call Syntax[0] where f(x) can be written as x.f()

(I missed this syntax in C, and tried to approximate it by putting functions in structs, but you still have to pass the caller as an arg so it ends up being player.update(player) which looks stupid, so at that point player_update(player) seems an acceptable alternative)

Later you say obj.mutate() is bad code, are you referring only to methods which mutate the object?

[0] https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...

I've also seen template hierarchies so deep it was a major effort trying to figure out which template did anything besides forward to another template.
Ugh yes. This has beeb my experience with Java code in large corporate projects. Just endless layers of seemingly pointless abstraction
Yep, that's because indirection is a necessary tradeoff for implementing OO systems in languages that don't fully support it, which surprisingly includes all current OO languages.

People then get confused and think that indirection is the point. It's not.

It would be better if the indirection weren't needed and we could express more than just procedural abstraction in our PLs.

> is essential to nearly any large, complex software system

> in performance sensitive code

Those two things are not the same.

The question you're responding too already makes it clear those things are quite different, perhaps even orthogonal.