SOLID

Nguyên tắc thiết kế SOLID

Lập trình hướng đối tượng (object oriented programming – OOP) là một trong những mô hình lập trình được sử dụng nhiều nhất. Các tính chất đặc biệt khiến việc hướng đối tượng trở nên hiệu quả đó là:

  • Tính trừu tượng (abstraction): Tạo ra các lớp trừu tượng mô hình hoá các đối tượng trong thế giới thực.

  • Tính đóng gói (Encapsulation): Các thực thể của lớp trừu tượng có các giá trị thuộc tính riêng biệt.

  • Tính kế thừa (Inheritance): Các đối tượng có thể dễ dàng kế thừa và mở rộng lẫn nhau.

  • Tính đa hình (Polymorphism): Có thể thực hiện một hành động đơn theo nhiều cách thức khác nhau tuỳ theo loại đối tượng cụ thể đang được gọi.

1. SOLID là gì ?

SOLID là một quy tắc thiết kế phần mềm, được đúc kết từ xương máu, hàm trăm dự án thất bại lớn nhỏ các lập trình viện.

Các câu hỏi liên quan SOLID thường sẽ có trong phỏng vấn.

SOLID stands for:

2. Giải thích các thành phần

2.1 S - Single-Responsibility

Dao đa năng

Mỗi lớp chỉ nên chịu trách nhiệm về một nhiệm vụ cụ thể nào đó mà

Ví dụ thực tế: Dao đa năng là con dao được thiết kế với nhiều chức năng vượt qua những chức năng thông thường của con dao. Nó có thể cắt móng tay, mở két bia, .... Tuy nhiên, trong thiết kế dao đa năng đã vi phạm nguyên tắc số S, mỗi lớp nên chịu trách nhiệm cụ thể nào đó. Vì sao: + Khó sửa chữa khi gặp sự cố, khi có sự cố phát sinh, một con dao phức tạp với nhiều chi tiết sẽ khó sửa chữa phục hồi. + Khó nâng cấp thêm chức năng mới.

Ví dụ trong lập trình: Employee, Developer, Tester.

2.2 O - Open-Closed

Không được sửa đổi một Class có sẵn, nhưng có thể mở rộng bằng kế thừa.
Open/Closed princinples

Mã giải :

class Calculator{

    public float add(float a, float  b){}
    public float  sub(float a, float b){}
    public float  mul(float a, float b){}
    public float div(float a, float b){}
    
}

class ScientfiticCalculator : Calculator {

    public float sin(float degree){}
    public float cos(float degree){}
    public float tan(float degree){}
  
}

class BusinessCalculator : Calculator {

    public float futureValue(){}
    public float monthlyPayment(){}
    
}


Giả sử ta có lớp Calculator là máy tính, Khi chúng ta muốn nâng cấp thành máy tính Science thì chúng ta không nên sửa cái lớp Calculator cũ mà thay vào đó phải tạo ra một class ScientificCalculator mới kế thừa chức năng của class cũ.

Nguyên nhân:

  • Class Calculator là một class đã hoàn thiện, đã được test, duyệt rồi, nên khi mở rộng sẽ không phải duyệt lại.

  • Khi muốn mở rộng thêm một loại máy tính mới chằng hạn máy tính BusinessCalculator ta có thể kế thừa từ lớp Calculator, trong trường hợp ta sửa lớp Calculator thì sẽ không thể kế thừa được nữa vì sẽ vi phạm nguyên tắc thứ 3 sau đây.

2.3 L - Liskov substitution

Các đối tượng (instance) kiểu class con có thể thay thế các đối tượng kiểu class cha mà không gây ra lỗi.

Có thể hiểu đơn giản là: lớp con muốn kế thừa lớp cha thì phải hợp lý về mặt ý nghĩa.

Cách giải quyết: tạo ra một interface , hoặc class mới có chứa hàm checkAttendance()

2.4 I - Interface segregation principle

Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể.

Interface lớn sẽ có nhiều hàm, khi implement interface đó thì phải override tất cả các phương thức. Nhưng sẽ có trường hợp khi cài đặt không cần dùng đến các chức năng đó.

Interface segregation

2.5 D- Dependency inversion principle

Các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction.
Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại (Các class giao tiếp với nhau thông qua interface (abstraction), không phải thông qua implementation.)

"Trâu rừng, trâu nhà, trâu châu phi ... cuối cùng cũng là trâu Vittel, Mobi, ViNa.. cuối cùng cũng là nhà mạng" Nguyên tắc này có lợi cho việc mở rộng đối tượng: + nguyên tắc này được áp dụng trong mẫu thiết kế design pattern

Ví dụ:

Lấy ví dụ về ổ cứng của máy tính, bạn có thể dùng loại ổ cứng thể rắn SSD đời mới để chạy cho nhanh, tuy nhiên cũng có thể dùng ổ đĩa quay HDD thông thường. Nhà sản xuất Mainboard không thể nào biết bạn sẽ dùng ổ SSD hay loại HDD đĩa quay thông thường. Tuy nhiên họ sẽ luôn đảm bảo rằng bạn có thể dùng bất cứ thứ gì bạn muốn, miễn là ổ đĩa cứng đó phải có chuẩn giao tiếp SATA để có thể gắn được vào bo mạch chủ. Ở đây chuẩn giao tiếp SATA chính là interface, còn SSD hay HDD đĩa quay là implementation cụ thể.

Trong khi lập trình cũng vậy, khi áp dụng nguyên lý này, ở những lớp trừu tượng cấp cao, ta thường sử dụng interface nhiều hơn thay vì một kiểu kế thừa cụ thể. Ví dụ, để kết nối tới Database, ta thường thiết kế lớp trừu tượng DataAccess có các phương thức phương thức chung như save(), get(), … Sau đó tùy vào việc sử dụng loại DBMS nào (vd: MySql, MongoDB, …) mà ta kế thừa và implement những phương thức này. Tính chất đa hình của OOP được vận dụng rất nhiều trong nguyên lý này.

Last updated

Was this helpful?