SOLID principles
- Single Responsibility - a class should only have one responsibility. Furthermore, it should only have one reason to change.
- Open/Closed - classes should be open for extension but closed for modification.
- Liskov Substitution - Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it
- Interface Segregation - Many client-specific interfaces are better than one general-purpose interface
- Dependency Inversion - depend on abstractions (interfaces and abstract classes) instead of concrete implementations (classes)
Link 1
Link 2
Single Responsibility
Benefits:
- Testing - A class with one responsibility will have far fewer test cases.
- Lower coupling - Less functionality in a single class will have fewer dependencies.
- Organization - Smaller, well-organized classes are easier to search than monolithic ones.
public class Book {
private String name;
private String author;
private String text;
//constructor, getters and setters
// methods that directly relate to the book properties
public String replaceWordInText(String word){
return text.replaceAll(word, text);
}
public boolean isWordInText(String word){
return text.contains(word);
}
void printTextToConsole(){
// our code for formatting and printing the text
}
}
public class Book {
private String name;
private String author;
private String text;
//constructor, getters and setters
// methods that directly relate to the book properties
public String replaceWordInText(String word){
return text.replaceAll(word, text);
}
public boolean isWordInText(String word){
return text.contains(word);
}
}
public class BookPrinter {
// methods for outputting text
void printTextToConsole(String text){
//our code for formatting and printing the text
}
void printTextToAnotherMedium(String text){
// code for writing to any other location..
}
}
Open for Extension, Closed for Modification
public class VehicleCalculations {
public double calculateValue(Vehicle v) {
if (v instanceof Car) {
return v.getValue() * 0.8;
if (v instanceof Bike) {
return v.getValue() * 0.5;
}
}
public class Vehicle {
public double calculateValue() {...}
}
public class Car extends Vehicle {
public double calculateValue() {
return this.getValue() * 0.8;
}
public class Truck extends Vehicle{
public double calculateValue() {
return this.getValue() * 0.9;
}
Liskov Substitution
Objects of a superclass should be replaceable with objects of its subclasses without breaking the system.
Simply put, if class A is a subtype of class B, we should be able to replace B with A without disrupting the behavior of our program.
Interface segregation principle
The Interface Segregation Principle (ISP) states that clients should not be forced to depend upon interface members they do not use. In other words, do not force any client to implement an interface that is irrelevant to them.
public interface Vehicle {
public void drive();
public void stop();
public void refuel();
public void openDoors();
}
As you can see, it does not make sense for a Bike class to implement the openDoors() method as a bike does not have any doors! To fix this, ISP proposes that the interfaces be broken down into multiple, small cohesive interfaces so that no class is forced to implement any interface, and therefore methods, that it does not need.
Dependency inversion principle
The Dependency Inversion Principle (DIP) states that we should depend on abstractions (interfaces and abstract classes) instead of concrete implementations (classes). The abstractions should not depend on details; instead, the details should depend on abstractions.
public class Car {
private Engine engine;
public Car(Engine e) {
engine = e;
}
public void start() {
engine.start();
}
}
public class Engine {
public void start() {...}
}
The code will work, for now, but what if we wanted to add another engine type, let’s say a diesel engine? This will require refactoring the Car class.
public interface Engine {
public void start();
}
public class Car {
private Engine engine;
public Car(Engine e) {
engine = e;
}
public void start() {
engine.start();
}
}
public class PetrolEngine implements Engine {
public void start() {...}
}
public class DieselEngine implements Engine {
public void start() {...}
}