HomeOur Team
DIP in Android, Dagger and Koin (P1)
Articles
DIP in Android, Dagger and Koin (P1)
thang.duong
thang.duong
March 28, 2021
5 min

1. Giới thiệu

Là một lập trình viên chắc hẳn ai cũng ít nhất một lần nghe đến khái niệm SOLID.

SOLID là viết tắt của 5 chữ cái đầu trong 5 nguyên tắc thiết kế hướng đối tượng. Giúp cho lập trình viên viết ra những đoạn code dễ đọc, dễ hiểu, dễ maintain. Nó được đưa ra bởi Robert C. Martin và Michael Feathers. 5 nguyên tắc đó bao gồm:

  • Single responsibility priciple (SRP)
  • Open/Closed principle (OCP)
  • Liskov substitution principe (LSP)
  • Interface segregation principle (ISP)
  • Dependency inversion principle (DIP)

Để hiểu thêm về SOLID các bạn có thể đọc bài viết của anh ToiDiCodeDao.

Trong bài viết hôm nay, mình sẽ nói về nguyên tắc Dependency Inversion (DIP) và cách áp dụng nó trong lập trình Android.

2. Dependency Inversion Principle (DIP)

DIP phát biểu như sau:

  • Các module, class cấp cao (high-level) không nên phụ thuộc vào module, class cấp thấp hơn (low-level) mà nên phụ thuộc với nhau thông qua một abstraction.
  • Abtraction không nên phụ thuộc vào chi tiết. Chi tiết nên phụ thuộc vào abtraction.

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.

ioc and mapper in c 4 638

Nếu tuân theo Dependendy Inversion principle, các module cùng 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.

Chúng ta thường hay lẫn lộn giữa các khái niệm Dependency Inversion Principle (DIP), Inversion of Control (IoC), Dependency Injection (DI). Ba khái niệm này tương tự nhau, tất cả đều hướng đến một mục đích duy nhất là tạo ra ứng dụng ít kết dính (loosely coupling), dễ mở rộng (flexibility) cũng như giúp lập trình viên tập trung chủ yếu vào công việc business flow.

Sự khác biệt giữa 3 khái niệm trên như sau:

  • Dependency Inversion Principle (DIP): là một nguyên lý để thiết kế và viết code.
  • Inversion of Control (IoC): là một design pattern được tạo ra để code có thể tuân thủ nguyên lý Dependency Inversion. Có nhiều cách hiện thực Pattern này như Service Locator, Event, Delegate, … và Dependency Injection là một trong các cách đó.
  • Dependency Injection: Đây là một cách để hiện thực Inversion of Control Pattern.

Vì trong lập trình Android 2 khái niệm được áp dụng chủ yếu và phổ biến nhất là Dependency Injection (DI)Service Locator (SL) nên trong bài mình sẽ nói rõ về 2 khái niệm này

2.1 Dependency Injection (DI)

Dependency Injection (DI) là một design pattern, một kỹ thuật cho phép xóa bỏ sự phụ thuộc giữa các module, làm cho ứng dụng dễ dàng hơn trong việc thay đổi module, bảo trì code và testing.

DI cung cấp cho một class các phụ thuộc (dependencies) của nó từ bên ngoài truyền vào mà không phải khởi tạo trực tiếp từ trong class đó.

Nguyên tắc hoạt động của DI:

  • Các module không giao tiếp trực tiếp với nhau, mà thông qua interface. Module cấp thấp sẽ implement interface, module cấp cao sẽ gọi module cấp thấp thông qua interface.
  • Việc khởi tạo các module cấp thấp sẽ do DI Container thực hiện.
  • Việc Module nào gắn với interface nào sẽ được config trong file properties, trong file XML hoặc thông qua Annotation.

