许多小型组件的特定缺点?

时间:2009-07-28 05:00:58

标签: .net dependency-injection ninject

我正在计划将依赖注入引入当前大型整体库中的一些工作,试图使库更容易进行单元测试,更容易理解,并且可能更灵活地作为奖励。

我决定使用NInject,我真的很喜欢Nate的'做一件事,做得好'(转述)的座右铭,而且在DI的背景下似乎特别好。

我现在想知道的是,我是否应该将当前单个大型装配拆分为具有不相交特征集的多个较小装配。其中一些较小的程序集将具有相互依赖性,但远非所有这些程序集,因为代码的体系结构已经非常松散地耦合。

请注意,这些功能集不是微不足道的,也不是很小......它包含客户端/服务器通信,序列化,自定义集合类型,文件IO抽象,常见例程库,线程库,标准日志记录等等。

我看到前一个问题:What is better, many small assemblies, or one big assembly?有点解决了这个问题,但是看起来更精细的粒度,这让我想知道那里的答案是否仍适用于这种情况?

此外,在接近这一主题的各种问题中,一个常见的答案是,“太多”集会导致了未指明的“痛苦”和“问题”。我真的想要具体了解这种方法可能存在的缺点。

我同意在仅需要1之前添加8个程序集是“有点痛苦”,但是必须为每个应用程序包含一个大的单片库也不是完全理想的...加上8个程序集是一些东西你只做过一次,所以我对这个论点很少有同情心(即使我最初可能会和其他人一起抱怨)。

附录:
到目前为止,我没有看到反对较小的组件的任何令人遗憾的理由,所以我想我现在将继续,好像这是一个非问题。如果有人能够用可验证的事实来考虑好的可靠理由来支持它们,我仍然会非常有兴趣了解它们。 (我会尽快增加赏金以提高知名度)

编辑:将效果分析和结果移到单独的答案中(见下文)。

7 个答案:

答案 0 :(得分:28)

答案 1 :(得分:14)

我将给你一个真实世界的例子,其中许多(非常)小组件的使用产生了.Net DLL Hell。

在工作中,我们有一个长大的本土框架(.Net 1.1)。除了通常的框架类型管道代码(包括日志记录,工作流,排队等)之外,还有各种封装的数据库访问实体,类型化数据集和一些其他业务逻辑代码。我不是为了这个框架的初始开发和后续维护,但确实继承了它的用法。正如我所提到的,整个框架导致了许多小DLL。而且,当我说很多时,我们说的是100以上 - 而不是你所提到的可管理的8。更复杂的是,这些程序集都经过严格签名,版本化并出现在GAC中。

因此,快进几年和以后的一些维护周期,发生的事情是DLL和它们支持的应用程序的相互依赖性造成了严重破坏。在每台生产机器上都是machine.config文件中的一个巨大的程序集重定向部分,它确保无论请求何种程序集,都可以通过Fusion加载“正确”的程序集。这是因为重建每个依赖于已修改或升级的依赖框架和应用程序程序集所遇到的困难。采取了很大的痛苦(通常)以确保在修改组件时不会对组件进行任何重大更改。重建了程序集,并在machine.config中创建了一个新的或更新的条目。

以下是我会停下来听一声巨大的集体呻吟和喘息的声音!

这个特殊场景是不做的事情的典型代表。确实在这种情况下,你会陷入完全无法维持的境地。我记得当我第一次开始使用它时,花了两天的时间让我的机器设置用于开发这个框架 - 解决我的GAC和运行时环境的GAC,machine.config程序集重定向之间的差异,编译时的版本冲突由于不正确的引用,或者更可能是由于直接引用组件A和组件B引起的版本冲突,但是组件B引用了组件A,但是与我的应用程序的直接引用不同。你明白了。

这个特定场景的真正问题是程序集内容过于细化。而且,这最终导致了相互依赖的纠缠网络。我的想法是,最初的架构师认为这将创建一个高度可维护的代码系统 - 只需要重建对系统组件的非常小的更改。事实上,情况正好相反。此外,对于已经发布的其他一些答案,当你达到这个数量的组件时,装载大量的组件确实会导致性能损失 - 绝对是在解决期间,我猜,虽然我没有经验证据,但是在某些边缘情况下,运行时可能会受到影响,特别是在反射可能起作用的地方 - 在这一点上可能是错误的。

