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.
1. ABC Problem
Phần tiêu đề “1. ABC Problem”Đâ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 TutorAPIlet 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.
2. Khởi tạo từ dưới lên
Phần tiêu đề “2. Khởi tạo từ dưới lên”Để 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àolet tutorAPI = TutorAPI(api: api)let todoList = TODOList(api: api)
// 3. Tạo tầng cao nhất, chỉ truyền thứ nó cầnlet courseAPI = CourseAPI(tutorAPI: tutorAPI, todoList: todoList)Bây giờ:
CourseAPIchỉ biếtTutorAPI— không biết bên trong dùngAPIthật hay mock.- Dependency graph rõ ràng, dễ đọc.
3. Factory Pattern
Phần tiêu đề “3. Factory Pattern”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.
4. Container
Phần tiêu đề “4. Container”Để 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.