Nếu chúng ta chỉ biết về những dòng code khô khan thì chúng ta vẫn chỉ là những coder hay dev quèn thôi. Vậy chúng ta cần làm gì để trở thành dev xịn. Để trở thành dev xịn, chúng ta cần phải tìm hiểu thêm nhiều các nguyên lý, cách tổ chức code,… Và SOLID là 1 trong những nguyên lý mà các dev cần phải biết và có thể sử dụng khi phỏng vấn. Chúng ta cùng tìm hiểu nhé.
SOLID có nghĩa là tập hợp của 5 nguyên tắc sau:
- Single responsibility principle
- Open/closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
Nguyên tắc 1: Single responsibility principle
Một class chỉ nên giữ 1 trách nhiệm duy nhất
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 CSDL như này:
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:
Nguyên tắc 2: Open/closed principle
Có thể thoải mái mở rộng 1 class, nhưng không được sửa đổi bên trong class đó
Nguyên lý này khá dễ hiểu. Ý nghĩa là khi chúng ta muốn thêm chức năng cho chương trình thì chúng ta nên kế thừa lai class đó chứ không nên sửa đổi lại class đó.
Nguyên tắc 3: Liskov substitution principle
Trong một chương trình, các object của class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình
Hơi khó hiểu? Không sao, lúc mới đọc mình cũng vậy. Hãy tưởng tượng bạn có 1 class cha tên Vịt. Các class VịtBầu, VịtXiêm có thể kế thừa class này, chương trình chạy bình thường. Tuy nhiên nếu ta viết class VịtChạyPin, cần pin mới chạy được. Khi class này kế thừa class Vịt, vì class Vịt không có pin không chạy được, sẽ gây lỗi. Đó là 1 trường hợp vi phạm nguyên lý này.
Nguyên tắc 4: 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ể
Nguyên lý này khá dễ hiểu. Hãy tưởng tượng chúng ta có 1 interface lớn, khoảng 100 methods. Việc implements sẽ khá cực khổ, ngoài ra còn có thể dư thừa vì 1 class không cần dùng hết 100 method. Khi tách interface ra thành nhiều interface nhỏ, gồm các method liên quan tới nhau, việc implement và quản lý sẽ dễ hơn.
Nguyên tắc 5: Dependency inversion principle
1. 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.
2. Abstraction không nên phụ thuộc vào detail. Detail nên phụ thuộc vào abstraction.
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.