Solid Principles In Java

By | | Updated : 2021-03-14 | Viewed : 267 times

Solid Principles In Java

The current tutorial explains the solid principles in java with various examples. The SOLID Principles are Single Responsibility Principle, Open/Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, Dependency Inversion. We will learn all these solid principles in java with examples.

S.O.L.I.D principles in Software design

S.O.L.I.D is the acronym of five Software design principles. Using SOLID principles in software design is the best practice that provides a better software design and structure. We will focus the all these five principles in java software design.

S.O.L.I.D principles are

  • 1. Single Responsibility Principle

  • 2. Open/Closed Principle

  • 3. Liskov Substitution Principle

  • 4. Interface Segregation Principle

  • 5. Dependency Inversion

Single Responsibility Principle Java

Single Responsibility Principle states the single responsibility of each superclass . It means each Class in Java should have only one reason to change. In other words, each Super Class should have only one job. So it is easy to modify super Class where subclasses of this superclass will not be affected. We will look and verify the same statement with an example given below.

Meal Interface for Single Responsibility Principle
public interface Meal {
	//ingredients for Meal
}
VegMeal class for Single Responsibility Principle
public class VegMeal implements Meal {
	public Meal getVegMeal(){
		//get Veg Meal
	}
}
NonVegMeal class for Single Responsibility Principle
public class NonVegMeal implements Meal {
	public Meal getNonVegMeal(){
		//get Non Veg Meal
	}
}
MealMaker class for Single Responsibility Principle
public class MealMaker {
	public Meal getVegMeal(){
		//get veg meal
	}	
	pulic Meal getNonVegMeal(){
		//get non-veg meal
	}
}

If you want to change MealMaker for packing you should have to introduce a new method called packMealMeal. But it breaks the Single Responsibility Principle in the class MealMaker. So we need to introduce a new class MealPacker for meal packing. MealMaker should handle the preparing Meal whereas MealPacker should concentrate on the packing.

MealPacker class for Single Responsibility Principle
public class MealPacker {
	public Meal packMeal(){
		//pack the meal
	}
}

Open/Closed Principle

It is one of S.O.L.I.D principles to apply in designing the software. The Open and Closed principle states about the Software entities (class, function, or module) should be opened for extension and closed for modification. It provides lots of benefits in the usage of software implementation.

Suppose you want to implement open API such as Java APIs, you need to provide only specifications. Different vendors can implement their own implementations. This is nothing but Open for extension and closed for modification. Considering the below example for better understanding the Open and Closed principle.

One Standard Organization is decided to give car specifications for the car vendors for manufacturing. Please notice below the Specification for the car vendors.

Car interface for Open/Closed Principle
private interface Car {
	public Engine designCarEngine ();
	public BasicFeatures provideBasicFeatures();
}
CarImpl class for Open/Closed Principle
public interface CarImpl implements Car {
	public Engine designCarEngine () {
		// disign the carEngine with required rules
	}
	public BasicFeatures provideBasicFeatures() {
		//provie basic features
	}
}
BenzCar Class for Open/Closed Principle
public class BenzCar extends carImpl {
	//Can be extended with new features
}

Notice the BenzCar class that can implement the extra feature without modifying the existing specifications. This principle is used in this way that it is possible extending the specifications but not possible for changing the specifications.

Liskov Substitution Principle

It is an extension of the Open/Closed Principle. This principle states that

" Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T. "

As a scientific definition, it isn't easy to understand. In general words, it is described as each superclass should be replaceable with its subclass object. In other words, every overridden method of the subclass should have the same type of input parameters as in the superclass. We will be discussing this concept to understand.

We will consider an example for the same MealPacker as defined above.

Meal class for Liskov Substitution Principle
public interface Meal {

	public void getNonVegCurryItems();
	public void getVegCurryItems();

}
VegMeal class for Liskov Substitution Principle
public class VegMeal implements Meal {

	public void getNonVegCurryItems(){
		//get NonVegCurryItems for vegMeal
		//It is voilates Liskov Substitution 
	}
	public void getVegCurryItems(){
		//get VegCurryItems for vegMeal
	}
}

So When you order the VegMeal, the Meal object is not replaceable with the VegMeal object as not possible to implement getNonVegCurryItems() method. So need one more segregation for this.

VegMeal class for Liskov Substitution Principle
public class VegMeal extends Meal {
	public void getVegCurryItems(){
		//get Veg Curry Items
    }
}
NonVegMeal class for Liskov Substitution Principle
public class NonVegMeal extends Meal {
	public void getNonVegCurryItems(){
		//get Non Veg Curry Items
	}
}

The above-given classes are exactly replaceable for a Meal with its subclasses objects when we order the veg/non-veg meals.

Interface Segregation Principle

This principle talks about the segregation of interfaces where small interfaces are separated from the large interface. If the interface contains more methods, it is required to implement all in its subclasses, even some of them are nonrelated to the subclass. Applying the Interface Segregation Principle, all the large interfaces will be segregated as the smallest ones and much easier to implement required methods.

We will see the example for better understanding So we can apply it in development.

AutoMechanic interface for Interface Segregation Principle
public interface AutoMechanic {
	public void repairBike();
	public void repairCar();
	Public void repairBus();	
}

But we will go for a bike or car or bus but not possible for all at a time. So if we segregate one interface with three interfaces then we can use the required interfaces for repair.

Segregated interfaces using with Interface Segregation Principle
public interface BikeMechanic {
	public void repairBike();
}	
public interface CarMechanic {
	public void repairCar();
}

public interface BusMechanic {
	ublic void repairBus();	
}

Dependency Inversion

The last principle of SOLID is Dependency Inversion addresses the importance of decoupling of modules/components. So The Dependency Inversion states that high-level modules should not depend upon the low-level modules, but both should rely upon the abstraction.

We will understand this loose coupling between software modules where it will be very very useful for implementation and maintenance.

MealPacker class for Dependency Inversion
public class MealPacker {
	private Meal meal;
	private Double price;
	public  MealPacker(Meal meal,Double price){
		//prepare the Meal for packing
	}
}

So here Meal and MealPacker classes are tightly coupled. It is not against the dependency inversion. Higher and lower modules should be coupled on abstractness.

To follow this principle we need to create one more interface on top of the Meal.

Meal interface for Dependency Inversion
public interface Meal {
	//
}

So we can pack Veg or Non-Veg Meal as a Meal is the abstraction of VegMeal and NonVegMeal classes.

VegMeal class for Dependency Inversion
public class VegMeal  implements Meal {
	//
}

In this way, you can pass VegMeal or NonVegMeal object into MealPacker class. So decoupled is happened between Meal and MealPacker.

Leave A Reply