C# 依赖注入和开发生产率
摘要 在过去的几个月里,我一直在用API抽象和实体/组件/脚本系统编程一个轻量级、基于C#的游戏引擎。它的整体思想是通过提供类似于Unity引擎的架构,简化XNA、SlimDX等游戏的开发过程 设计挑战 正如大多数游戏开发人员所知,在代码中需要访问许多不同的服务。许多开发人员求助于使用全局静态实例,例如渲染管理器(或编写器)、场景、图形设备(DX)、记录器、输入状态、视口、窗口等。有一些全局静态实例/单例的替代方法。一种是通过构造函数或构造函数/属性依赖项注入(DI)为每个类提供其需要访问的类的实例,另一种是使用全局服务定位器,如StructureMap的ObjectFactory,其中服务定位器通常配置为IoC容器 依赖注入 我选择走DI的路有很多原因。最明显的一个是可测试性,通过对接口编程,并通过构造函数将每个类的所有依赖项提供给它们,这些类很容易测试,因为测试容器可以实例化所需的服务或它们的模拟,并将其输入到要测试的每个类中。信不信由你,执行DI/IoC的另一个原因是为了提高代码的可读性。不再需要实例化所有不同服务和手动实例化引用所需服务的类的庞大初始化过程。配置内核(NInject)/注册表(StructureMap)可以方便地为引擎/游戏提供一个单一的配置点,在这里选择和配置服务实现 我的问题C# 依赖注入和开发生产率,c#,dependency-injection,architecture,ninject,structuremap,C#,Dependency Injection,Architecture,Ninject,Structuremap,摘要 在过去的几个月里,我一直在用API抽象和实体/组件/脚本系统编程一个轻量级、基于C#的游戏引擎。它的整体思想是通过提供类似于Unity引擎的架构,简化XNA、SlimDX等游戏的开发过程 设计挑战 正如大多数游戏开发人员所知,在代码中需要访问许多不同的服务。许多开发人员求助于使用全局静态实例,例如渲染管理器(或编写器)、场景、图形设备(DX)、记录器、输入状态、视口、窗口等。有一些全局静态实例/单例的替代方法。一种是通过构造函数或构造函数/属性依赖项注入(DI)为每个类提供其需要访问的类的
- 我经常觉得我是为了接口而创建接口的
- 我的生产力急剧下降,因为我所做的只是担心如何用DI的方式来做事情,而不是快速简单的全局静态方式李>
- 在某些情况下,例如在运行时实例化新实体时,需要访问IoC容器/内核来创建实例。这就产生了对IoC容器本身的依赖(SM中的ObjectFactory,Ninject中内核的实例),这实际上违背了首先使用IoC容器的理由。如何解决这个问题?抽象工厂浮现在脑海中,但这只会使代码更加复杂
- 根据服务需求,某些类的构造函数可能会变得非常大,这将使该类在其他未使用IoC的环境中完全无用李>
基本上,执行DI/IoC会大大降低我的工作效率,在某些情况下会使代码和体系结构更加复杂。因此,我不确定这是一条我应该走的道路,还是放弃,用老式的方式做事。我不是在寻找一个单一的答案来说明我应该做什么或不应该做什么,而是讨论从长远来看,使用DI是否值得,而不是使用全局静态/单一方式,在处理DI时,我忽略了可能的优点和缺点,以及上面列出的问题的可能解决方案 你应该回到传统的方式吗? 简言之,我的答案是否定的。基于你提到的所有原因,DI有许多好处 我经常觉得我是为了接口而创建接口的 如果您这样做,您可能违反了 根据服务需求,某些类的构造函数可以 非常大,这将使该类在其他方面完全无用 未使用国际奥委会的情况 如果您的类构造函数太大、太复杂,这是向您表明您违反了一个非常重要的其他原则的最佳方式: . 在本例中,是时候将代码提取并重构为不同的类了,建议的依赖项数量大约为4个 为了进行DI,您不必有接口,DI只是将依赖项放入对象的方式。创建接口可能是为测试目的替换依赖项所必需的方法。 除非依赖项的对象是:
new SomeBusinessObject(
new SomethingChangedNotificationService(new EmailErrorHandler()),
new EmailErrorHandler(),
new MyDao(new EmailErrorHandler()));
SomeBusinessObject bo = SomeBusinessObjectFactory.Create();
您可以创建一个具体的工厂,知道如何建造:
public static class SomeBusinessObjectFactory
{
public static SomeBusinessObject Create()
{
return new SomeBusinessObject(
new SomethingChangedNotificationService(new EmailErrorHandler()),
new EmailErrorHandler(),
new MyDao(new EmailErrorHandler()));
}
}
然后像这样使用它:
new SomeBusinessObject(
new SomethingChangedNotificationService(new EmailErrorHandler()),
new EmailErrorHandler(),
new MyDao(new EmailErrorHandler()));
SomeBusinessObject bo = SomeBusinessObjectFactory.Create();
您还可以使用poor mans di并创建一个
public interface IDataAccessFactory
{
TDao Create<TDao>();
}
public class ConcreteDataAccessFactory : IDataAccessFactory
{
private readonly IocContainer _Container;
public ConcreteDataAccessFactory(IocContainer container)
{
this._Container = container;
}
public TDao Create<TDao>()
{
return (TDao)Activator.CreateInstance(typeof(TDao),
this._Container.Resolve<Dependency1>(),
this._Container.Resolve<Dependency2>())
}
}
public class SomeOtherBusinessObject
{
private IDataAccessFactory _DataAccessFactory;
public SomeOtherBusinessObject(
IDataAccessFactory dataAccessFactory,
INotificationService notifcationService,
IErrorHandler errorHandler)
{
this._DataAccessFactory = dataAccessFactory;
}
public void DoSomething()
{
for (int i = 0; i < 10; i++)
{
using (var dao = this._DataAccessFactory.Create<MyDao>())
{
// work with dao
// Console.WriteLine(
// "Working with dao: " + dao.GetHashCode().ToString());
}
}
}
}