你认为我会被嘲笑,但我相信程序集有逻辑物理分离 - 当我在这里说“程序集”时,我假设每个DLL有一个程序集。这一切归结为互相依赖。如果我有一个取决于装配件B的装配件A,我总是问自己是否需要在装配件A中引用装配件B.或者,这种分离是否有益处。查看如何引用程序集通常也是一个很好的指标。如果您要在程序集A,B,C,D和E中划分大型库。如果您在90%的时间内引用了程序集A,那么您总是必须引用程序集B和C,因为A依赖于它们,那么组合A,B和C可能更好的结合,除非有一个非常有说服力的论据允许它们保持分离。 Enterprise Library就是这样的经典示例,您几乎总是需要引用3个程序集才能使用库的单个方面 - 但在Enterprise Library的情况下,能够构建核心功能和代码之上重用是它的架构的原因。

看建筑是另一个很好的指导方针。如果你有一个很好的干净堆叠架构,你的程序集依赖关系是堆栈的形式,比如说“垂直”,而不是“web”,当你在每个方向都有依赖关系时会开始形成,然后分离程序集功能边界是有道理的。否则,请将内容整合到一起或寻找重新构建。

无论哪种方式,祝你好运!

答案 2 :(得分:4)

加载每个程序集会有轻微的性能损失(如果它们已签名则更多),因此这是在同一程序集中将常用事物聚集在一起的一个原因。一旦加载了东西,我不相信会有很大的开销(尽管可能存在一些静态优化的东西,JIT在跨越装配边界时可能会有更难的时间执行)。

我尝试采用的方法是:命名空间用于逻辑组织。程序集用于对应该在物理上一起使用的类/命名空间进行分组。 IE浏览器。如果您不希望ClassA而不是ClassB(反之亦然),则它们属于同一个程序集。

答案 3 :(得分:2)

单片怪物会重复使用部分代码,以便以后的工作比以前更昂贵。并且导致不需要耦合的类之间的耦合(通常是显式的)导致维护成本更高,因为测试和纠错将更加困难。

有许多项目的缺点是它(至少在VS中)与少数项目相比需要花费相当长的时间进行编译。

答案 4 :(得分:2)

装配组织中最重要的因素应该是依赖图,在类和汇编级别。

程序集不应该有循环引用。这应该是非常明显的开始。

彼此依赖程度最高的类应该在一个程序集中。

如果一个A类依赖于B类,而B可能不直接依赖于A,那么它不可能在A之外被使用,那么它们应该共享一个程序集。

您还可以使用程序集来强制分离关注点 - 将您的GUI代码放在一个程序集中,而您的业务逻辑驻留在另一个程序集中将提供与GUI无关的业务逻辑的某种程度的执行。

基于代码运行位置的程序集分离是另一个要考虑的问题 - 可执行文件之间的公共代码应该(通常)在一个公共程序集中,而不是让一个.exe直接引用另一个。

也许可以使用程序集的一个更重要的事情是区分公共API和内部使用的对象以使公共API能够工作。通过将API放在单独的程序集中,您可以强制其API的不透明性。

答案 5 :(得分:1)

我想如果你只谈了十几个,你应该没问题。我正在开发一个包含100多个程序集的应用程序,而且它非常非常痛苦。

如果你没有办法管理依赖项 - 知道如果你修改程序集X会破坏什么,那你就麻烦了。

我遇到的一个'好'问题是当程序集A引用程序集B和C时,B引用程序集D的V1,而C引用程序集D的V2。('Twisted diamond'对于它来说是个很好的名字。那)

如果你想拥有一个自动构建,你将会很乐意维护构建脚本(需要以相反的顺序构建依赖项),或者有“一个解决方案来统治它们”,这将是如果你有很多程序集,几乎不可能在Visual Studio中使用。

修改 我认为你的问题的答案在很大程度上取决于你的程序集的语义。不同的应用程序是否可能共享程序集?您是否希望能够单独更新两个应用程序的程序集?您打算使用GAC吗?或者复制可执行文件旁边的程序集?

答案 6 :(得分:0)

就个人而言,我喜欢整体方法。

但有时你无法帮助创建更多的程序集。当您需要通用接口程序集时,.NET远程处理通常负责这一点。

我不确定装载组件的开销是多么“沉重”。 (也许有人可以启发我们)