Architecture

Dependency Injection Explained: Dependency injection

Phần hai: Dependency không ăn được. Vậy dependency injection thì sao ?

Updated: Video cho bài viết này. Các bạn có thể xem ở đây, tuy nhiên đọc bài viết sẽ chi tiết hơn nhé:

Ở phần một chúng ta đã cùng tìm hiểu về Dependency. Nếu các bạn chưa đọc thì mình sẽ để link ở đây, hãy đọc nó trước khi tiếp tục bài viết này nhé.

Trong phần này chúng ta sẽ tìm hiểu về dependency injection. Một nhóm các nguyên tắc cơ bản và quan trọng nhất để viết được một phần mềm tốt.

Một phần mềm tốt cho phép các developers làm việc độc lập, dễ dàng bảo trì, thêm, sửa hay thậm chí xoá chức năng, dễ dàng test và debug, có khả năng tái sử dụng các components…

Khi đọc bài viết này bạn có thể sẽ cảm thấy hoá ra trước giờ mình đã áp dụng dependency injection rồi mà không nhận ra, có thể do từng thấy một ông senior ở công ty sử dụng nên bắt trước, hoặc đâu đó ở trên mạng… Còn nếu chưa từng sử dụng thì cũng không sao cả, bạn hoàn toàn có thể áp dụng nó sau khi đọc bài viết này vì nó không có gì khó cả đâu 😊.

Definition

Dependency injection là tập hợp một nhóm các patterns giúp bạn quản lý các dependencies bằng cách truyền dependencies một cách “công khai” (explicitly )

Nếu như trong phần một của bài viết, mình đã giới thiệu cho các bạn hai loại dependency là implicit dependency và explicit dependency thì từ định nghĩa các bạn có thể hiểu, Dependency injection là tập hợp một nhóm các patterns giúp bạn tạo ra các explicit dependencies. Đó là lý do vì sao mình nói ở bài viết trước rằng một khi bạn hiểu về dependency thì việc hiểu về dependency injection sẽ trở nên vô cùng dễ dàng 😇.

OK, vậy “nhóm các patterns” mà trong định nghĩa nhắc tới là những patterns nào ?

Three types of dependency injection

Constructor injection

Là khi bạn truyền các dependencies thông qua hàm khởi tạo của component phụ thuộc.

Như các bạn có thể thấy ở ví dụ trên, ListMovieViewModel là dependency và được truyền vào thông qua hàm khởi tạo của ListMovieController và chính do vậy ListMovieController không thể được khởi tạo nếu thiếu ListMovieViewModel.

Khi sử dụng Constructor injection, bạn đang nói rằng: “Hey, nếu mày muốn tạo ra class A, mày cần truyền cho nó dependencies X, Y, Z…

When to use constructor injection

Một điều đơn giản: hãy sử dụng constructor injection bất cứ khi nào có thể và xem nó là pattern mặc định của dependency injection, nhất là khi các dependencies bạn truyền trong hàm khởi tạo sẽ được lưu trữ và sử dụng xuyên suốt phục vụ cho component phụ thuộc.

Property injection

Là khi bạn truyền các dependencies sau khi object được khởi tạo thông qua các properties của object đó.

When to use property injection

Bạn chỉ nên sử dụng property injection khi dependency có một giá trị mặc định đủ tốt, trong hầu hết trường hợp sử dụng bạn sẽ sử dụng giá trị mặc định đó, tuy nhiên property injection giúp bạn có thể thay đổi giá trị mặc định đó nếu bạn muốn.
Như ở ví dụ trên, khi sử dụng bình thường mình sẽ sử dụng giá trị mặc định của session và có thể chỉ thay nó bằng một mock session khi viết unit test.

Method injection

Là khi bạn truyền các dependencies sau khi object được khởi tạo thông qua các phương thức của object đó.

When to use method injection

Bạn sử dụng method injection khi các dependencies thay đổi ở mỗi lần gọi method. Xem xét ví dụ ở trên, mỗi lần mình gọi hàm load(url:completion:) mình sẽ muốn truyền cho nó một url khác, nếu url không đổi bạn nên sử dụng constructor injection.
Một lưu ý khi sử dụng method injection là dependency trong method injection không nên được lưu lại trong object để sử dụng cho các method khác, nếu bạn cần lưu trữ dependency, một lần nữa, hãy sử dụng constructor injection.

Why dependency injection ?

Có một điều dễ nhận thấy là các patterns trong dependency injection luôn truyền các dependencies cho component phụ thuộc bằng một cách nào đó từ bên ngoài vào chứ không khởi tạo dependencies bên trong component phụ thuộc. Điều này có ý nghĩa gì không ?

Có ! Một số lợi điểm trong quá trình học hỏi và áp dụng dependency injection mình muốn kể ra ở đây:

  • Dependency injection hướng tới sự rõ ràng trong cách sử dụng dependency. Khi khởi tạo ViewControllerB ta biết rằng ta cần truyền cho nó dependency gì để nó có thể hoạt động.
  • Dependency injection cho phép bạn test các components một cách độc lập. Nó cho phép ta dễ dàng thay thế dependency bằng một mock object trong test mà không cần thay đổi code ở production.
  • Dependency injection giúp tăng khả năng maintain và tính mở rộng. Chính việc cho phép thao tác với dependency từ bên ngoài giúp ta có thể truyền các dependency khác nhau để tăng tính mở rộng mà không làm ảnh hưởng tới component phụ thuộc.

Dependency Injection (DI) vs. Dependency Inversion Principle (DIP)

Cho bạn nào chưa biết thì dependency inversion principle là nguyên tắc cuối trong năm nguyên tắc lập trình SOLID.

Để so sánh hai khái niệm này thì nếu xem DIP là lý thuyết thì DI là thực hành. Nếu ví DIP giống như cái đích, cái mục tiêu mà chúng ta muốn đạt được, thì DI thì giống như một con đường để đi đến cái đích đó.

Conclusion

Phù… Vậy là chúng ta đã cùng tìm hiểu xong về dependency injection. Tất nhiên, sức mạnh của dependency injection không chỉ có thế, những gì mình viết trong bài đều chỉ là những kiến thức cơ bản, các bạn có thể đọc và tìm hiểu thêm về nó nhé.

Hy vọng các bạn đã học thêm được một chút qua bài viết của mình. Cảm ơn các bạn đã đọc đến đây. Nếu thấy bài viết hữu ích, đừng quên share để ủng hộ mình. Nếu có góp ý, câu hỏi thì hãy để lại ở phần bình luận mình sẽ trả lời nhé. Love you all 😘.


Tham khảo:

  1. Dependency Injection Principles, Practices, and Patterns — Mark Seemann & Steven van Deursen.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *