Fre*_*son 48 c# castle-windsor inversion-of-control
我的应用程序中将包含以下组件
我希望使用Castle Windsor作为IoC来粘合各层,但我对胶合的设计有点不确定.
我的问题是谁应该负责将物品注册到温莎?我有几个想法;
有人可以用不同的途径帮助我提出一些想法和利弊吗?以这种方式利用Castle Windsor的示例项目的链接将非常有用.
Mar*_*ann 76
通常,应用程序中的所有组件应尽可能晚地组合,因为这可确保最大程度的模块化,并且模块尽可能松散地耦合.
实际上,这意味着您应该在应用程序的根目录配置容器.
容器只是将模块组合成工作应用程序的引擎.原则上,您可以手动编写代码(这称为穷人的DI),但使用像Windsor这样的DI容器要容易得多.
理想情况下,这样的组合根将是应用程序根目录中唯一的代码段,使应用程序成为所谓的Humble Executable(来自优秀的xUnit测试模式的术语),它本身不需要单元测试.
您的测试根本不需要容器,因为您的对象和模块应该是可组合的,并且您可以从单元测试中直接向它们提供测试双打.最好是将所有模块设计为与容器无关.
特别是在Windsor中,您应该将组件注册逻辑封装在安装程序中(实现类型IWindsorInstaller)有关更多详细信息,请参阅文档
Mar*_*chy 28
虽然Mark的答案非常适合Web场景,但将其应用于所有架构(即富客户端 - 即:WPF,WinForms,iOS等)的关键缺陷是假设可以/应该创建操作所需的所有组件立刻.
对于Web服务器,这是有道理的,因为每个请求都是非常短暂的,并且ASP.NET MVC控制器由底层框架(无用户代码)为每个请求进入.因此控制器及其所有依赖项可以轻松组合通过DI框架,这样做的维护成本非常低.请注意,Web框架负责管理控制器的生命周期,并为所有目的管理所有依赖项的生命周期(DI框架将在创建控制器时为您创建/注入).在请求期间,依赖项是完全正常的,并且您的用户代码不需要管理组件和子组件本身的生命周期.另请注意,Web服务器在不同的请求中是无状态的(会话状态除外,但这与此讨论无关),并且您永远不会有多个控制器/子控制器实例需要同时存在以服务单个请求.
但在富客户端应用程序中,情况并非如此.如果使用MVC/MVVM架构(你应该这样做!),用户的会话是长寿的,当用户浏览应用程序时,控制器会创建子控制器/兄弟控制器(请参阅底部有关MVVM的说明).与Web世界类似的是,富客户端应用程序中的每个用户输入(按钮单击,执行操作)等同于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>()
Run Code Online (Sandbox Code Playgroud)
情景B:
// responsibility is clear that this component is responsible for creation
Factory.CreateMeA<Thing>()
// or simply
new Thing()
Run Code Online (Sandbox Code Playgroud)
场景C:
// responsibility is clear that this component is not responsible for creation, but rather only consumption
ServiceLocator.GetMeTheExisting<Thing>()
// or simply
ServiceLocator.Thing
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,DI使得不清楚谁负责子组件的生命周期管理.
注意: 从技术上讲,许多DI框架确实有一些方法可以懒惰地创建组件(请参阅:如何不执行依赖注入 - 静态或单例容器)这比传递容器要好得多,但是您仍然需要支付成本改变你的代码以在任何地方传递创建函数,你缺乏在创建过程中传递有效构造函数参数的第一级支持,并且在一天结束时你仍然在不必要地使用间接机制的地方,唯一的好处是实现可测试性,可以用更好,更简单的方式实现(见下文).
这是什么意思呢?
这意味着DI适用于某些场景,不适合其他场景.在富客户端应用程序中,它恰好带有很多DI的缺点,只有很少的优势.您的应用程序越复杂,维护成本就会越大.它还具有滥用的巨大潜力,这取决于您的团队沟通和代码审查流程的紧密程度,可以是从非发行到严重的技术债务成本的任何地方.有一个神话围绕着服务定位器或工厂或旧的实例化在某种程度上是坏的和过时的机制只是因为它们可能不是Web应用程序世界中的最佳机制,也许很多人都参与其中.我们不应该 - 将这些学习概括为所有场景,并将所有内容视为指甲,因为我们已经学会使用特定的锤子.
我对RICH-CLIENT APPS的建议是使用满足手头每个组件要求的最小机制.80%的时间这应该是直接即时.服务定位器可用于存放您的主要业务层组件(即:应用程序服务,通常是单件式的),当然,工厂甚至Singleton模式也有其自己的位置.没有什么可以说你不能使用隐藏在你的服务定位器后面的DI框架来创建你的业务层依赖项和它们一次性依赖的所有东西 - 如果最终使你的生活变得更容易,那层就不会展示了富客户端表示层压倒性地做的延迟加载.只需确保屏蔽您的用户代码以防止访问该容器,以便您可以防止通过DI容器的混乱可以创建.
可测试性怎么样?
没有DI框架,绝对可以实现可测试性.我建议使用拦截框架,如UnitBox(免费)或TypeMock(昂贵).这些框架为您提供了解决手头问题所需的工具(如何在C#中模拟实例化和静态调用)并且不需要您更改整个架构来绕过它们(不幸的是,趋势已经到来)进入.NET/Java世界).找到手头问题的解决方案并使用最适合底层组件的自然语言机制和模式然后尝试将每个方形钉子放入圆形DI孔中是明智的.一旦开始使用这些更简单,更具体的机制,您会注意到代码库中的DI几乎没有任何需要.
注意:对于MVVM体系结构
在基本的MVVM架构中,视图模型有效地承担了控制器的责任,因此出于所有目的,请考虑上面的"控制器"措辞以应用于"视图模型".基本MVVM适用于小型应用程序,但随着应用程序复杂性的增加,您可能希望使用MVCVM方法.视图模型变成大多数愚蠢的DTO以促进与视图的数据绑定,同时与业务层的交互以及表示屏幕/子屏幕的视图模型组之间被封装到显式控制器/子控制器组件中.在任一架构中,控制器的责任都存在并且具有与上述相同的特征.
| 归档时间: |
|
| 查看次数: |
11929 次 |
| 最近记录: |