Tìm hiểu nhanh SOLID thần thánh
solid
2
oop
19
Java
179
software design
5
White

Nguyễn Tuấn Anh viết ngày 27/11/2020

SOLID là năm nguyên lý cơ bản trong thiết kế phần mềm hướng đối tượng, giúp code trở nên dễ hiểu, mềm dẻo 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:

  • S: Single responsibility principle
  • O: Open/closed principle
  • L: Liskov substitution principle
  • I: Interface segregation principle
  • D: Dependency inversion principle

1. Single responsibility principle

A class should have only a single responsibility.

Ý tưởng của nguyên lý này là giúp chúng ta giảm đi sự phức tạp của class: một class chỉ nên phục vụ một mục đích duy nhất.

Ví dụ:

Thay vì viết một class ôm đồm tất cả các xử lý liên quan đến CSDL như này:

public class DBHelper {

    public Connection openConnection() {};

    public void saveUser(User user) {};

    public List<Product> getProducts() {};

    public void closeConnection() {};
}

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

public class DBConnection {

    public Connection openConnection() {};

    public void closeConnection() {};

}

public class UserRepository {

    public void saveUser(User user) {};
}

public class ProductRepository {

    public List<Product> getProducts() {};
}

2. Open/closed principle

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

Ý tưởng của nguyên lý này là khi triển khai các tính năng mới, thay vì sửa đổi code đã tồn tại, chúng ta nên mở rộng/kế thừa.

Ví dụ: Trước đây, logic xử lý tính phí vận chuyển của một đơn hàng được đặt luôn bên trong class Order.

public class Order {

    public long calculateShipping(ShippingMethod shippingMethod) {
        if (shippingMethod == GROUND) {
            // Calculate for ground shipping
        } else if (shippingMethod == AIR) {
            // Calculate for air shipping
        } else {
            // Default
        }
    }
}

Giả sử hệ thống cần bổ sung thêm một phương thức vận chuyển mới, chúng ta lại phải bổ sung một case nữa trong method calculateShipping. Điều này sẽ làm code trở nên rất khó quản lý.

Thay vào đó, chúng ta nên tách rời logic xử lý tính phí vận chuyển vào một interface Shipping chẳng hạn. Interface Shipping sẽ có nhiều implementation ứng với từng hình thức vận chuyển: GroundShipping, AirShipping, ...

public interface Shipping {

    long calculate();
}

public class GroundShipping implements Shipping {

    @Override
    public long calculate() {
        // Calculate for ground shipping
    }
}

public class AirShipping implements Shipping {

    @Override
    public long calculate() {
        // Calculate for air shipping
    }
}

public class Order {

    private Shipping shipping;

    public long calculateShipping(ShippingMethod shippingMethod) {
        // Find relevant Shipping implementation then call calculate() method
    }
}

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ụ:

Chúng ta có interface Animal và 2 implementation BirdDog như sau:

public interface Animal {

    void fly();
}

public class Bird implements Animal {

    @Override
    public void fly() {
        // Flying...
    }
}

public class Dog implements Animal {

    @Override
    public void fly() {
        // Dog can't fly
        throw new UnsupportedOperationException();
    }
}

---> Rõ ràng class Dog đã vi phạm nguyên lý Liskov substitution.

Cách giải quyết ở đây sẽ là: tạo một interface FlyableAnimal như sau:

public interface Animal {
}

public interface FlyableAnimal {

    void fly();
}

public class Bird implements FlyableAnimal {

    @Override
    public void fly() {
        // Flying...
    }
}

public class Dog implements Animal {
}

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:

public interface Animal {

    void eat();

    void run();

    void fly();
}

Chúng ta có 2 class DogSnake 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:

public interface Animal {

    void eat();
}

public interface RunnableAnimal extends Animal {

    void run();
}

public interface FlyableAnimal extends Animal {

    void fly();
}

5. Dependency inversion principle

Depend on abstractions, not on concretions.

Ý tưởng của nguyên lý này là 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.

Ví dụ, chúng ta có 2 module cấp thấp BackendDeveloperFrontendDeveloper và 1 module cấp cao Project sử dụng 2 module trên:

public class BackendDeveloper {

    private void codeJava() {};
}

public class FrontendDeveloper {

    private void codeJS() {};
}

public 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 (BackendDeveloperFrontendDeveloper) mà còn phải sửa code ở cả module cấp cao (Project) đ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:

public interface Developer {

    void develop();
}

public class BackendDeveloper implements Developer {

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

    private void codeJava() {};

    private void codeCSharp() {};
}

public class FrontendDeveloper implements Developer {

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

    private void codeJS() {};

    private void codeAngular() {};
}

public class Project {

    private List<Developer> developers;

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

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

Tài liệu tham khảo

https://en.wikipedia.org/wiki/SOLID

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Nguyễn Tuấn Anh

30 bài viết.
268 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
60 42
MyContact là một ứng dụng mà mình thường viết mỗi khi học một ngôn ngữ hay công nghệ mới. MyContact chỉ là một ứng dụng CRUD đơn giản, cho phép ngư...
Nguyễn Tuấn Anh viết hơn 4 năm trước
60 42
White
43 16
Hướng dẫn lập trình Spring Security Trong bài viết lần này, mình sẽ giúp các bạn bước đầu tìm hiểu (Link) thông qua xây dựng các chức năng: Đăng ...
Nguyễn Tuấn Anh viết hơn 4 năm trước
43 16
White
23 0
Trước đây khi mới học Spring, mình thường nhảy thẳng lên tìm hiểu các project như Spring MVC hay Spring Boot để viết ứng dụng, thỉnh thoảng mới ngó...
Nguyễn Tuấn Anh viết hơn 2 năm trước
23 0
Bài viết liên quan
White
88 8
SOLID là gì – Áp dụng các nguyên lý SOLID để trở thành lập trình viên code “cứng” Trong quá trình học, hầu như các bạn sinh viên đều được học một s...
Huy Hoàng Phạm viết hơn 5 năm trước
88 8
White
2 0
Trong bài viết này, một số hình ảnh hoặc nọi dung có thể bị thiếu do quá trình chế bản. Vui lòng xem nội dung ở blog gốc sau: (Link) (Link), chúng...
programmerit viết hơn 5 năm trước
2 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
30 bài viết.
268 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!