多级DI重构

时间:2011-10-20 03:16:46

标签: c# dependency-injection

我不知道这是否真的符合DI,因为我不是在谈论注入抽象依赖的具体实现。我只是在谈论注入所需时间的东西。

在我的游戏引擎中,我想清理处理游戏状态的部分(菜单,舞台选择,游戏中,过场动画等)。所有这些都实现了一个通用的接口。但其中一个,在游戏中,也被特别引用为当前正在播放的水平。

现在我的项目中有43个引用到当前级别,通过游戏访问,单个。例如,Game.CurrentGame.CurrentMap.Something。引用在屏幕,实体,行为组件中,甚至在主窗体中(用于调试工具)。

我想通过注入所需的一切来摆脱这个参考。但是,CurrentMap本身并不是理想的依赖关系 - 其他东西正在它下面访问。所以我最初的计划是进入每个地方,找到实际使用的东西,并通过向该类构造函数添加一个参数来注入它。这引入了一个级别的另一个依赖,所以我重复这个过程,直到一切都完成。但问题是它会引入更多的构造函数参数,包括不直接使用依赖项的地方。许多类最终会接受一个依赖项,因此它们可以将它传递给它们下面的另一个对象。

什么是更清洁的选择呢?

3 个答案:

答案 0 :(得分:2)

您正在讨论的情况是,依赖关系必须通过几层对象来实现,才能获得真正需要的位置。这不需要发生。

如果某个对象本身创建了依赖项,那么您最终会遇到此问题。如果对象被工厂或DI容器(它只是一个奇特的工厂)所需的依赖关系给你,那么你就不会遇到这个问题。因此,为了避免这个问题,您需要确定每个类是关注游戏逻辑还是创建类。

假设你有对象a,它调用对象b,它调用对象c而对象c需要当前级别,而对象b需要不。

执行此操作的错误方法是从new C(level);内拨打b。正如您所指出的那样,b不需要知道关卡的级别,所以看起来情况越来越糟,而不是更好。你还没有进行依赖注入。不要在c中创建b,而只需在c的构造函数中询问b。现在,班级b只知道c,对level一无所知。

Misko比我在http://misko.hevery.com/2009/03/30/collaborator-vs-the-factory/

更好地解释了这一点

工厂中的代码如下所示:

Level level = new Level();
C c = new C( level );
B b = new B( c );
A a = new A( b );

B类只知道它的直接合作者(c)并且不依赖于Level。因为我们在工厂中创建东西,所以没有必要通过对象图向下传递对象图的叶子。

如果班级B有责任创建c,则需要了解如何创建班级c的所有内容。这是错的。

答案 1 :(得分:1)

使用DI框架,比如Castle Windsor,Structuremap或Unity(还有很多其他的东西也非常可靠)。

您所描述的问题不是这些框架解决的唯一的问题,而是它们几乎完全消除的摩擦类型的重要部分。

依赖关系是具体实现而不是更高级别的抽象这一事实是无关紧要的。看到这个问题(真的问你):

IOC/DI: Is Registering a Concrete Type a Code Smell?

答案 2 :(得分:0)

通常我会注入一个跟踪当前状态的服务:

public interface IGameStateTracker
{
    Game CurrentGame {get;}
    GameMap CurrentMap {get;}
}

您应该只需要在直接需要它的类的构造函数中注入此服务。我担心我不完全理解为什么你发现你需要将东西注入不直接需要它的类中:这可能表明你没有使用正确的DI模式。你能提供一些代码示例来展示这个问题的一个例子吗?

我是Phil Sandler关于使用DI框架的观点。它会让生活变得更轻松。但它首先不能弥补使用错误的DI模式。