我是依赖注入模式的新手。我喜欢这个主意,但努力将其应用于我的案例。我有一个单例对象,我们称其为X,我经常在程序的许多部分,许多不同的类中使用它,有时甚至在调用堆栈的深处。通常,我会将其实现为全局可用的单例。如何在DI模式(特别是.NET Core DI容器)中实现此功能?我知道我需要将DI单独注册到DI容器中,但是如何访问它呢? DI将使用构造函数实例化类,这些构造函数将引用X,这很棒–但是,我需要在调用层次结构中的X深入,在我自己的对象(.NET Core或DI容器一无所知)中,在使用new而不是创建的对象中由DI容器实例化。
我想我的问题是–全局单例模式如何与DI模式对齐/实现/替换/避免使用DI模式?
答案 0 :(得分:0)
好吧,“ new
是胶水”(Link)。这意味着,如果您已经new
了一个实例,它将被粘贴到您的实现中。您不能轻易地将其与其他实现交换,例如用于测试的模拟。就像将乐高积木粘在一起。
我想使用适当的依赖项注入(无论是否使用容器/框架),所以您需要以不将组件粘合在一起而是注入它们的方式来构造程序。
然后,每个类基本上都处于层次结构1级。您需要记录器的实例吗?您注入它。您需要一个需要记录器的类的实例吗?您注入它。您想测试您的日志记录机制吗?容易,您只需注入符合您的记录器界面的内容即可登录到列表中,并且在测试结束时,您可以检查列表并查看是否存在所有必需的日志。这是可以自动化的(与使用常规日志记录机制并手动检查日志文件相反)。
这意味着最后,您实际上并没有一个层次结构,因为您刚刚注入的每个类都将注入其依赖项,而容器/框架或您的控制代码将决定实例化顺序的含义对象。
就设计模式而言,请允许我观察一下:即使现在,您也不需要 一个单例。现在在您的程序中,如果您有一个简单的全局变量,它将可以正常工作。但是我想您读过全局变量是“错误的”。设计模式是“好的”。而且,由于您需要一个全局变量,而单例传递一个全局变量,那么当可以使用“好”字时,为什么要使用“坏”字呢?嗯,问题是,即使是单例,全局变量也是错误的。这是模式的后退,您必须吞下一只蟾蜍才能使单例逻辑起作用。在您的情况下,您不需要单例逻辑,但是您喜欢蟾蜍的味道。因此,您创建了一个单例。不要使用设计模式来做到这一点。仔细阅读它们,并确保将它们用于预期目的,而不是因为您喜欢它们的副作用或使用设计模式感觉很好。
答案 1 :(得分:0)
只是一个想法,也许我需要你的想法:
public static class DependencyResolver
{
public static Func<IServiceProvider> GetServiceProvider;
}
然后在启动中:
public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider)
{
DependencyResolver.GetServiceProvider = () => { return serviceProvider; };
}
现在在任何契约类中:
DependencyResolver.GetServiceProvider().GetService<IService>();
答案 2 :(得分:0)
这里是一个简单的示例,说明了如何在没有单例情况下工作。 本示例假定您的项目是通过以下方式构建的:
所以您的简化代码如下:
// main
// ... (boilerplate)
container = new Container();
gui = new GuiCreator(container.getDatabase(), container.getLogger(), container.getOtherDependency());
gui.createAndRunGUI();
// ... (boilerplate)
// GuiCreator
public class GuiCreator {
private IDatabase db;
private ILogger log;
private IOtherDependency other;
public GuiCreator(IDatabase newdb, ILogger newlog, IOtherDependency newother) {
db = newdb;
log = newlog;
other = newother;
}
public void createAndRunGUI() {
// do stuff
}
}
您可以在Container类中实际定义将使用的实现,而GuiCreator构造函数则将接口作为参数。现在,假设您选择的ILogger实现本身具有依赖关系,该依赖关系由其构造函数作为参数的接口定义。容器知道了这一点,并通过将Logger实例化为new LoggerImplementation(getLoggerDependency());
来解决它。整个依赖链都这样。
所以本质上:
容器可以由某些第三方库/框架提供,也可以自己编写。通常,它将使用一些配置文件来确定哪些实现实际上应该用于各种接口。第三方容器通常会执行“自动装配”实现的注释所支持的某种代码分析,因此,如果您使用现成的工具,请确保已阅读该部分的工作原理,因为它通常会使您的生活更轻松道路。