Bỏ qua để đến nội dung

Dependency Injection không framework

Đã hiểu tại sao cần DI, chương này hướng dẫn cách làm DI bằng code thuần — không framework, không annotation, không magic.

Đây là vấn đề phổ biến khi làm DI “ngây thơ”:

  • Class A cần Class B
  • Class B cần Class C
  • → Phải truyền C cho A, để A truyền cho B

Ví dụ: CourseAPI cần TutorAPI. TutorAPI cần API. Nếu truyền theo kiểu pass-through:

// ❌ CourseAPI phải biết về API — dù nó chỉ cần TutorAPI
let courseAPI = CourseAPI(tutorAPI: TutorAPI(), api: API())

CourseAPI bị phụ thuộc vào API dù không trực tiếp dùng → unnecessary coupling.

Để giải quyết, ta không truyền dependency xuyên qua các tầng trung gian. Thay vào đó, khởi tạo từ leaf nodes lên:

// 1. Tạo tầng thấp nhất (leaf)
let api = API(network: ProductionNetwork())
// 2. Tạo tầng giữa, truyền leaf vào
let tutorAPI = TutorAPI(api: api)
let todoList = TODOList(api: api)
// 3. Tạo tầng cao nhất, chỉ truyền thứ nó cần
let courseAPI = CourseAPI(tutorAPI: tutorAPI, todoList: todoList)

Bây giờ:

  • CourseAPI chỉ biết TutorAPI — không biết bên trong dùng API thật hay mock.
  • Dependency graph rõ ràng, dễ đọc.

Nếu việc khởi tạo quá dài hoặc cần tạo object lúc runtime (chưa có đủ info khi app start):

struct CourseAPIFactory {
let api: API
func makeCourseAPI() -> CourseAPI {
let tutorAPI = TutorAPI(api: api)
return CourseAPI(tutorAPI: tutorAPI, ...)
}
}

Factory encapsulate logic khởi tạo — caller không cần biết chi tiết bên trong.

Để quản lý toàn bộ dependency tree của app, tạo một Container — composition root:

class AppContainer {
let api = API()
lazy var courseAPI = CourseAPIFactory(api: api).makeCourseAPI()
}

Đây là DI đơn giản nhất: không framework, không annotation, chỉ là code thuần.


Kết luận: Bí quyết DI bằng code thuần: khởi tạo từ leaf lên, dùng Factory khi cần deferred creation, và gom tất cả vào Container. Đơn giản, dễ debug, dễ hiểu.