Tight-coupling and Loose-coupling Implementation in Java
Tags: java, spring
Published: 2023-01-31 17:07:11

If you are quite familiar with Java and moving towards Spring Framework or you want to get an idea of loosely-coupled and tightly-coupled system in a programming perspective or you want to add a bit more knowledge to your mental knowledge repository, then we are good to go. 

## What are tightly and loosely coupled systems?
Breifly, tightly-coupled means a system in which the components are dependent on each other. It reduces flexibility of a system and swapping a component for another is very difficult. Loosely-coupled systems are those where components are not dependent on each other and the components can be swapped with ease. So, how do we achieve loose-coupling in Java?

## How do we implement loose-coupling in Java?
We achieve this in Java by creating a superior interface. All other classes will implement this interface to define the methods. Its like android apps for the Android software and iOS apps for iOS. Now finally we need a runner class that will instantiate the games and the object of this runner class will be used to run the games from the main class. This might seem a bit overwhelming but stick till the end and you will get the picture.

## Tight-coupling Coding Implementation
In the below example, we consider creating a gaming device that will run 2 games: **Super Mario** and **Super Contra**. If we implement this in Java, the code will be:
```java
public class SuperMario {  
    public void up() { System.out.println("Super Mario: up"); }  
    public void down() { System.out.println("Super Mario: down"); }  
    public void left() {  System.out.println("Super Mario: left"); }  
    public void right() { System.out.println("Super Mario: right"); }  
}

public class SuperContra {  
    public void up() { System.out.println("Super Contra: up"); }  
    public void down() { System.out.println("Super Contra: down"); }  
    public void left() {  System.out.println("Super Contra: left"); }  
    public void right() { System.out.println("Super Contra: right"); }  
}

public class GameRunner {  
	private SuperMario game;
    public GameRunner(SuperMario game) {  
        this.game = game;  
    }  
    public void run() {  
        game.up();  
        game.down();  
        game.left();  
        game.right();  
    }  
}

public class GamingDevice {  
   public static void main(String[] args) { 
	  SuperMario game = new SuperMario(); 
      GameRunner runner = new GameRunner(game);  
      runner.run();  
   }  
}
```

Now the problem with the above implementation is that in the `GameRunner` class, the object is strictly set as a `SuperMario` object. So, currently it seems like our device can only run(**tightly-coupled**) the SuperMario game. What I mean is, if we try to run the below code in my `GamingDevice` class, we will get an error.

```java
public class GamingDevice {  
   public static void main(String[] args) { 
	  SuperContra game = new SuperContra(); 
      GameRunner runner = new GameRunner(game);  
      runner.run();  
   }  
}
```

But a device running one game is not ideal right? Our device is supposed to run many games. 

To summarize the scenario, our device can currently run only one game now i.e. **Super Mario**, but we want it to be able to run both **Super Mario** and **Super Contra**. So, how do we fix our device?

We fix it by installing a **Gaming Software**(interface) in our **Gaming Device** that can run both the games. So, rather than running the game directly on the device, our device will be able to run the gaming software which supports both the games.

So, our new implementation goes like:
## Loose-coupling Coding Implementation
```java
public interface GamingSoftware {  
    public void up();  
    public void down();  
    public void left();  
    public void right();  
}

public class SuperMario implements GamingSoftware {  
    public void up() { System.out.println("Super Mario: up"); }  
    public void down() { System.out.println("Super Mario: down"); }  
    public void left() {  System.out.println("Super Mario: left"); }  
    public void right() { System.out.println("Super Mario: right"); }  
}

public class SuperContra implements GamingSoftware {  
    public void up() { System.out.println("Super Contra: up"); }  
    public void down() { System.out.println("Super Contra: down"); }  
    public void left() {  System.out.println("Super Contra: left"); }  
    public void right() { System.out.println("Super Contra: right"); }  
}

public class GameRunner {  
	private GamingSoftware game;  
    public GameRunner(GamingSoftware game) {  
        this.game = game;  
    }  
    public void run() {  
        game.up();  
        game.down();  
        game.left();  
        game.right();  
    }  
}

public class GamingDevice {  
   public static void main(String[] args) { 
	  SuperMario superMario = new SuperMario(); 
	  SuperContra superContra = new SuperContra();
      GameRunner marioRunner = new GameRunner(superMario);  
      GameRunner contraRunner = new GameRunner(superContra);
      marioRunner.run();
      contraRunner.run();
   } 
}
```

Now, our both games will run perfectly. So, what we achieved here is a **loosely-coupled system** or gaming device which will be able to run any game that the Gaming Software supports(i.e. the game is implemented using the GamingSoftware interface). 

So, this is the difference between tightly-coupled and loosely-coupled systems in Java and the difference between their implementations. If you found this article helpful, leave a comment :)