IoC 优于我的工厂单例
本文关键字:工厂 单例 我的 于我的 IoC | 更新日期: 2023-09-27 18:21:52
在使用单例方面似乎存在污名。我个人从未购买过它,但为了思想开放,我试图尝试 IoC 概念作为替代方案,因为坦率地说,我对日常工作感到厌倦,想尝试一些不同的东西。如果我对 IoC 概念的解释不正确或被误导,请原谅我。
情况是这样的:我正在Windows服务中构建一个简单的基于HttpListener
的Web服务器,该服务器利用插件模型来确定应如何根据请求的URL处理请求(就像其他询问HttpListener
的其他人一样(。我发现插件的方法是在配置的目录中查询用HttpModuleAssemblyAttribute
修饰的程序集。这些程序集可以包含 0 个或多个IHttpModule
子程序集,此外,这些子程序集还使用用于指定模块名称、版本、人类可读说明和各种其他信息的HttpModuleAttribute
进行修饰。像这样:
[HttpModule(/*Some property values that matter */)]
public class SimpleHttpModule : IHttpModule
{
public void Execute(HttpListenerContext context)
{
/* Do Something Special */
}
}
当发现HttpModule
时,我通常会将其添加到Dictionary<string, Type>
对象中,该对象的唯一目的是跟踪我们知道哪些模块。这本词典通常存在于我的各种单例中,它具有ACE风格单例的角色(这是我C++岁时了解单例的遗产(。
现在我正在尝试实现的是使用(我对(一般 IoC 概念的类似内容。基本上我有一个AppService
集合,其中IAppService
定义为:
public interface IAppService : IDisposable
{
void Initialize();
}
我的插件AppService
看起来像这样:
[AppService("Plugins")]
internal class PluginAppService : IAppService, IDictionary<string, Type>
{
/* Common IDictionary Implementation consisting of something like: */
internal Type Item(string modName)
{
Type modType;
if (!this.TryGetValue(modName, out modType)
return null;
return modType;
}
internal void Initialize()
{
// Find internal and external plug-ins and add them to myself
}
// IDisposable clean up method that attempts to dispose all known plug-ins
}
然后在服务期间OnStart
我实例化了一个本地已知但传递给所有实例化插件的构造函数的 AppServices
实例:
public class AppServices : IDisposable, IDictionary<string, IAppService>
{
/* Simple implementation of IDictionary */
public void Initialization()
{
// Find internal IAppService implementations, instantiate them (passing this as a constructor parameter), initialize them and add them to this.
// Somewhere in there would be something like
Add(appSvcName, appSvc);
}
}
我们曾经的单一方法实现变成了子级上的抽象实现+构造函数:
[HttpModule(/*Some property values that matter */)]
public abstract class HttpModule : IHttpModule
{
protected AppServices appServices = null;
public HttpModule(AppServices services)
{
appServices = services;
}
public abstract void Execute(HttpListenerContext context);
}
[HttpModule(/*Some property values that matter */)]
public class SimpleHttpModule : HttpModule
{
public SimpleHttpModule(AppServices services) : base(services) { }
public override void Execute(HttpListenerContext context)
{
/* Do Something Special */
}
}
对常用应用程序服务的任何访问都变为:
var plugType = appServices["Plugins"][plugName];
而不是:
var plugType = PluginManager.Instance[plugName];
我在这里是否缺少一些基本的 IoC 概念来简化这一切,或者所有这些附加代码真的有好处吗?在我的世界里,单例是简单的生物,它允许整个程序中的代码访问所需的(相对静态的(信息(在这种情况下是类型(。
更明确地提出问题:
- 这是转换为 IoC/DI 概念的工厂单例的有效实现吗?
- 如果是,所需的额外代码和强加看似更笨拙的 API 的回报/好处在哪里?
IoC 是一个通用术语。 依赖注入是当今更受欢迎的术语。
依赖注入在几种情况下确实大放异彩。 首先,它定义了比具有硬编码依赖项实例化的解决方案更可测试的体系结构。 单例很难进行单元测试,因为它们是静态的,静态数据不能"卸载"。
其次,依赖关系注入不仅实例化所需的类型,还实例化所有依赖类型。 因此,如果类 A 需要类 B,而类 B 需要类 C 和 D,那么一个好的 DI 框架将自动创建所有依赖项,并控制它们的生存期(例如,使它们在单个 Web 请求的生存期内存活(。
DI容器可以作为可以实例化任何类型的对象的泛型工厂(只要它配置正确并满足 DI 框架的要求(。 因此,您不必编写自定义工厂。
与任何通用解决方案一样,它旨在为 90% 的用例提供所需的内容。 当然,每次需要集合时,您都可以创建一个手工制作的自定义链表数据结构,但是 90=% 的时间通用的链表可以正常工作。 DI 和自定义工厂也是如此。
当你开始编写单元测试时,IoC变得更加有趣。 很抱歉回答有更多问题的问题,但是...对于您的两个实现,单元测试是什么样的? 您是否能够在不从磁盘查找程序集的情况下对使用该PluginManager
的类进行单元测试?
编辑
仅仅因为您可以使用单例实现相同的功能并不意味着它易于维护。 通过使用 IoC(至少这种带有构造函数的样式(,您可以显式声明对象具有的依赖项。 通过使用单例,该信息隐藏在类中。 这也使得用替代实现替换这些依赖项变得更加困难。
因此,使用单例PluginManager
很难使用模拟插件测试HTTP服务器,而是从磁盘上的某个位置查找它们。 使用 IoC 版本,您可以传递IAppService
的替代版本,该版本仅从预填充的Dictionary
中查找插件。
虽然我仍然不太相信 IoC/DI 在这种情况下会更好,但随着项目范围的扩大,我肯定看到了好处。对于日志记录和可配置性之类的事情,它肯定是正确的方法。
我期待在未来的项目中更多地尝试它。