Mar*_*ith 10 c# dependency-injection ioc-container inversion-of-control
在我的中型项目中,我使用静态类来存储库,服务等.它实际上工作得非常好,即使大多数程序员都期望相反.我的代码库非常紧凑,干净且易于理解.现在我尝试重写所有内容并使用IoC(Invertion of Control),我非常失望.我必须手动初始化每个类,控制器等中的十几个依赖项,为接口添加更多项目等等.我真的没有看到我的项目有任何好处,似乎它导致的问题多于解决问题.我在IoC/中发现了以下缺点DI:
我们不测试整个代码库,只测试某些方法并使用真实数据库.那么,如果不需要模拟测试,是否应该避免依赖注入?
Dav*_*d L 19
您的大部分担忧似乎归结为误用或误解.
更大的代码化
这通常是适当尊重单一责任原则和界面隔离原则的结果.它是否大得多?我怀疑没有你声称的那么大.但是,它正在做的事情很可能是将类分解为特定的功能,而不是拥有可以执行任何操作的"全能"类.在大多数情况下,这是关注点健康分离的标志,而不是问题.
馄饨代码而不是意大利面条代码
再次,这很可能导致您在堆栈中思考而不是难以看到的依赖关系.我认为这是一个很大的好处,因为它导致适当的抽象和封装.
性能较慢只需使用快速容器即可.我最喜欢的是SimpleInjector和LightInject.
需要初始化构造函数中的所有依赖项,即使我要调用的方法只有一个依赖项
再一次,这表明您违反了单一责任原则.这是一件好事,因为它迫使你在逻辑上思考你的架构,而不是添加willy-nilly.
在没有使用IDE时更难理解某些错误被推送到运行时
如果你仍然没有使用IDE,那么你会感到羞耻.现代机器对它没有好的争论.此外,如果您愿意,某些容器(SimpleInjector)将在首次运行时进行验证.您可以通过简单的单元测试轻松检测到这一点.
添加额外的依赖(DI框架本身)
你必须选择你的战斗.如果学习一个新框架的成本小于保持意大利面条代码的成本(我怀疑这将是),那么成本是合理的.
新员工必须首先学习DI才能使用它
如果我们回避新的模式,我们永远不会成长.我认为这是一个丰富和发展团队的机会,而不是一种伤害他们的方式.此外,权衡是学习意大利面条代码,这可能比采用行业范围的模式困难得多.
很多样板代码对创意人员不利(例如从构造函数复制实例到属性......)
这是完全错误的.应始终通过构造函数传递强制依赖项.只应通过属性设置可选的依赖项,并且只应在非常特定的情况下进行,因为它通常违反单一责任原则.
我们不测试整个代码库,只测试某些方法并使用真实数据库.那么,如果不需要模拟测试,是否应该避免依赖注入?
我认为这可能是对所有人最大的误解.依赖注入不仅仅是为了使测试更容易.这样你就可以浏览一下类构造函数的签名,并立即知道使该类打勾所需的内容.静态类是不可能的,因为类可以在没有押韵或理由的情况下随时调用堆栈上下.您的目标应该是为代码添加一致性,清晰度和区别.这是使用DI的最大原因,这也是我强烈建议您重温它的原因.
Ste*_*ven 10
虽然IoC/DI并不是适用于所有情况的银弹,但您可能没有正确应用它.依赖注入背后的原则需要时间来掌握,或者至少它确实对我有用.如果应用得当,它可以带来(以及其他)以下好处:
从您的问题,我已经可以提取一些可能在您的情况下出错的事情:
我必须在每个类中手动初始化十几个依赖项
这意味着您创建的每个类都负责创建它所需的依赖项.这是一种称为Control Freak的反模式.一个类new本身不应该依赖它的依赖.您甚至可以通过调用容器(或表示容器的抽象)来获取特定依赖项,从而应用Service Locator反模式,其中您的类请求其依赖项.类应该只定义它作为构造函数参数所需的依赖项.
十几个依赖
该陈述暗示您违反了单一责任原则.这实际上没有与IoC/DI耦合,您的旧代码可能已经违反了单一责任原则,导致其变得难以理解和维护其他开发人员.原作者通常很难理解为什么其他人很难维护代码,因为你写的东西经常很适合你.通常,违反SRP会导致其他人无法理解和维护代码.违反SRP的测试类通常更难.一个类最多应该有六个依赖项.
为界面添加更多项目等
这意味着您违反了重用抽象原则.通常,应用程序中的大多数组件/类应该包含在十几个抽象中.例如,实现某些用例的所有类可能都需要一个(通用)抽象.实现查询的类也应该有一个抽象.对于我编写的系统,80%到95%的组件(包含应用程序行为的类)由5到12个(主要是通用的)抽象覆盖.大多数情况下,您不需要仅为接口创建新项目.大多数时候,我将这些接口放在同一个项目的根目录中.
更大的代码化
您编写的代码量最初不会有很大差异.然而,依赖注入的实践仅在应用SOLID时才有效,并且SOLID会促进小型焦点类.有一个责任的班级.这意味着您将拥有许多易于理解且易于组成灵活系统的小类.不要忘记:我们不应该努力编写更少的代码,而是更易于维护的代码.
然而,凭借良好的SOLID设计和正确的抽象,我实际上不得不编写比以前少得多的代码.例如,通过在应用程序的基础结构层中编写几行代码,而不是将其分散到整个应用程序中,可以应用某些跨领域问题(如日志记录,审计跟踪,授权等).应用.它甚至让我能够做以前不可行的事情,因为它们迫使我在整个代码库中做出彻底的改变,这是非常耗时的,管理层不允许我这样做.
当没有使用IDE时,馄饨代码而不是意大利面代码更难以理解
这是真的.依赖注入促使类彼此分离.这有时会使浏览代码库变得更加困难,因为类通常依赖于抽象而不是具体的类.在过去,我发现DI给我的灵活性超过了到目前为止找到实现的成本.使用Visual Studio 2015,我可以简单地使用CTRL + F12来查找接口的实现.如果只有一个实现,Visual Studio将直接跳转到该实现.
性能较慢
这不是真的.性能不必与仅使用静态方法调用的代码库进行任何不同.然而,您选择让您的课程具有瞬态生活方式,这意味着您可以在整个地方创建新实例.我在去年的应用我创造了我所有的类每个应用程序只有一次,这大致给出了相同的性能,因为只有具有静态方法调用,但随着应用程序的好处是非常灵活的,可维护.但请注意,即使您决定new为每个(Web)请求完成对象图,性能成本也很可能比您在此期间执行的任何I/O(数据库,文件系统和Web服务调用)低几个数量级.请求,即使是最慢的DI容器.
一些错误被推送到运行时添加额外的依赖(DI框架本身)
这些问题都意味着使用DI库.DI库在运行时进行对象组合.但是,在执行依赖注入时,DI库不是必需的工具.在没有工具的情况下使用依赖注入可以使小应用程序受益; 一种叫做Pure DI的做法.您的应用程序可能无法从使用DI容器中受益,但大多数应用程序实际上都可以从使用依赖注入(正确使用时)中获益.Againt:工具是可选的,编写可维护的代码不是.
但即使您使用DI库,也有内置工具的库允许您验证和诊断配置.它们不会为您提供编译时支持,但它们允许您在应用程序启动或使用单元测试时运行此分析.这可以防止您对整个应用程序进行回归,以验证您的容器是否正确连接.我的建议是选择一个DI容器来帮助您检测这些配置错误.
新员工必须首先学习DI才能使用它
这是真的,但依赖注入本身实际上并不难学.实际上很难学到的是正确应用SOLID原则,当您想要编写需要由多个开发人员维护一段时间的应用程序时,无论如何都需要学习这一点.我宁愿投资教我的团队中的开发人员编写SOLID代码而不是让他们编写代码; 这肯定会导致以后的维护.
很多样板代码
当我们查看用C#6编写的代码时,有一些样板代码,但实际上并不是那么糟糕,特别是当你考虑它给出的优点时.C#的未来版本将删除样板文件,这主要是由必须定义构造函数引起的,这些构造函数接受空值检查并分配给私有变量的参数.当引入记录类型和非可空引用类型时,C#7或8肯定会解决这个问题.
这对创意人士不利
对不起,但这个论点简直就是胡说八道.我已经看到这个论点一遍又一遍地被用作编写不良代码的借口,开发人员不想学习设计模式和软件原理和实践.创造性并不是编写其他人无法理解的代码或无法测试的代码的借口.我们需要应用可接受的模式和实践,并且在该边界内有足够的空间来创造性,同时编写好的代码.编写代码不是一门艺术; 这是一种手艺.
就像我说的那样,DI并不适用于所有情况,并且围绕它的实践需要时间来掌握.我建议你阅读Mark Seemann撰写的" .NET中的依赖注入 "一书; 它会给出很多答案,并会让你很好地理解如何以及何时应用它,何时不能.
| 归档时间: |
|
| 查看次数: |
4078 次 |
| 最近记录: |