Nguyên lý đảo ngược phụ thuộc - Dependency inversion principle

Nguyên lý đảo ngược phụ thuộc (Dependency inversion principle - DIP) là một nguyên lý cơ bản trong thiết kế phần mềm hướng đối tượng. Nó là nguyên lý cuối cùng và cũng rất quan trọng trong bộ nguyên lý SOLID



Nội dung nguyên lý
1. Các module cấp cao không nên phụ thuộc vào các module cấp thấp. Cả 2 nên phụ thuộc vào abstraction.
2. 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, không phải thông qua implementation)

Với cách code thông thường, các module cấp cao sẽ gọi các module cấp thấp. Module cấp cao sẽ phụ thuộc và module cấp thấp, điều đó tạo ra các dependency. Khi module cấp thấp thay đổi, module cấp cao phải thay đổi theo. Một thay đổi sẽ kéo theo hàng loạt thay đổi, giảm khả năng bảo trì của code.


Nếu tuân theo DIP, các module cấp thấp lẫn cấp cao đều phụ thuộc vào 1 interface không đổi. Ta có thể dễ dàng thay thế, sửa đổi module cấp thấp mà không ảnh hưởng gì tới module cấp cao.

Ví dụ minh họa
Cùng đọc khai báo class dưới đây


Class Cart sẽ phụ thuộc vào các module cấp thấp hơn như Database, Logger, EmailSender.
Khi thay đổi các class phụ thuộc ta lại phải vào sửa đổi code ở class này. Với project nhỏ thì có thể nhưng với những project lớn chứa hàng trăm, hàng nghìn class thì điều này là rất khó và mất rất nhiều thời gian cũng như dễ gây ra lỗi

Áp dụng DIP, ta có thể viết lại class đó như sau


Trong thực tế, người ta thường áp dụng pattern Dependency Injection (DI) để đảm bảo nguyên lý DIP trong code. Các bạn tham khảo bài viết về Dependency Injection ở đây

Code sau khi áp dụng DI sẽ như sau


Kết luận
DIP được áp dụng nhiều nhất trong code, nhưng nó cũng gây ra nhiều điều tranh cãi. Bên cạnh một số ưu điểm như

  • Giảm sự kết dính giữa các module
  • Code dễ bảo trì, dễ thay thế module
  • Rất dễ test và viết Unit Test


DIP cũng đi kèm một số khuyết điểm sau:

  • Khái niệm DI khá “khó tiêu”, các developer mới sẽ gặp khó khăn khi học
  • Sử dụng interface nên đôi khi sẽ khó debug, do không biết chính xác module nào được gọi
  • Làm tăng độ phức tạp của code

Tùy vào quy mô của từng dự án khác nhau mà mình sẽ có những cách áp dụng khác nhau. Không nên máy móc áp dụng nguyên lý này nguyên lý kia.

Không có giải pháp nào là hoàn hảo cho mọi vấn đề!

Tiếp tục cập nhật ...