使用温莎时应在哪里登记物品
我的应用程序中将包含以下组件
我希望使用Castle Windsor作为IoC将各层粘合在一起,但我对粘合的设计有点不确定。
我的问题是谁应该负责将物体注册到温莎? 我有几个想法;
有人能帮助我解决一些想法和利弊吗? 以这种方式链接利用温莎城堡的示例项目将非常有帮助。
一般而言,应用程序中的所有组件应尽可能晚地组成,因为这确保了最大的模块化,并且模块尽可能松散耦合。
实际上,这意味着您应该在应用程序的根目录下配置容器。
容器只是将模块组合到工作应用程序中的引擎。 原则上,您可以手动编写代码(这称为Poor Man's DI),但使用像Windsor这样的DI容器要容易得多。
这样的组合根最好是应用程序根目录中唯一的代码片断,使应用程序成为所谓的Humble Executable(一个来自优秀的xUnit测试模式的术语),它本身不需要进行单元测试。
你的测试根本不需要容器,因为你的对象和模块应该是可组合的,你可以直接从单元测试中为它们提供测试双精度。 如果您可以将所有模块设计为容器不可知,那么最好。
特别是在Windsor中,您应该在安装程序(实现IWindsorInstaller
类型)中封装组件注册逻辑。有关更多详细信息,请参阅文档
尽管Mark的答案对网络场景来说很好,但将其应用于所有体系结构(即富客户端 - 即:WPF,WinForms,iOS等)的主要缺陷是可以/应该创建操作所需的所有组件立刻。
对于Web服务器来说,这是有道理的,因为每个请求都是非常短暂的,并且ASP.NET MVC控制器是由底层框架(无用户代码)为每个请求进入创建的。因此,控制器及其所有依赖关系可以很容易地编写通过DI框架,这样做的维护成本非常低。 请注意,Web框架负责管理控制器的生命周期,并为所有目的管理其所有依赖项的生命周期(DI框架将在控制器创建时为您创建/注入)。 在请求的持续时间内,依赖关系保持活跃并且您的用户代码不需要管理组件和子组件自身的生命周期。 另外请注意,Web服务器在不同的请求中是无状态的(会话状态除外,但与本次讨论无关),并且您从不拥有需要同时处理单个请求的多个控制器/子控制器实例。
但是,在富客户端应用程序中,情况并非如此。 如果使用MVC / MVVM体系结构(您应该!),用户的会话是长期存在的,并且控制器会在用户浏览应用程序时创建子控制器/同级控制器(请参阅关于底部MVVM的注意事项)。 与网络世界类比的是,富客户端应用程序中的每个用户输入(按钮点击,操作执行)都等同于Web框架接收到的请求。 然而,最大的区别在于,您希望富客户端应用程序中的控制器在操作之间保持活跃状态(很可能用户在同一屏幕上执行多个操作(由特定控制器控制),并且子控制器也可以当用户执行不同的操作时(例如,如果用户导航到标签控件时懒惰地创建了标签,或者用户在屏幕上执行特定操作时只需要加载一个UI)就可以创建和销毁。
这两个特征意味着它是需要管理控制器/子控制器的生命周期的用户代码,并且控制器的依赖性不应该全部被预先创建 (即:子控制器,视图模型,其他表示组件等等)。 )。 如果您使用DI框架来执行这些职责,您最终不仅会得到更多不属于它的代码(请参阅:构造函数注入反模式),而且您还需要传递一个依赖项容器大部分表示层,以便您的组件可以在需要时使用它来创建其子组件。
为什么我的用户代码可以访问DI容器?
1)依赖容器持有对应用程序中很多组件的引用。 将这个坏孩子传递给需要创建/管理anoter子组件的每个组件,相当于在您的体系结构中使用全局变量。 更糟糕的是,任何子组件也可以在容器中尽快注册新组件,它也将成为全球存储。 为了传递组件之间的数据(在兄弟控制器之间或在深度控制器层次之间 - 即:祖先控制器需要从祖父控制器获取数据),开发人员将对象放入容器中。 请注意,在容器未传递给用户代码的Web世界中,这绝不是问题。
2)依赖容器与服务定位器/工厂/直接对象实例化的另一个问题是,从容器中解析使得它完全不明确,无论是创建组件还是简单地重用现有组件。 相反,它是由一个集中配置(即:bootstrapper / Composition Root)来确定组件的生命周期。 在某些情况下,这是可以的(即:Web控制器,它不是需要管理组件生命周期的用户代码,而是运行时请求处理框架本身)。 这是非常成问题的,但是当你的组件的设计应该显示它是否有责任管理一个组件以及它的生命周期应该是什么时(例如:一个电话应用弹出一张询问用户一些信息的表单,这是通过控制器创建一个控制叠加表的子控制器,一旦用户输入了一些信息,表单就会被放弃,并且控制权返回到初始控制器,控制器仍然保持着用户之前所做的状态)。 如果使用DI来解决工作表子控制器,它不明确它应该是什么时间或应该由谁来负责管理它(启动控制器)。 将其与使用其他机制所规定的明确责任进行比较。
情景A:
// not sure whether I'm responsible for creating the thing or not
DependencyContainer.GimmeA<Thing>()
情景B:
// responsibility is clear that this component is responsible for creation
Factory.CreateMeA<Thing>()
// or simply
new Thing()
情景C:
// responsibility is clear that this component is not responsible for creation, but rather only consumption
ServiceLocator.GetMeTheExisting<Thing>()
// or simply
ServiceLocator.Thing
正如你所看到的,DI不清楚是谁负责子组件的生命周期管理。
注意:从技术上说,许多DI框架确实有一些懒惰地创建组件的方法(请参阅:如何不执行依赖注入 - 静态或单例容器),这比传递容器好得多,但您仍然支付改变你的代码以遍布各处的创建函数,你在创建过程中缺乏对有效构造函数参数的一级支持,并且在一天结束时,你仍然在不必要的地方使用间接机制,而这些地方唯一的好处是实现可测试性,这可以通过更好,更简单的方式实现(见下文)。
这是什么意思呢?
这意味着DI适用于某些情况,对其他人不适用。 在富客户端应用程序中,它恰好带来了很多缺点,只有很少的优势。 随着维护成本的增加,您的应用越复杂,规模越大。 它还具有滥用的严重可能性,这取决于团队沟通和代码审查流程的严密程度,可以是从非问题到严重的技术债务成本的任何地方。 有一个神话,服务定位器或工厂或旧的实例化在某种程度上是坏的和过时的机制,因为它们可能不是Web应用程序领域的最佳机制,也许很多人都参与其中。我们不应该过度使用,将这些学习概括为所有场景,并将所有内容视为钉子,因为我们已经学会了使用特定的锤子。
我对RICH-CLIENT APPS的建议是使用最小的机制,以满足手头每个组件的要求。 80%的时间,这应该是直接瞬间。 服务定位器可以用来容纳你的主要业务层组件(例如:本质上通常是单身的应用服务),当然工厂甚至Singleton模式也有它们的位置。 没有什么可说的,你不能使用隐藏在你的服务定位器后面的DI框架去创建你的业务层依赖关系和他们依赖的所有东西 - 如果这样做最终会让你的生活变得更容易,那层不会“ t展示了富客户端表示层压倒性地做的懒惰加载 。 只要确保屏蔽你的用户代码访问该容器,以便防止通过DI容器的混乱可以创建。
可测试性呢?
没有DI框架,绝对可以实现可测性。 我建议使用一个拦截框架,如UnitBox(免费)或TypeMock(昂贵)。 这些框架为您提供了解决手头问题所需的工具(您如何模拟C#中的实例化和静态调用),并且不要求您更改整个体系结构以避开它们(不幸的是,趋势所在的位置去了.NET / Java世界)。 找到手头问题的解决方案更为明智,并且使用最佳的底层组件的自然语言机制和模式,然后尝试将每个方形钉固定到圆形DI孔中。 一旦你开始使用这些更简单,更具体的机制,你会发现你的代码库中几乎不需要DI,如果有的话。
注意:对于MVVM体系结构
在基本的MVVM体系结构中,视图模型有效地承担了控制器的责任,因此出于所有目的,将上面的'控制器'措辞应用于'视图模型'。 基本MVVM对小应用程序工作正常,但随着应用程序复杂性的增长,您可能需要使用MVCVM方法。 视图模型变成大多数愚蠢的DTO,以促进与视图的数据绑定,同时与业务层交互,并且代表屏幕/子屏幕的视图模型组之间被封装成明确的控制器/子控制器组件。 在任何架构中,控制器的职责都存在,并呈现出上述相同的特征。
链接地址: http://www.djcxy.com/p/77777.html上一篇: Where should objects be registered when using Windsor
下一篇: Where should I do Injection with Ninject 2+ (and how do I arrange my Modules?)