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

Nguyên tắc thiết kế SOLID là gì? - Viblo

 

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.

Bài viết khác

HTTP & HTTPS – Nền tảng giao tiếp trên Web

HTTP & HTTPS – Nền tảng giao tiếp trên Web Khi làm web hoặc backend, có một thứ gần như xuất hiện ở mọi nơi nhưng rất dễ bị xem là hiển nhiên: HTTP. Mỗi khi mở một website, gọi một API, đăng nhập, gửi form hay tải một file, client và server đều cần […]

Return First

Return First – Tư duy xử lý điều kiện trong code Khi bắt đầu làm việc với code thực tế, chúng ta sẽ thường gặp những function chứa rất nhiều điều kiện. Ban đầu, việc sử dụng if/else để xử lý từng trường hợp là hoàn toàn bình thường. Tuy nhiên, khi logic ngày càng […]

Waterfall và Agile/Scrum: Mô hình nào phù hợp hơn?

Waterfall và Agile/Scrum: Mô hình nào phù hợp hơn? Khi tìm hiểu về phát triển phần mềm, chúng ta thường nghe rằng Agile/Scrum hiện đại hơn Waterfall và Waterfall đã trở nên lỗi thời. Tuy nhiên, đây là một cách nhìn chưa hoàn toàn chính xác. Waterfall vẫn được sử dụng trong thực tế và […]

Kanban là gì? Hiểu nhanh về phương pháp quản lý công việc

Kanban là gì? Hiểu nhanh về phương pháp quản lý công việc Khi làm việc trong một team phát triển phần mềm, việc biết ai đang làm gì, công việc đang ở đâu và task nào đang bị tồn đọng rất quan trọng. Kanban được sử dụng để giúp team trực quan hóa và quản […]

Verification vs Validation (Xác minh-Xác nhận)

1. Tổng quan nền tảng và định nghĩa cốt lõi về Verification và Validation: Trong ngành kỹ thuật phần mềm, việc kiểm soát chất lượng sản phẩm luôn đòi hỏi một hệ thống tiêu chuẩn khắt khe và rõ ràng. Tuy nhiên, ranh giới giữa việc “xem phần mềm đã đúng chuẩn chưa” và “phần […]

Waterfall là gì?

Mô hình Waterfall là gì? Waterfall (Thác nước) là một phương pháp quản lý dự án và phát triển phần mềm theo trình tự tuyến tính, nghiêm ngặt. Trong mô hình này, các giai đoạn phát triển diễn ra nối tiếp nhau như một dòng thác chảy từ trên xuống: giai đoạn trước phải hoàn […]

Leave a Reply

Your email address will not be published. Required fields are marked *