What is the difference between loose coupling and tight coupling in the object oriented paradigm?
Can any one describe the exact difference between loose coupling and tight coupling in Object oriented paradigm?
Tight coupling is when a group of classes are highly dependent on one another.
This scenario arises when a class assumes too many responsibilities, or when one concern is spread over many classes rather than having its own class.
Loose coupling is achieved by means of a design that promotes single-responsibility and separation of concerns.
A loosely-coupled class can be consumed and tested independently of other (concrete) classes.
Interfaces are a powerful tool to use for decoupling. Classes can communicate through interfaces rather than other concrete classes, and any class can be on the other end of that communication simply by implementing the interface.
Example of tight coupling:
class CustomerRepository
{
private readonly Database database;
public CustomerRepository(Database database)
{
this.database = database;
}
public void Add(string CustomerName)
{
database.AddRow("Customer", CustomerName);
}
}
class Database
{
public void AddRow(string Table, string Value)
{
}
}
Example of loose coupling:
class CustomerRepository
{
private readonly IDatabase database;
public CustomerRepository(IDatabase database)
{
this.database = database;
}
public void Add(string CustomerName)
{
database.AddRow("Customer", CustomerName);
}
}
interface IDatabase
{
void AddRow(string Table, string Value);
}
class Database implements IDatabase
{
public void AddRow(string Table, string Value)
{
}
}
Another example here.
Explanation without any code
It is best to start with simple analogies and reason from there, hopefully to understand the WHYs with REAL LIFE examples i've used in my own production code. I beg the patience of our more experienced software developers with the following explanation:
Summary Example of Loose Coupling:
In the picture above, the Hat is "loosely coupled" to the body. This means you can easily take the hat off without making any changes to the person/body. When you can do that then you have "loose coupling". See below for elaboration.
Tight coupling (Detailed Example)
Think of your skin. It's stuck to your body. It fits like a glove. But what if you wanted to change your skin colour from say white to black? Can you imagine just how painful it would be to peel off your skin, dye it, and then to paste it back on etc? Changing your skin is difficult because it is tightly coupled to your body. You just can't make changes easily. You would have to fundamentally redesign a human being in order to make this possible.
- Key Point #1: In other words, if you want to change the skin, you would also HAVE TO change the design of your body as well because the two are joined together - they are tightly coupled.
God was not a good object oriented programmer.
Loose coupling (Detailed Example)
Now think of getting dressed in the morning. You don't like blue? No problems: you can put a red shirt on instead. You can do this easily and effortlessly because the shirt is not really connected to your body the same way as your skin. The shirt doesn't know or care about what body it is going on. In other words, you can change your clothes, without really changing your body.
- That's key point #2. If you change your shirt, then you are not forced to change your body - when you can do that, then you have loose coupling. When you can't do that, then you have tight coupling.
That's the basic concept in a nutshell.
Why is all of this important?
When writing software, change is inevitable. If we know in advance that a change is going to come in a particular place, then we should ensure our software is loosely coupled on that specific point, because that would enable us to make those changes easily and quickly, without bugs.....so what does that mean? Consider some examples:
Loose Coupling in Software:
- CSV/JSON Examples: Early on in my career, my manager said: "give me the output as a CSV file". Great. I hacked away and created a routine that worked like a charm. Then one or two weeks later, he says: "actually, I want the output for another client in JSON".
"You want one output in CSV, and another in JSON?"
"That's right"
What a pain. I had to rewrite the entire thing. Who knows? Tomorrow he could turn around and say: "I want that in an XML format".
I should have known that he would have asked for a different format, and I could have planned for it.
I rewrote the entire thing using interfaces - a loosely coupled design pattern - and now adding new output formats, or changing things is now so much easier. I can edit the JSON portions without fear I will break my CSV output.
-
DB Examples: if you want to switch from MySQL to PostGreSQL easily - loosely coupled code makes it really easy to switch (i.e. to put on a red shirt instead of a blue shirt). The Rails ActiveRecord library is loosely coupled on its database implementation. This makes it super easy for someone to use their own database implementation, while using the same code base!
-
Cloud Provider examples: Or if you're using AWS and they start charging too much because of market dominance, you should be able to somewhat easily switch to Google or Azure etc. This is precisely the reason why libraries like Active Storage exist - they provide users with a healthy indifference as to the specific cloud provider being used (Azure, AWS S3, GCS etc.). You can easily change cloud providers with just a one-line code change. The implementation details of the cloud storage providers are loosely coupled.
-
Testing: if you want to test your software, with predetermined outputs and inputs - how are you going to do it? With loosely coupled software - it's a breeze: you can run your tests, and you can also deploy your production code and do it all in the same code base. With tightly coupled code, testing your production code is nearly impossible.
Do we need to make everything "loosely coupled"? Probably not. We have to exercise our judgement. When writing code, some amount of coupling is inevitable. But you should consider minimizing it if you can, especially if you know in advance where it will change.
Summary
In short, loose coupling makes code easier to change.
The answers above provide some code which is worth reading.
Advanced Topics
Loose coupling goes hand-in-hand with polymorphism and interfaces. If you want to continue reading on some more advanced topics, here are some other stack overflow answers which might help:
- What is Polymorphism?
- What is an interface?
- What do you mean by 'leaky abstractions' - not written by me.
(If my answers are confusing - please post a comment so I can fix. @lupchiazoem has written a very brilliant reply below, which is worth reading as well.)
Picture Attribution.