依赖关系注入 - 使用静态依赖关系字段而不是注入到每个对象
本文关键字:依赖 注入 关系 对象 字段 静态 | 更新日期: 2023-09-27 17:56:40
将依赖对象作为静态类中的静态字段而不是通过构造函数将其注入到依赖于它的每个对象有什么问题?
public static class Dependencies
{
public static IUsersRepository Users;
...
}
//Use in a method that depends on Users Repository
var users = Dependencies.Users.GetUsers();
与。
public class UserController
{
private IUsersRepository _users;
public UserController(IUsersRepository repo)
{
this._users = repo;
}
public List<User> GetCustomUsers()
{
var users = this._users.GetUsers();
...
}
}
称为"环境上下文"的 DI 模式来执行此操作。
它允许您避免一直传递横切关注点,但它仍然允许您对事物进行单元测试。
规范示例是日期时间提供程序:
public abstract class TimeProvider {
private static TimeProvider current =
DefaultTimeProvider.Instance;
public static TimeProvider Current {
get { return TimeProvider.current; }
set {
if (value == null) {
throw new ArgumentNullException("value");
}
TimeProvider.current = value;
}
}
public abstract DateTime UtcNow { get; }
public static void ResetToDefault() {
TimeProvider.current = DefaultTimeProvider.Instance;
}
}
实现可能如下所示:
public class DefaultTimeProvider : TimeProvider {
private readonly static DefaultTimeProvider instance =
new DefaultTimeProvider();
private DefaultTimeProvider() { }
public override DateTime UtcNow {
get { return DateTime.UtcNow; }
}
public static DefaultTimeProvider Instance {
get { return DefaultTimeProvider.instance; }
}
}
代码将使用TimeProvider.Current
来访问日期时间,而不是直接使用DateTime
。
默认的具体实现返回通常的 DateTime.UtcNow。但是,对于单元测试,您可以使用特殊的测试实现并在运行单元测试之前对其进行TimeProvider.Current
设置。
有关详细信息,请参阅此页面(代码的来源)。
请注意,应仅将此模式用于真正的横切关注点,例如日期时间、安全性、日志记录等。
假设一个UserController
想要使用即时IUsersRepository
,而另一个UserController
想要使用不同IUserRepository
实现的实例,那么你不能使用静态依赖项来做到这一点。
老实说,如果你这样做,警察不会来敲你的门,但考虑到这是合乎逻辑的结论(即任何可观大小的应用程序),你最终会得到一个更难维护的"意大利面条代码"代码库。
大多数情况下,耦合和诸如SOLID原则之类的东西。您正在与Dependency
类紧密耦合,理想情况下,DI 通过为您构建对象图、注入依赖项来防止这种情况,因此这些对象没有知识(即不耦合)到提供它们的实现。如果您使用的是 DI 容器和单例生活方式,那么您基本上已经得到了您所描述的静态字段。但是有了容器(甚至是越来越流行的"无容器"风格的容器),您可以获得更大的灵活性,并且为您完成困难的事情。
在某些情况下,使用 DI(尤其是通过容器)可能是一个坏主意(日志记录、生成新的 Guid 值、获取当前日期)。您可以使用"环境上下文"解决方案解决这几种情况(有关更多详细信息,请参阅 Matthew Watson 的回答)。