5 ms·
> I have found that OOP with inheritance is actually a form of tight coupling and that it is best to not use class hierarchies. Tight coupling is the basis of
by ninetyninenine 2y ago
> I have found that OOP with inheritance is actually a form of tight coupling and that it is best to not use class hierarchies.
Tight coupling is the basis of oop. A method is tied to an instance. Methods cannot be composed with other methods or functions without instantiating state. Methods cannot be moved into other scope.
The literal definition of an object is a tightly bound set of methods that cannot ever be used without instantiating state.
- the_af 2y agoI don't think a method being tied to an instance is the best case for calling it "tight coupling". OOP can be used to design relatively uncoupled systems. One of the lessons learned in all these decades is what the grandparent post alludes to, which is distilled into "prefer composition over inheritance". Implementation inheritance (as opposed to interface inheritance) indeed introduces coupling and is therefore discouraged in current advice.
- gorjusborg 2y ago> OOP can be used to design relatively uncoupled systems. Never go full-Object-oriented programming. In this case I think it is valuable to make a distinction between OOP which is a style of programming, and object-oriented languages, which are just a language designed with that style in mind. I have seen issues in codebases where developers have used OOP as style to aspire to, using it in an academic sense. They tend to try to use inheritance frequently, have deep inheritance trees, and suffer from hidden coupling through these. On the other hand, those who use object oriented languages in a mostly functional style (side-effect free functions, effective immutability, and almost no use of inheritance) tend to be much healthier in the long term. So it's fine to use OO languages, but never go full OO programming.
- the_af 2y ago> Never go full-Object-oriented programming. Agreed, but that wasn't what I was saying or replying to, was it? I was arguing that method implementation tied to an instance isn't the type of thing people mean when they refer to tight coupling. Coupling is related to breakage/maintenance; when you touch this thing here, if it's tightly coupled with some other component, it will also require (sometimes unexpected) changes in that other component. Whether one should or shouldn't go full OO is an orthogonal consideration.
- gorjusborg 2y agoOh, no, I didn't intend to suggest anything contrary to what you said, I was just adding on.
- jnwatson 2y agoI think the poster child of going full OOP (that one can look at) is ACE/TAO [1], an implementation of CORBA. It had deep inheritance trees and abstractions piled on abstractions. Similar to Mach and microkernels, folks ran ACE/TAO and thought CORBA was slow, when it was just the implementation that was not built for speed. 1. https://github.com/DOCGroup/ACE_TAO https://github.com/DOCGroup/ACE_TAO
- ninetyninenine 2y agoYou don’t see the problem. It’s better to have all your logic be loosely coupled down to the smallest primitive. What’s the point of tying up groups of logic together and glue it up with state and say this is the fundamental unit of composition? You see the problem? 2 years down the line you find out that a certain class has methods that are better reused in another context but it’s so tightly coupled that the refactoring is insanity. Better to have had state decoupled from function and to have functions decoupled from each other and not tied together by common state. If you do this you get rid of all the fundamental technical debt that arises from oop. You guys don’t see it. Oop is a major cause of technical debt because of tight coupling. We can’t predict the future. You can’t guess that a method that exists in class A will 2 years down the line be better fit in class B or as its own class. So because you can’t know the future isn’t it logically better to not couple all your logic together into these arbitrary bundles called classes? Break your function down into more smaller modules of computation. The object class is too large. But then you ask how do I create bigger abstractions? Just compose functions together to form bigger functions. For state Compose struct types together to form bigger structs. Using this method to build your abstractions allows you to break down your abstractions into smaller units whenever you want! You can’t break down the class. The class is stuck. I can’t reuse a portion of state in another context and I can’t do the same thing with my methods. What’s the point of using classes to place arbitrary and pointless restrictions on modularity? None.
- deleted 2y ago[deleted]
- the_af 2y agoI agree that in many cases there is a problem, and indeed, objects can be designed as too coarse. I agree in many cases it leads to problems of composition. Some design principles have been devised to mitigate this, such as the "Single Responsibility Principle" (and others). Nothing is fool-proof however, and everything is further complicated by the fact no-one seems to agree on precise definitions of any principles. God Objects are one such know problem of highly coupled, low cohesion functions grouped into arbitrary objects. Objects naturally group related functions in some cases (when they truly conform to a coherent entity), so I guess I disagree they are always wrong. But when OOP became fashionable, designers started thinking everything must be an object, and this is obviously wrong -- but is it OOP's fault, or was it the fault of its adopters? The "everything is an object" mantra is indeed misguided when applied to every software system. Functions can fall prey to the same faulty thinking. I've seen many times functions "in the wild" that do too many things, tweakeable through too many parameters. They usually must be refactored. In fact, refactoring is where you split objects that have become too large or ill-defined for their own good, is it not? In the end, I think this is more about good software engineering practices rather than "one must use/must not use OOP/FP" or whatever ?Programming style.
- chucksmash 2y ago> Tight coupling is the basis of oop. This is not what OOP people are talking about when they talk about tight or loose coupling though. They are talking about the relationship between classes.
- ninetyninenine 2y agoAnd that’s the fundamental problem. They fail to see that if they took those same methods and made it independent of state then those things are now called functions. Functions can be moved to different scopes. Functions don’t rely on state to exist. You can compose functions with other functions to build new functions. And here’s the kicker. All of these functions did the same thing as the method. Functions are more modular. A method, is a restricted function that is tightly bounded with state and all other sibling methods.
- ajuc 2y agoThere's a trade-off. Functional programs are easier to read, because the structure makes the state transitions and dependencies obvious - you see your dependencies in the arguments list. But it forces you to basically rewrite big parts of your program after even very simple changes. You had the structure of the program so fine-tuned to the dependencies of every part of the code - that when any dependencies change you have to completely change that structure. It's rewrite-only programming style. Imperative (and OO) programming idiomatically let you do a bigger mess with side effects, and you know less about the data dependencies just from looking at the function specifications - but it also allows you to do exploratory programming much faster (no need to pass a new argument down 20 levels of your call stack when some code deep down suddenly requires a new argument). And it allows you to modify the behaviour locally without refactoring the whole thing constantly. If you have a for loop that filters out even numbers and suddenly you want to sum the numbers and find maximum and minimum too - most of the code stays the same. If you have functional code doing the same and want to modify it in similar manner - it's a completely different code. Most people would just rewrite it from scratch. And that's just a very small scale example. With larger programs the rewrite gets harder. That's why big programs are almost never functional.
- echelon 2y agoCoupling behaviors to types is not a problem. Class inheritance and multi-inheritance with their weird taxonomical trees are the problem. There's little practical reason to build a family tree of supertypes and subtypes outside of building GUIs, yet this is how class-based OOP is designed and taught. Trait-based OOP gets this right. The hierarchy is completely flat. You simply implement the behaviors for the types you want and don't have to think about grandparent behavior and final interfaces.
- whstl 2y agoIMO: The hierarchy part itself is not as problematic when there is no implementation inheritance involved. Hierarchical interface chains can be fine in my experience, but I’m open to changing my mind. The problem IMO arises when you mix both deep hierarchies and implementation inheritance.
- ninetyninenine 2y ago> Coupling behaviors to types is not a problem. Class inheritance and multi-inheritance with their weird taxonomical trees are the problem. I didn’t mention coupling behaviors to types. I said coupling behaviors to state is the problem. When I couple method a to state it’s a big problem. I want method banana but method banana lives in the jungle and has all the other methods related to jungle. So all I wanted was a banana now I have a jungle. Why not think of the banana as modular from the jungle? Why even use oop? I agree with you on the inheritance thing. Thats a different issue.
- nsonha 2y agoobviously when they refer to loose coupling, they're refer to that between interface and hidden states/implementation of object. It's very easy to deliberately miss the point and complain about the "tight coupling" that, by design, prevents messages to be sent to/called upon invalid states, which functional programming also has, albeit with different a approach using static type inference & pattern matching.
- the_af 2y agoThe tight coupling the OP refers to is between different components, in this case different classes in the same inheritance tree. If you use implementation inheritance, i.e. a base class with a tree inheritance of derived classes, there's coupling between the derived classes and the base class, because changes done carelessly in the base class (for example, to accommodate one additional child class) may impact the behavior of already existing derived classes. There are all sorts of principles to minimize this risk, but they are there precisely because it's such a big risk. Therefore "inheritance as reuse" introduces dangerous coupling, but it's not a case of the "interface being coupled to the implementation of the object".
- nsonha 2y agono one ever brought up "inheritance as reuse" in this thread, don't straw man. It's called OOP, NONE of those letters stands for "inheritance"
- the_af 2y ago> no one ever brought up "inheritance as reuse" in this thread, don't straw man. Yes they did. I quote one of the statements at the top of this thread: > But in real life, when there is a team, you run into the fragile base class [1] constantly and changing that base class causes horrible issues across your code base. The "fragile base class" is one of the problems of "inheritance as reuse". It simply doesn't occur with interface inheritance (the other common type of OOP inheritance). > It's called OOP, NONE of those letters stands for "inheritance" That line of conversation is fruitless. We're discussing OOP as practiced, not as defined by Alan Kay (who came to regret the name "OOP" anyway). OOP as practiced is all about inheritance, and sadly, its pitfalls. This is exactly what other people in this thread are discussing pros and cons of! Not OOP in academia, but in practice. Also, it's mentioned in the article which you hopefully have read: In this post I use the word “OOP” to mean programming in statically-typed language with: - Classes, that combine state and methods that can modify the state. - Inheritance, which allows classes to reuse state and methods of other classes. - Subtyping, where if a type B implements the public interface of type A, values of type B can be passed as A. - Virtual calls, where receiver class of a method call is not determined by the static type of the receiver but it’s runtime type.
- DeathArrow 2y ago> Tight coupling is the basis of oop. But then we invented inversion of control principle to remove that tight coupling. :)
- ninetyninenine 2y agoNo inversion of control is still coupling. You have modules A and B. If module A is defined in terms of B or B defined in terms of A then you have a coupling. The inversion is orthogonal to the problem right? The inversion of A defined in terms of B is rewriting B to be defined in terms of A. Better to have A and B not know about each other and be independent instead of dependent.