Dependency Injection (DI) Trong Python: Hướng Dẫn Chi Tiết
Trong lập trình hướng đối tượng (OOP), Dependency Injection (DI) là một kỹ thuật giúp giảm sự phụ thuộc giữa các lớp và tăng tính linh hoạt của ứng dụng.
📌 Nội dung chính:
1️⃣ Dependency Injection là gì?
2️⃣ Tại sao cần sử dụng Dependency Injection?
3️⃣ Cách triển khai DI trong Python
4️⃣ DI với Constructor Injection
5️⃣ DI với Method Injection
6️⃣ DI với Property Injection
7️⃣ Sử dụng DI trong thực tế với Flask
1. Dependency Injection Là Gì?
💡 Dependency Injection (DI) là một kỹ thuật trong đó các dependency (phụ thuộc) của một class được truyền từ bên ngoài, thay vì tạo trực tiếp bên trong class.
✔ Giảm sự phụ thuộc giữa các class
✔ Dễ dàng mở rộng và thay thế dependency
✔ Dễ dàng kiểm thử (Unit Test)
Ví dụ về dependency mà không có DI:
class Database:
def connect(self):
return "Kết nối database..."
class UserService:
def __init__(self):
self.db = Database() # Phụ thuộc cứng vào Database
def get_user(self):
return f"User từ {self.db.connect()}"
🚨 Vấn đề: UserService bị ràng buộc với Database, nếu ta muốn đổi sang MongoDB, ta phải sửa trực tiếp trong class.
2. Tại Sao Cần Dependency Injection?
Nếu không sử dụng DI, class sẽ bị phụ thuộc chặt chẽ vào các dependency. Điều này gây khó khăn khi:
❌ Muốn thay thế dependency (VD: đổi từ MySQL sang MongoDB)
❌ Muốn kiểm thử (Unit Test)
❌ Mở rộng ứng dụng mà không sửa nhiều code
✅ DI giúp:
- Dễ bảo trì: Có thể thay thế dependency mà không cần thay đổi class
- Tái sử dụng: Class không còn bị khóa chặt vào một dependency cụ thể
- Dễ kiểm thử: Có thể dùng dependency giả lập (mock) trong test
3. Cách Triển Khai DI Trong Python
Không dùng DI (cách truyền thống)
class Database:
def connect(self):
return "Kết nối MySQL"
class UserService:
def __init__(self):
self.db = Database() # Phụ thuộc trực tiếp vào Database
def get_user(self):
return f"User từ {self.db.connect()}"
🚨 Nhược điểm: Khó thay đổi Database, khó kiểm thử.
Dùng Dependency Injection
class Database:
def connect(self):
return "Kết nối MySQL"
class UserService:
def __init__(self, database): # Dependency Injection qua constructor
self.db = database
def get_user(self):
return f"User từ {self.db.connect()}"
# Truyền dependency từ bên ngoài
db = Database()
user_service = UserService(db)
print(user_service.get_user()) # Output: User từ Kết nối MySQL
📌 Lợi ích:
✔ UserService không bị khóa vào Database nữa.
✔ Có thể truyền một database khác mà không cần sửa UserService.
4. DI Với Constructor Injection
Constructor Injection là cách phổ biến nhất để truyền dependency.
class Logger:
def log(self, message):
print(f"LOG: {message}")
class App:
def __init__(self, logger):
self.logger = logger
def run(self):
self.logger.log("Ứng dụng đang chạy...")
logger = Logger()
app = App(logger) # Truyền dependency từ bên ngoài
app.run()
📌 Lợi ích:
✔ Tách biệt trách nhiệm (Separation of Concerns)
✔ Dễ kiểm thử
5. DI Với Method Injection
Thay vì truyền dependency vào constructor, ta truyền vào method.
class Logger:
def log(self, message):
print(f"LOG: {message}")
class App:
def run(self, logger): # Dependency Injection qua method
logger.log("Ứng dụng đang chạy...")
logger = Logger()
app = App()
app.run(logger)
📌 Ưu điểm: Linh hoạt, chỉ truyền dependency khi cần.
6. DI Với Property Injection
Ở cách này, dependency được gán trực tiếp vào thuộc tính của class.
class Logger:
def log(self, message):
print(f"LOG: {message}")
class App:
def __init__(self):
self.logger = None # Không khởi tạo sẵn
def run(self):
if self.logger:
self.logger.log("Ứng dụng đang chạy...")
app = App()
app.logger = Logger() # Truyền dependency từ bên ngoài
app.run()
📌 Ưu điểm: Có thể thay đổi dependency bất kỳ lúc nào.
7. Sử Dụng Dependency Injection Trong Flask
Flask có hỗ trợ DI thông qua việc truyền dependency vào function handler.
from flask import Flask
app = Flask(__name__)
class UserService:
def get_user(self):
return {"name": "Thảo", "age": 21}
def user_handler(user_service: UserService): # DI trong Flask
user = user_service.get_user()
return user
@app.route("/user")
def get_user():
return user_handler(UserService()) # Truyền dependency
if __name__ == "__main__":
app.run(debug=True)
📌 Lợi ích: Dễ dàng thay đổi UserService mà không sửa nhiều code.
8. Khi Nào Nên Dùng Dependency Injection?
✅ Khi muốn tách biệt dependency để dễ bảo trì và mở rộng.
✅ Khi làm việc với các hệ thống lớn có nhiều module.
✅ Khi cần viết unit test dễ dàng hơn.
🚨 Không nên dùng DI nếu:
- Ứng dụng quá nhỏ, việc tạo dependency không gây ảnh hưởng gì lớn.
9. Tổng Kết
✔ DI giúp tách biệt dependency và dễ dàng thay đổi chúng.
✔ Có 3 cách triển khai DI: Constructor Injection, Method Injection, Property Injection.
✔ Flask có hỗ trợ DI, giúp làm việc với service dễ dàng hơn.
💡 Bạn có câu hỏi gì về Dependency Injection? Hãy để lại bình luận bên dưới!