SOLID

S: Single Responsibility Principle

  • There should never be more than one reason for a class to change.
  • In other words, every class should have only one responsibility.

O: Open Closed Principle

  • Open for extension and Closed for modification
  • a software entity (class, module, function) should be open for extension but closed for modification.
  • Example:
    • This can be achieved through abstraction, i.e., by creating interfaces
    • We can write implementation when it is needed

L: Liskov’s Substitution Principle

  • It states that subclasses should be substitutable for their base classes.
  • A client using a base class should continue to function properly even if a derived class is passed to it.
  • It is possible to have array of different child classes
class Animal {}  
class Cat extends Animal {}  
class Dog extends Animal {}  
class Squirrel extends Animal {}
 
// Array of different child classes
List<Animal> animals = List.of(new Cat(), new Dog(), new Squirrel());

I: Interface Segregation Principle

  • Many client-specific interfaces are better than one general-purpose interface.
  • An interface should be dependent more on the code that calls it rather than the code that implements it.
  • Suppose you are implementing an interface then you should use all the methods, otherwise create a new interface
  • Example: In the following example RestaurantEmployee has unnecessary methods for Waiter. Hence a new interface is needed!
interface RestaurantEmployee
{
	void washDishes();
	void serveCustomers();
	void cookFood();
}
 
class Waiter implements RestaurantEmployee
{
	public void washDishes() {
		// This is unnecessary to implement!
	}
 
	public void serveCustomers() {
		// serving implementation
	}
 
	public void cookFood() {
		// This is unnecessary to implement!
	}
}

This is correct way:

interface WaiterInterface
{
	void serveCustomers();
}
 
class Waiter implements WaiterInterface
{
	public void serveCustomers() {
		// serving implementation
	}
}

D: Dependency Inversion Principle

  • Classes should depend on interfaces of other classes instead of the concrete implementations.
  • Without Dependency Inversion, code is difficult to manage, change and test
  • Writing Unit tests are easy of dependency inversion principle is followed
  • Example:
    • Interface: Keyboard with following implementation:
      • WiredKeyboard
      • MouseKeyboard
class Macbook
{
	// private final WiredKeyboard keyboard; // wrong!
	private final Keyboard keyboard; // correct! using interface!
	
	public Macbook() {
		keyboard = new WiredKeyboard();
	}
}