“Tight Coupling is Bad” How many times you have heard this word from your seniors. Probably many many times.
But why Coupling is bad what are the implications comes if you do tight coupling?
What is actually a Tight coupling?
How we can fight with it.?
In this tutorials, we will dig the answers.
Coupling: In a simple term coupling is when a Class or Interface dependent on another class/interface i.e has a HAS-A relationship.
Example :
Class Vehicle{
private Wheel wheel =new Wheel();
}
In above example, Vehicle is dependent on Wheel, Which means without creating Wheel Object we can’t create Vehicle Object if anything goes wrong while creating Wheel Object vehicle will never be created. Also if we need to test Vehicle, first Wheel Object has to be created then Vehicle can be tested. Without, Wheel Vehicle has no existence. This type of coupling is called Tight Coupling, We know Vehicle must contain Wheel, To make the statement more generic sometimes we have requirements where a class must have to contains other classes to fulfill its purpose, they can't-do thing independently. So Coupling is inevitable, it can not be avoided but by programming technique we can make it pluggable in such a way so that we can reduce the degree of coupling so Dependable and Dependent class/interface can be changed without impacting each other. We called this technique as Loose coupling. I will show you some techniques which reduce the coupling between Objects.
Creation of Objects : Often while we doing coding we direct create the dependable Object instance, either in a init method or in Constructor or supply it through setter/constructor. But it is always risky. Once you have done that, Then you lose the flexibility if requirement changes in future you have to change the Dependent Object to accommodate the change in dependable Object. Let say All Ford cars use MRF Wheel So they are dependent on MRF wheel, Now if they change the mind and want to use Another company’s wheel then they have to change all car's Wheel Object creation so it is against the Open-Close principle.
Ex.
public Class Ford{
MRFWheel wheel;
Ford(MRFWheel wheel){
this.wheel =wheel;// replaced by new JKWheel()
}
}
So the best practices would be If you think dependent object will be changed frequently or may have multiple implementations always create an Interface and use that interface as a reference so anytime you can change the implementation without affecting the dependent class. But if you are sure about the dependent Objects behavior will not change then unnecessary don’t create interface it again against YAGNI and KISS.
IWheel{
//wheel related methods
}
public class Ford{
IWheel wheel;
Ford(IWheel wheel){
this.wheel =wheel;// replaced by new JKWheel()
}
}
Assuming MRFWheel and JKWheel are the subclasses of the IWheel
new Ford(new MRFWheel());
new Ford(new JKWheel())
Use Static factory method for creating Object : While creating an Interface for multiple implementations is good as you can change implementation dynamically. But if the dependable Object implementation is changed then again you have to change the all dependant Object which again breaks the Open/close principle.
Say Now Wheel take Air Pressure as a Constructor arguments then the caller of the Ford car has to change its logic, as multiple cars have dependencies on Wheel every implementation will break due to this change.
new Ford(new MRFWheel(AirPressure pressure));
new Ford(new JKWheel(AirPressure pressure))
The problem is we do not centralize the Object creation so all the caller has a responsibility to create the Objects and when the project grows it is a tedious job to find all references and fix them in case of a change in business logic in dependable Object. We certainly reduce our effort, if we use a Static Factory method to create the instances, So we centralize the creation of dependable object all dependent objects refer static factory method to get the dependable Object.So if any implementation details change it will only affect the Static factory method, Unless the method signature of the Static factory method is changed.
public static IWheel createWheel(WHEEL wheel){
if(WHEEL.mrf.equals(wheel)
new MRFWheel(AirPressure pressure)
}
else if(WHEEL.JK.equals(wheel)
new JKWheel(AirPressure pressure)
}else{
New DumyWheel(AirPressure pressure)
}
calling:
new Ford(WheelFactory.createWheel(WHEEL.mrf)));
new Ford(WheelFactory.createWheel(WHEEL.JK)));
Don’t take dependable Object Responsibility : Often knowingly or unknowingly, caller take the responsibility of dependable Object, which is breaking the encapsulation and it is the most common coding mistake I have seen, Not judging the Cohesion properly and it breaks another principle Tell Don’t Ask. Which increases unnecessary coupling and gradually your code not welcoming any future changes. Let's take a Simple example how we take dependable Object responsibility. Say Ford car has a method which shows the specifications of the car in very detail manner. So when it shows the Wheels Specifications often we do code like this in Ford Class.
public void FordSpecification(){
//Ford car specific specifications
//then
wheel.getAirPressure();
wheel.getManufactureDate();
wheel.getBrandName();
}
But it has a severe problem if in future Wheel specification is changed if it adds or removes any attribute it has a direct impact on Ford class, So all Ford Car classes have to be changed to incorporate the specification changes of the wheel.
It is wheel specification changes so why Ford class would be the sufferer?
Because while coding we did not understand the Wheel class responsibility, It is wheel class responsibility to provide specification through a specification method which uses by the Ford.
In Wheel class
public void specification(){
wheel.getAirPressure();
wheel.getManufactureDate();
wheel.getBrandname();
}
In Ford Class
public void FordSpecification(){
//Ford car specific specifications
//then
wheel.specification();
}
If wheel specification changes it does not impact the Ford class.
Try to reduce Hide-coupling :
Hide Coupling means from the API, you can’t understand there is and Dependency inside it.
It is a common programming technique where we hide the coupling from the user, Like create necessary Objects inside init method or constructor. I am not saying this is bad but , When you hide a Coupling think very carefully , If the Object is a kind of Utility, Connection pools, Worker thread that is fine but if it is a normal business Object, always provide an option for setting the Object from Outside, So user can set a Dummy or Mock Object while testing, Unless as a developer it is very hard to track down why the Object is not created as user does not aware of hiding coupling
Take the first example again
Public Class Ford{
MRFWheel wheel;
Ford(){
this.wheel =new MRFWheel();
}
}
From the API of Ford, it is impossible to say Ford has a dependency on MRFWheel. You will discover it in runtime if MRFWheel Object is not created from the stack trace. But if you change the implementation.
Public Class Ford{
IWheel wheel;
Ford(IWheel wheel){
this.wheel =wheel;// replaced by new JKWheel()
}
}
Then we can inject a DummyWheel while unit testing the Ford specific method.
Conclusion : Tight Coupling always creates a Big Ball of Mud. And gradually loses the flexibility to incorporate changes. Always take a closer look for coupling while writing code-- A silly mistake can cost you very much in near future. If you take above best practices most of the time you will be on safer side.