Các dạng DI

  • Constructor injection: Các dependency sẽ được container truyền vào (inject vào) 1 class thông qua constructor của class đó. Đây là cách thông dụng nhất.
  • Setter injection: Các dependency sẽ được truyền vào 1 class thông qua các hàm setter.
  • Fields/ properties: Các dependency sẽ được truyền vào 1 class một cách trực tiếp vào các field.
  • Interface injection: Class cần inject sẽ implement 1 interface. Interface này chứa 1 hàm tên Inject. Container sẽ injection dependency vào 1 class thông qua việc gọi hàm Inject của interface đó. Đây là cách rườm rà và cũng ít được sử dụng.

Ưu điểm:

  • Reduced dependencies: giảm sự kết dính giữa các module.
  • Reusable: code dễ bảo trì, dễ tái sử dụng, thay thế module. Giảm boiler-plate code do việc tạo các biến phụ thuộc đã được injector thực hiện.
  • Testable: rất dễ test và viết Unit Test.
  • Readable: dễ dàng thấy quan hệ giữa các module vì các dependecy đều được inject vào constructor.

Khuyết điểm:

  • Khái niệm DI khá khó hiểu đối với người mới tìm hiểu.
  • 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.
  • Các object được khởi tạo toàn bộ ngay từ đầu, có thể làm giảm performance.
  • Có thể gặp lỗi ở run-time thay vì compile-time.

2.2 Service Locator (SL)

Service Locator là một design pattern thông dụng cho phép tách rời một class với các dependency của nó. Service Locator có thể coi là một đối tượng trung gian trong việc liên kết class và các dependency. Service Locator pattern mô tả cách để đăng ký và lấy các dependency để sử dụng.

Ưu điểm:

  • Nó tạo và giữ một thể hiện của các class Service trong một ServiceLocator duy nhất.
  • Tách rời một class với các dependency của nó, nhờ đó có thể dễ dàng thay thế các dependency tại thời điểm run-time mà không cần phải re-compile hay thậm chí là restart ứng dụng.
  • Các dependency sẽ được sử dụng dưới dạng interface, đảm bảo không sử dụng các class cụ thể (concrete) của dependency.
  • Dễ dàng test các class do không phụ thuộc vào các dependency.
  • Ứng dụng có thể được chia ra các phần ít bị ràng buộc (loose coupling) với nhau. Các đoạn code để tạo, quản lý dependency được tách riêng ra khỏi các class.

Nhược điểm:

  • Khó khăn khi debug và phát hiện lỗi do Client không biết được phần còn lại của hệ thống, Service nào sẽ được thực thi, mọi thứ được đăng ký với Service Locator.
  • Service Locator phải là duy nhất, nên có thể gặp hiện tượng thắt cổ chai.
  • Service Locator che dấu các lớp được đăng ký với Client, do đó có thể gặp lỗi run-time thay vì compile-time khi Service không tồn tại.
  • Service Locator làm cho code khó bảo trì hơn so với việc sử dụng DI (Dependency Injection). Các concrete class vẫn có phụ thuộc vào ServiceLocator, ServiceLocator chịu trách nhiệm tạo ra các phụ thuộc của nó. Với DI chỉ được gọi 1 lần để tiêm phụ thuộc vào một số lớp chính. Các lớp mà lớp chính này phụ thuộc vào sẽ đệ quy các phụ thuộc của chúng, cho đến khi có một đối tượng hoàn chỉnh. DI dễ viết Unit Test hơn.

Áp dụng DIP trong lập trình Android

Như mình đã nhắc ở trên thì trong Android cách phổ biến nhất để áp dụng DIPDISL. Tương ứng là 2 thư viện DaggerKoin.

Bài viết khá dài nên mình chia ra 2 phần, phần sau mình sẽ nói về 2 thư viện DaggerKoin.

Source:


Tags

202103androiddidaggerkoin
thang.duong

thang.duong

Developer

Related Posts

Why Protocol-Oriented Programming?
Why Protocol-Oriented Programming?
March 31, 2021
2 min
© 2021, All Rights Reserved.

Quick Links

HomeOur Team

Social Media