SETTER INJECTION LÀ GÌ? SỰ LINH HOẠT TRONG THẾ GIỚI TIÊM PHỤ THUỘC
sau khi đã mổ xẻ "ông vua" Constructor Injection ở bài viết trước, hôm nay chúng ta sẽ khám phá người anh em còn lại trong gia đình Dependency Injection: Setter Injection (Tiêm phụ thuộc qua phương thức Setter).
Nếu Constructor Injection giống như việc trang bị đầy đủ áo giáp và vũ khí cho chiến binh trước khi họ bước vào trận chiến, thì Setter Injection lại mang một triết lý linh hoạt hơn rất nhiều. Hãy cùng mổ xẻ chi tiết.
1. Bản Chất Của Setter Injection Là Gì? (The What)
Trong Dependency Injection, thay vì truyền các dịch vụ phụ thuộc thông qua hàm khởi tạo (constructor) ngay khi đối tượng vừa được sinh ra, Setter Injection cho phép đối tượng được khởi tạo một cách trống trơn trước, sau đó các phụ thuộc sẽ được "bơm" vào sau thông qua các phương thức công khai gọi là Setter methods (ví dụ: setLogger(), setCache()).
Nhìn qua một đoạn code mô phỏng bằng PHP hiện đại:
PHP
class ReportGenerator
{
private ?LoggerInterface $logger = null;
// 1. Không bắt buộc phải có dependencies khi vừa "new" đối tượng
public function __construct()
{
// Khởi tạo các giá trị cơ bản
}
// 2. Tiêm phụ thuộc thông qua phương thức Setter bất cứ lúc nào sau đó
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
public function generate()
{
if ($this->logger) {
$this->logger->info('Đang tạo báo cáo...');
}
// Logic sinh báo cáo...
}
}
// Cách sử dụng:
$report = new ReportGenerator();
// Tiêm dependency vào sau khi đối tượng đã tồn tại
$report->setLogger(new FileLogger());
$report->generate();
2. Khi Nào Nên Dùng Setter Injection? (Ưu Điểm)
Dù không phải là lựa chọn mặc định như Constructor Injection, Setter Injection cực kỳ hữu dụng trong một số kịch bản đặc thù:
-
Xử lý các phụ thuộc tùy chọn (Optional Dependencies): Nếu một class có thể hoạt động hoàn hảo mà không cần dịch vụ đó (ví dụ: Service có thể chạy độc lập, nhưng nếu có Logger thì ghi log, không có thì thôi), Setter Injection là giải pháp hoàn hảo. Bạn không bắt buộc người dùng phải truyền vào những thứ không cần thiết ngay lúc khởi tạo.
-
Thay đổi phụ thuộc khi đang chạy (Re-configuration at Runtime): Trong vòng đời của một ứng dụng dài hạn (như Worker chạy ngầm, Daemon), bạn có thể muốn thay đổi cấu hình kết nối hoặc đổi chiến lược xử lý (strategy) giữa chừng mà không cần phải hủy đối tượng cũ đi để
newlại từ đầu. Chỉ cần gọi lại hàm setter là xong! -
Giải quyết bài toán phụ thuộc vòng (Circular Dependency): Đôi khi Service A cần Service B, và Service B lại quay sang cần Service A. Dùng Constructor Injection trong trường hợp này sẽ dẫn đến lỗi tràn bộ nhớ (infinite recursion). Setter Injection giúp phá vỡ vòng luẩn quẩn đó bằng cách cho phép khởi tạo đối tượng trước, rồi mới gắn kết nối sau.
3. Mặt Tối Và Cạm Bẫy Cần Lưu Ý (The Pitfalls)
Tại sao các framework hiện đại ít khi khuyến khích dùng Setter Injection làm phương án chính?
-
Trạng thái không hợp lệ (Invalid State / Nullable Risk): Vì đối tượng được phép khởi tạo mà không cần dependencies, lập trình viên rất dễ quên gọi hàm
set...()trước khi sử dụng. Hậu quả là chương trình chạy đến giữa chừng thì văng lỗiNullPointerExceptionhoặc gọi trúng đối tượng trống (null). -
Phá vỡ tính bất biến (Mutability): Vì các phụ thuộc có thể bị thay đổi bất cứ lúc nào thông qua hàm setter bên ngoài, đối tượng của bạn trở nên "mềm dẻo" quá mức, khó kiểm soát được luồng dữ liệu và dễ phát sinh bug ngầm trong hệ thống đa luồng.
💡 Lời Kết
Nếu Constructor Injection là tiêu chuẩn vàng mang lại sự an toàn, cứng cáp và tính bất biến cho code, thì Setter Injection lại là một công cụ bổ trợ linh hoạt dành cho các cấu hình tùy chọn và thay đổi trạng thái động. Hiểu rõ ưu nhược điểm của cả hai sẽ giúp bạn pha trộn chúng một cách khéo léo để thiết kế nên những kiến trúc phần mềm vừa an toàn vừa uyển chuyển.
All rights reserved