SOLID thần thánh chắc hẳn chúng ta đôi khi đã nghe đến từ này khi tiếp xúc với lập trình. Vậy SOLID là gì chúng ta cùng tìm hiểu về nó nhé.

Solid là gì?

SOLID là năm nguyên lý cơ bản trong thiết kế phần mềm hướng đối tượng (OOP , giúp code trở nên dễ hiểu, linh động và dễ bảo trì hơn. Tác giả của SOLID là kỹ sư phần mềm nổi tiếng Robert C. Martin. Năm nguyên lý trong SOLID bao gồm:

  1. S: Single responsibility principle
  2. O: Open/closed principle
  3. L: Liskov substitution principle
  4. I: Interface segregation principle
  5. D: Dependency inversion principle

1. Single responsibility principle

A class should have only a single responsibility.

Nguyên lý này có thể hiểu là một class chỉ nên đảm nhận một và chỉ một trách nhiệm duy nhất. Hay nói theo một cách khác, chúng ta chỉ nên viết và sửa đổi một class bởi vì một lý do duy nhất mà thôi. Nguyên lý giúp code dễ quản lý và bảo trì hơn.

Ví dụ:

Thay vì viết một class ôm đồm tất cả các công việc liên quan đến thao tác với cơ sở dữ liệu như này:

class DBHelper {

    public Connection openConnection() {};

    public void saveUser(User user) {};

    public List<Product> listProducts() {};

    public void closeConnection() {};

}

Chúng ta nên tách thành các class con, mỗi class xử lý một công việc riêng kiểu như này:

class DBConnection {

    public Connection openConnection() {};

    public void closeConnection() {};

}

class UserHelper {

    public void saveUser(User user) {};

}

class ProductHelper {

    public List<Product> listProducts() {};

}

2. Open/closed principle__

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

Nguyên lý này có thể hiểu là nếu như có thêm yêu cầu phát sinh, chúng ta nên mở rộng/kế thừa các class đã tồn tại thay vì sửa đổi chúng.

Ví dụ: Lớp Staff được thiết kế chỉ để đóng gói các thông tin cơ bản của nhân viên. Nếu muốn bổ sung thêm nghiệp vụ quản lý cho nhân viên, chúng ta nên tách riêng một class Manager kế thừa class Staff, bởi vì không phải nhân viên nào cũng có nghiệp vụ quản lý.

3. Liskov substitution principle

Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

Nguyên lý này có thể hiểu là các đối tượng của class cha có thể được thay thế bởi các đối tượng của các class con mà không làm thay đổi tính đúng đắn của chương trình.

Ví dụ: Các đối tượng Square đều có thể xem là các đối tượng Rectangle.

4. Interface segregation principle

Many client-specific interfaces are better than one general-purpose interface.

Nguyên lý này có thể hiểu là thay vì viết một interface cho một mục đích chung chung, chúng ta nên tách thành nhiều interface nhỏ cho các mục đích riêng. Chúng ta không nên bắt buộc client phải implement các method mà client không cần đến.

Ví dụ:

Chúng ta có một interface Animal như sau:

interface Animal {

    void eat();

    void run();

    void fly();

}

Chúng ta có 2 class Dog và Snake implement interface Animal. Nhưng thật vô lý, Dog thì làm sao có thể fly(), cũng như Snake không thể nào run() được? Thay vào đó, chúng ta nên tách thành 3 interface như thế này:

interface Animal {

    void eat();

}

interface RunnableAnimal extends Animal {

    void run();

}

interface FlyableAnimal extends Animal {

    void fly();

}

5. Dependency inversion principle

Depend on abstractions, not on concretions.

Nguyên lý này gồm có 2 ý nhỏ:

Các module cấp cao không nên phụ thuộc vào các module cấp thấp. Cả hai nên phụ thuộc vào abstraction. Abstraction không nên phụ thuộc vào detail. Detail nên phụ thuộc vào abstraction.

Ví dụ, chúng ta có 2 module cấp thấp BackendDeveloper và FrontendDeveloper và 1 module cấp cao

Project sử dụng 2 module trên:

class BackendDeveloper {

    private void codeJava() {};

}

class FrontendDeveloper {

    private void codeJS() {};

}

class Project {

    private BackendDeveloper backendDeveloper = new BackendDeveloper();
    private FrontendDeveloper frontendDeveloper = new FrontendDeveloper();

    public void build() {
        backendDeveloper.codeJava();
        frontendDeveloper.codeJS();
    }

}

Giả sử nếu sau này, dự án thay đổi công nghệ. Các backend developer không code Java nữa mà chuyển sang code C#. Các frontend developer không code JS thuần nữa mà nâng lên các JS framework. Rõ ràng chúng ta không những phải sửa code ở các module cấp thấp mà còn phải sửa code ở cả module cấp cao đang sử dụng các module cấp thấp đó. Điều này cho thấy module cấp cao đang phải phụ thuộc vào các module cấp thấp.

Lúc này, chúng ta sẽ bổ sung thêm một abstraction Developer để các module trên phụ thuộc vào:

interface Developer {

    void develop();

}

class BackendDeveloper implements Developer {

    @Override
    public void develop() {
        codeJava();
    }

    private void codeJava() {};

}

class FrontendDeveloper implements Developer {

    @Override
    public void develop() {
        codeJS();
    }

    private void codeJS() {};

}

class Project {

    private List<Developer> developers;

    public Project(List<Developer> developers) {
        this.developers = developers;
    }

    public void build() {
        developers.forEach(developer -> developer.develop());
    }

}

Viết câu trả lời

Drop Images

0 Bình luận