Solid Principles in java with examples

SOLID is one of the most popular sets of design principles when developing object-oriented software. It is a mnemonic acronym for the five design principles which follow:
  • Single Responsibility Principle
  • Open- Close Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle
1) Single Responsibility Principle (SRP)
The Single Responsibility Principle (SRP) states that a class should only have one responsibility. Additionally, it should have just one reason to change.

A lot of members can exist as long as they relate to the single responsibility. It could be that multiple class members may need modification when the one reason for change occurs. Multiple classes may need to be updated, too.

Example:
class Movie{
     public String author(){...}
     public void displayDetails(){...}
     public void updateMovie(){...}
}

Here we have logic of the database and logic of reporting all mixed up within one class. If you have multiple responsibilities that are combined into one class, it may be hard to change one part without breaking another. Mixing responsibilities also makes the class more difficult to understand, and more difficult to test, diminishing cohesion. The best way to fix this is to split the class into two different classes, each of which has one responsibility: access to the database and reporting, all separate.

2) Open- Close Principle (OCP)
The Open-Closed Principle (OCP) states classes should be open to extension, but closed to change. "Open to Extension" means you should design your classes to be able to add new functionality as new requirements are generated. "Closed for modification" means that you should change existing code and trigger possible new bugs.

Those two parts of the principle seem contradictory. However, you can add functionality without editing existing source code, if you structure your classes and their dependencies correctly.

Example:
In this example we implemented a class called movieTickets.

public class movieTickets {

    private String type;
    private String movieName;
    private double price;

    //Constructors, getters & setters
}

public class upcomingMovies extends movieTickets {

    private String upcomingMovieName;

    //constructor, getters + setters
}
3) Liskov Substitution Principle (LSP)
The Liskov Substitution Principle (LSP) applies to inheritance hierarchies, specifying that you should design your classes so that subclasses can be substituted for client dependencies without the client being aware of the change.

Therefore all subclasses have to operate in the same way as their base classes. The subclass's particular features may be different but must adhere to the base class's expected behaviour. The subclass must not only follow the methods and properties of the base class but also adhere to its implied behaviour, in order to be a true behavioral subtype.
Example:
public class Rectangle{
     private double height;
     private double width;

     public double area();
     public void setHeight(double height);
     public void setWidth(double width);
}

public class Square extends Rectangle{
     public void setHeight(double height){

        super.setHeight(height);
        super.setWidth(width);
    }

     public void setWidth(double width){
       setHeight(width);
   }
}
4) Interface Segregation Principle (ISP)
The Interface Segregation Principle (ISP) states that Consumers should not be forced to rely on interfaces they do not use. When we have non-cohesive interfaces, we are guided by the ISP to create multiple cohesive, smaller interfaces.

Using tightly focused interfaces, classes and their dependencies communicate when you apply ISP, minimizing dependencies on unused members and decreasing coupling accordingly. Smaller interfaces are easier to implement, enhancing the flexibility and reuse options. As fewer classes share these interfaces, the number of changes required in response to a modification of the interface is reduced, which increases robustness.

Example:
public interface Messenger{
     askForCard();
     tellInvalidCard();
     askForPin();
     tellInvalidPin();
     tellCardWasSiezed();
     askForAccount();
     tellNotEnoughInAccount();
     tellAmountDeposited();
     tellBalance();
}

Instead, split the Messenger interface up so that different ATM functionality depend on separate Messengers.

public interface LoginMessenger{
     askForCard();
     tellInvalidCard();
     askForPin();
     tellInvalidPin();
}

public interface WithdrawMessenger{
     tellNotEnoughInAccount();
     askForPaymentConfirmation();
}

public class EnglishMessenger implements LoginMessenger,WithdrawMessenger{...}
}
5) Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; abstractions should rely upon them. Furthermore, abstraction should not depend on details; abstraction should focus on facts.

The aim is to isolate our class behind a boundary formed by the abstraction on which it depends. If all the details behind those abstractions change, our class will remain safe. This helps to keep the coupling down and makes it easier to change our design. In addition, DIP helps us to test things in isolation, information such as database are plugins for our program.

Example:
public interface Reader {char getchar();}

public interface Writer { void putchar(char c)}

class CharCopier{
     void copy(Reader reader,Writer writer){
          int c;
          while ((c=reader.getchar())!=EOF){
        
          wrter.putchar();
     }
  }
}

public Keyboard 
implements Reader {...}

public Printer
 implements Writer {...}

Comments

Popular posts from this blog

Project

Closure Scope Chain

Microservices and Node.js