Photo by Miltiadis Fragkidis on Unsplash
From Tight Coupling to Loose Coupling: The Spring Boot Advantage - PART 1
Table of contents
Introduction
Before diving into any framework, it's essential to understand why we need one in the first place. You might wonder, if I can accomplish tasks using just the Java language and its features, why invest time in learning an entire framework? Here, we'll explain the necessity of using the Spring Boot framework to streamline the development process and write efficient code with minimal configuration for your Java Enterprise applications. Let's try to understand this using an example :
Example 1 : Tight Coupling
class PetrolEngine {
public void start() {
System.out.println("Petrol engine started!");
}
}
class Car {
//class Car needs instance of class PetrolEngine
private PetrolEngine petrolEngine ;
public Car() {
// Create an object of PetrolEngine using new keyword
petrolEngine = new PetrolEngine ();
}
public void start() {
petrolEngine.start();
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.start();
}
}
In the above example, the class "Car" is Tightly Coupled with the class "Engine" because it directly creates an instance of "Engine." If the "Engine" class needs to be modified or replaced, the "Car" class must also be changed. Tight coupling refers to the high dependency of classes and components on each other. Direct references and strong dependencies can make classes less reusable and difficult to test.
Example 2 : Loose Coupling
What if at a later stage, the class "Car" wants to use a diesel engine instead of petrol engine ? Since the class "Car" is tightly coupled to Petrol Engine, changing engine will need change in Car class too where initially Petrol Engine was directly instantiated. This problem can be solved using Java Interfaces.
Loose Coupling can be achieved by using Java Interfaces and implementing them into concrete classes. Using this approach, the Car class will depend on the interface rather than specific implementation of Engine such as Petrol Engine in above example.
- Define the interface
interface Engine {
void start();
}
- Implement the interface
class PetrolEngine implements Engine {
@Override
public void start() {
System.out.println("Petrol engine started.");
}
}
class DieselEngine implements Engine {
@Override
public void start() {
System.out.println("Diesel engine started.");
}
}
- Inject concrete implementation via constructor
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
- Main class
public class Main {
public static void main(String[] args) {
Engine petrolEngine = new PetrolEngine();
Car carWithPetrolEngine = new Car(petrolEngine);
carWithPetrolEngine.start();
Engine dieselEngine = new DieselEngine();
Car carWithDieselEngine = new Car(dieselEngine);
carWithDieselEngine.start();
}
}
Perfect! seems like the problem of solving dependencies for which the Spring framework is renowned has been solved using just Java Interface here right ? But if you notice we're still manually instantiating and wiring dependencies using new keyword in Main class.
While interfaces allow different implementations to be used without modifying dependent class, Spring Beans take a step further by managing the lifecycle and object dependencies automatically. In the next article we'll see how Spring Beans can be used to achieve Loose Coupling.