OOP's SOLID Principles


Single Responsibility Principle

It means one single class should only have a single purpose. And when we need to change one class, the reason we make the change should be consistent with the purpose of this class. The following example violates the SRP:

public class Invoice {

    private Book book;
    private int quantity;
    private double discountRate;
    private double taxRate;
    private double total;

    public Invoice(Book book, int quantity, double discountRate, double taxRate) {
        this.book = book;
        this.quantity = quantity;
        this.discountRate = discountRate;
        this.taxRate = taxRate;
        this.total = this.calculateTotal();
    }

    public double calculateTotal() {
            double price = ((book.price - book.price * discountRate) * this.quantity);

        double priceWithTaxes = price * (1 + taxRate);

        return priceWithTaxes;
    }

    public void printInvoice() {
            System.out.println(quantity + "x " + book.name + " " +          book.price + "$");
            System.out.println("Discount Rate: " + discountRate);
            System.out.println("Tax Rate: " + taxRate);
            System.out.println("Total: " + total);
    }

        public void saveToFile(String filename) {
    // Creates a file with given name and writes the invoice
    }

}

In here, printInvoice method involves logic about printing and saveToFile method contains persistent logic. By doing so, we mix the logic of printing and persistent with the idea of creating a class model for book. To fix that, we can create helper class like:

public class InvoicePrinter {
    private Invoice invoice;

    public InvoicePrinter(Invoice invoice) {
        this.invoice = invoice;
    }

    public void print() {
        System.out.println(invoice.quantity + "x " + invoice.book.name + " " + invoice.book.price + " $");
        System.out.println("Discount Rate: " + invoice.discountRate);
        System.out.println("Tax Rate: " + invoice.taxRate);
        System.out.println("Total: " + invoice.total + " $");
    }
}

and

public class InvoicePersistence {
    Invoice invoice;

    public InvoicePersistence(Invoice invoice) {
        this.invoice = invoice;
    }

    public void saveToFile(String filename) {
        // Creates a file with given name and writes the invoice
    }
}

Open-closed Principle

It means a class is open for extension but closed for modification. If we are going to add some new functionalities into a class, we should avoid modifying its original code, instead, we can something like put new interfaces to make it have some new functionalities.

Example:

we have a javaCourse class:

public interface ICourse {    
    Integer getId();    
    String getName();   
    Double getPrice();}

public class JavaCourse implements ICourse {
    private Integer id;
    private String name;
    private Double price;

    public JavaCourse(Integer id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    @Override
    public Integer getId() {
        return this.id;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Double getPrice() {
        return this.price;
    }
}

now our course is on discount, the price of the course should be 80%, according to the open-close principle, the JavaCourse itself is closed for modification, but we can use other attributes in OOP to extension it, for example use a sub-class the JavaDiscountCourse class:

public class JavaDiscountCourse extends JavaCourse {
   
    public JavaDiscountCourse(Integer id, String name, Double price) {
        super(id, name, price);
    }

    public Double getOriginPrice() {
        return super.getPrice();
    }

    public Double getPrice() {
        return super.getPrice() * 0.8;
    }
}

Liskov Substitution Principle

Any places that require a base class, can but be satisfied with the subclasses of that base class

Interface Segregation Principle

Interfaces should be in low granularity, not too fat to cause a lot of zombie methods in its implementation classes

Dependency Inversion Principle

Details should upon abstractions

Both high level modules and low level modules should upon abstractions

It means your program should reply on interfaces and abstractions but not implementation class

https://zhuanlan.zhihu.com/p/24175489

https://juejin.cn/post/7024415644271509535


A u t h o r: Joe
P o l i c y: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Joe !
Leave Your Comment Here
  TOC