logoalt Hacker News

sirwhinesalotyesterday at 1:36 PM1 replyview on HN

To be clear I'm not arguing against objects here, I don't disagree with anything you wrote. I'm only arguing against the somewhat commonly held idea that OOP helps with loose coupling.

Interfaces (a feature of OOP) can be used to decrease coupling, but the paradigm itself doesn't really provide any assistance in regards to coupling.

The default is for objects to directly instantiate other concrete objects and to be able to directly communicate with any object they at any point come in contact with. That is strong coupling, by default.

You have to do extra work to get looser coupling. Smalltalk style OO is much better (regarding coupling) because you don't have explicit interfaces, you can always replace an object with another. You can also query all live objects of a certain type and replace all of them with a proxy if you so wish. Way better.

But you can go even further in regards to loosening coupling. You can have objects communicate through a tuplespace (Linda), you can have objects receive and send messages only through ports, you can have objects send a message to their implicit parent who is then responsible for redirecting it.

Mainstream OOP is a very poor paradigm full of issues that gets mogged by everything. Composition-over-inheritance is an admission of defeat for a paradigm that lacks native delegation support (good on Kotlin for realising this).

It even gets mogged by languages like Haskell that introduced the more loosely coupled idea of typeclasses, which were inherited by Rust (traits), Swift (protocols), Go (interfaces, not to be confused with declaration-time interfaces as in Java).

We dug a suboptimal hole and stuck our head in there for 50 years.


Replies

socketclusteryesterday at 2:10 PM

I think OOP helps to build loosely coupled systems but it doesn't protect you from tight coupling. You have to know what you're doing.

I think OOP languages made some pragmatic decisions. Sometimes a feature which is harmful 95% of the time could be genuinely useful and safe 5% of the time... Some languages like Haskell might choose to not allow that feature at all and force the developer to find another approach which is almost as effective for that 5% of cases; that's fair enough. It's a different philosophy.

I feel like that about passing mutable objects by reference. I find it harmful most of the time but there are rare cases were it's convenient and beneficial. I've worked on open source projects were I wanted the user to be able to use the software with any database so my function accepted a database adapter as an argument.

I could have achieved a similar goal in another way but I would have had to sacrifice separation of concerns slightly. I wanted the ability to substitute any database but also wanted the component to be responsible for the persistence and recovery of its own state as this was within its responsibilities. Also this was the only violation in the entire codebase so I deemed it acceptable. It didn't pose any problems at all in practice.

show 1 reply