SOLID – 5 nguyên tắc giúp thiết kế code tốt hơn
SOLID là 5 nguyên tắc thiết kế trong lập trình hướng đối tượng (OOP), giúp code dễ đọc, dễ thay đổi, dễ mở rộng và dễ bảo trì.
SOLID không phải framework hay tool mà là tư duy thiết kế code. Nó giúp hạn chế những vấn đề thường gặp khi dự án lớn dần như class quá lớn, dependency quá chặt, sửa một chỗ ảnh hưởng nhiều chỗ hoặc thêm tính năng mới trở nên khó khăn.
SOLID 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 – SRP
một class nên có một trách nhiệm chính và một lý do để thay đổi một class không nên ôm quá nhiều loại công việc khác nhau.
Ví dụ, một UserService vừa xử lý logic user, vừa lưu database, vừa gửi email:
UserService
├── xử lý User
├── lưu Database
└── gửi Email
Khi database thay đổi, UserService phải thay đổi. Khi cách gửi email thay đổi, nó cũng phải thay đổi.
Có thể tách thành:
UserService
UserRepository
EmailService
Mỗi thành phần tập trung vào một nhóm trách nhiệm liên quan.
SRP không có nghĩa là mỗi class chỉ được có đúng một method. Điều quan trọng là các trách nhiệm bên trong class phải có sự liên quan chặt chẽ với nhau.
tóm lại: khi một class có quá nhiều công việc cũng như có quá nhiều lí do để thay đổi đó là cái nên xem xét lại cái class đó
2. Open/Closed Principle – OCP
code nên viết để có thể mở rộng và tái sử dụng nhưng hạn chế phải sửa code đã ổn định.
ví dụ cho việc trên có một hệ thống thanh toán:
CreditCard
Momo
Paypal
nếu mỗi lần thêm một phương thức mới là phải code một if else lớn cho phương thức đó:
if CreditCard
else if Momo
else if Paypal
else if ...
càng thêm phương thức chức năng code càng khó quản lý.
Thay vào đó, có thể định nghĩa một abstraction chung:
Payment
├── CreditCardPayment
├── MomoPayment
├── PaypalPayment
└── ZaloPayPayment
Khi muốn thêm ZaloPay, chỉ cần tạo implementation mới thay vì liên tục sửa logic cũ.
OCP không có nghĩa là không bao giờ được sửa code. Ý chính là những phần đã ổn định nên hạn chế bị ảnh hưởng mỗi khi hệ thống cần mở rộng.
tóm lại: khi mình thêm một tính năng mới nào đó hãy nghĩ đến việc phải mở rộng hệ thống thay vì thay đổi code cũ.
3. Liskov Substitution Principle – LSP
Class con phải có thể thay thế class cha mà không làm chương trình hoạt động sai.
ví dụ dễ hiểu:
Bird
└── fly()
Sau đó tạo:
Penguin extends Bird
Nhưng Penguin lại không thể bay.
Nếu code đang mong đợi một Bird có khả năng fly() mà nhận vào Penguin, hệ thống có thể gặp vấn đề.
Điều này cho thấy abstraction ban đầu đã không phù hợp.
thay vì coi tất cả các loài chim đều có khả năng bay, ta có thể tách chưng năng bay đó thành một abtraction riêng:
Bird
├── Eagle
└── Penguin
Flying
├── Eagle
└── ...
LSP không chỉ nói về inheritance. Cốt lõi của nó là behavior: class con phải tuân thủ những gì abstraction hoặc class cha đã cam kết.
tóm lại: nếu một class con không thể hoạt động một cách hợp lý ở nơi class cha được mong đợi, có thể thiết kế abstraction đang có vấn đề.
4. Interface Segregation Principle – ISP
không nên ép một class phụ thuộc và các method mà nó không cần.
ví dụ để hiểu:
Worker
├── work()
├── eat()
├── sleep()
└── program()
một human có thể sử dụng tất cả các method. nhưng một con robot thì chỉ có thể sử dụng hợp lý hai method là work
và program.
Nếu bắt Robot implement toàn bộ interface, nó sẽ phải viết những method không có ý nghĩa.
Thay vì một interface quá lớn, có thể chia nhỏ:
Workable
Programmable
Eatable
Sleepable
Mỗi class chỉ cần sử dụng những interface phù hợp với nó.
ISP giúp interface trở nên nhỏ, rõ ràng và đúng nhu cầu sử dụng.
tóm lại: đừng tạo một interface lớn rồi bắt mọi implement phải phụ thuộc vào toàn bộ những gì bên trong nó.
5. Dependency Inversion Principle – DIP
Code cấp cao không nên phụ thuộc trực tiếp vào implementation cụ thể. Cả hai nên phụ thuộc vào abstraction.
Ví dụ:
OrderService
↓
MySQL
OrderService phụ thuộc trực tiếp vào MySQL. Nếu sau này chuyển sang PostgreSQL hoặc một database khác, logic bên trên có thể phải thay đổi.
Một thiết kế tốt hơn:
OrderRepository
↑ ↑
MySQL PostgreSQL
↑
OrderService
OrderService chỉ biết đến OrderRepository, còn việc sử dụng MySQL hay PostgreSQL được xử lý ở tầng implementation.
Đây cũng là lý do chúng ta thường thấy Dependency Injection trong các framework hiện đại.
DIP giúp business logic không bị gắn chặt với một công nghệ cụ thể.
tóm lại: logic quan trọng của hệ thống nên phụ thuộc vào abstraction thay vì phụ thuộc trực tiếp vào implement.







Khoá học lập trình game con rắn cho trẻ em




