Hãy lấy một ví dụ đơn giản-- có lẽ bạn đang tiêm một phương tiện đăng nhập.
Tiêm một lớp
class Worker: IWorker
{
ILogger _logger;
Worker(ILogger logger)
{
_logger = logger;
}
void SomeMethod()
{
_logger.Debug("This is a debug log statement.");
}
}
Tôi nghĩ rằng đó là khá rõ ràng những gì đang xảy ra. Hơn nữa, nếu bạn đang sử dụng bộ chứa IoC, bạn thậm chí không cần tiêm bất cứ thứ gì một cách rõ ràng, bạn chỉ cần thêm vào gốc thành phần của mình:
container.RegisterType<ILogger, ConcreteLogger>();
container.RegisterType<IWorker, Worker>();
....
var worker = container.Resolve<IWorker>();
Khi gỡ lỗi Worker
, nhà phát triển chỉ cần tham khảo gốc thành phần để xác định lớp cụ thể nào đang được sử dụng.
Nếu một nhà phát triển cần logic phức tạp hơn, anh ta có toàn bộ giao diện để làm việc với:
void SomeMethod()
{
if (_logger.IsDebugEnabled) {
_logger.Debug("This is a debug log statement.");
}
}
Tiêm một phương pháp
class Worker
{
Action<string> _methodThatLogs;
Worker(Action<string> methodThatLogs)
{
_methodThatLogs = methodThatLogs;
}
void SomeMethod()
{
_methodThatLogs("This is a logging statement");
}
}
Đầu tiên, lưu ý rằng tham số constructor có tên dài hơn bây giờ , methodThatLogs
. Điều này là cần thiết bởi vì bạn không thể nói những gì Action<string>
cần phải làm. Với giao diện, nó đã hoàn toàn rõ ràng, nhưng ở đây chúng ta phải dùng đến việc dựa vào việc đặt tên tham số. Điều này dường như ít đáng tin cậy và khó thực thi hơn trong quá trình xây dựng.
Bây giờ, làm thế nào để chúng ta tiêm phương pháp này? Chà, container IoC sẽ không làm điều đó cho bạn. Vì vậy, bạn còn lại tiêm nó một cách rõ ràng khi bạn khởi tạo Worker
. Điều này đặt ra một số vấn đề:
- Đó là nhiều công việc để khởi tạo một
Worker
- Các nhà phát triển cố gắng gỡ lỗi
Worker
sẽ thấy khó khăn hơn để tìm ra trường hợp cụ thể nào được gọi. Họ không thể chỉ tham khảo các gốc thành phần; họ sẽ phải theo dõi thông qua mã.
Nếu chúng ta cần logic phức tạp hơn thì sao? Kỹ thuật của bạn chỉ phơi bày một phương pháp. Bây giờ tôi cho rằng bạn có thể nướng những thứ phức tạp vào lambda:
var worker = new Worker((s) => { if (log.IsDebugEnabled) log.Debug(s) } );
nhưng khi bạn đang viết bài kiểm tra đơn vị của mình, làm thế nào để bạn kiểm tra biểu thức lambda đó? Nó ẩn danh, vì vậy khung kiểm tra đơn vị của bạn không thể khởi tạo trực tiếp. Có thể bạn có thể tìm ra một số cách thông minh để làm điều đó, nhưng nó có thể sẽ là một PITA lớn hơn so với sử dụng giao diện.
Tóm tắt về sự khác biệt:
- Chỉ tiêm một phương pháp làm cho việc suy luận mục đích trở nên khó khăn hơn, trong khi một giao diện truyền đạt rõ ràng mục đích.
- Chỉ tiêm một phương thức làm lộ ít chức năng hơn đối với lớp nhận tiêm. Ngay cả khi bạn không cần nó ngày hôm nay, bạn có thể cần nó vào ngày mai.
- Bạn không thể tự động chỉ tiêm một phương thức bằng cách sử dụng bộ chứa IoC.
- Bạn không thể biết từ gốc thành phần mà lớp cụ thể đang làm việc trong một trường hợp cụ thể.
- Đó là một vấn đề để đơn vị kiểm tra chính biểu thức lambda.
Nếu bạn ổn với tất cả những điều trên, thì bạn chỉ nên tiêm phương pháp. Nếu không, tôi khuyên bạn nên gắn bó với truyền thống và tiêm giao diện.