反对控制反转容器的争论
似乎每个人都在转向 IoC 容器。我已经尝试“理解”它有一段时间了,尽管我不想成为高速公路上走错路的司机,但它仍然没有通过我的常识测试。让我解释一下,如果我的论点有缺陷,请纠正/启发我:
我的理解:IoC 容器应该让您在组合不同组件时变得更轻松。这是通过 a) 构造函数注入、b) setter 注入和 c) 接口注入来完成的。然后以编程方式“连接”它们或将它们连接到容器读取的文件中。然后按名称召唤组件,然后在需要时手动投射。
我不明白的是:
编辑:(更好的措辞) 如果组件设计得当(使用 IoC 模式、松散耦合),您可以以(恕我直言)更清晰的方式“连接”应用程序,为什么还要使用不符合语言惯用的不透明容器呢?这个“托管代码”如何获得重要的功能? (我听说过一些关于生命周期管理的提及,但我不一定明白这比自己动手更好/更快。)
原始: 为什么要费尽心思将组件存储在容器中,以不符合语言惯用的方式“连接它们”,在按名称调用组件时使用相当于“转到标签”的东西,然后丢失许多组件?通过手动转换来了解静态类型语言的安全优势,当您通过不这样做而获得等效功能时,而是使用现代面向对象语言提供的所有很酷的抽象功能,例如对接口进行编程?我的意思是,实际需要使用手头组件的部分必须知道它们在任何情况下都在使用它,在这里您将使用最自然、最惯用的方式进行“接线”——编程!
Seems like everyone is moving towards IoC containers. I've tried to "grok" it for a while, and as much as I don't want to be the one driver to go the wrong way on the highway, it still doesn't pass the test of common sense to me. Let me explain, and please correct/enlighten me if my arguments are flawed:
My understanding: IoC containers are supposed to make your life easier when combining different components. This is done through either a) constructor injection, b) setter injection and c) interface injection. These are then "wired up" programmatically or in a file that's read by the container. Components then get summoned by name and then cast manually whenever needed.
What I don't get:
EDIT: (Better phrasing)
Why use an opaque container that's not idiomatic to the language, when you can "wire up" the application in (imho) a much clearer way if the components were properly designed (using IoC patterns, loose-coupling)? How does this "managed code" gain non-trivial functionality? (I've heard some mentions to life-cycle management, but I don't necessarily understand how this is any better/faster than do-it-yourself.)
ORIGINAL:
Why go to all the lengths of storing the components in a container, "wiring them up" in ways that aren't idiomatic to the language, using things equivalent to "goto labels" when you call up components by name, and then losing many of the safety benefits of a statically-typed language by manual casting, when you'd get the equivalent functionality by not doing it, and instead using all the cool features of abstraction given by modern OO languages, e.g. programming to an interface? I mean, the parts that actually need to use the component at hand have to know they are using it in any case, and here you'd be doing the "wiring" using the most natural, idiomatic way - programming!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当然人们认为 DI 容器没有任何好处,这个问题是正确的。如果纯粹从对象组合的角度来看,容器的好处似乎可以忽略不计。任何第三方都可以连接松散耦合的组件。
然而,一旦您超越了玩具场景,您应该意识到连接协作者的第三方必须承担的不仅仅是简单的组合责任。还可能存在退役问题,以防止资源泄漏。由于作曲家是唯一知道给定实例是共享还是私有的一方,因此它还必须承担生命周期管理的角色。
当您开始组合各种实例范围、使用共享服务和私有服务的组合,甚至可能将某些服务的范围限定到特定上下文(例如 Web 请求)时,事情就会变得复杂。当然可以用穷人的 DI 编写所有代码,但它不会增加任何业务价值 - 它是纯粹的基础设施。
这样的基础设施代码构成了一个通用子域,因此创建一个可重用的库来解决此类问题是很自然的。这正是 DI 容器。
顺便说一句,我知道的大多数容器不使用名称来连接自己 - 它们使用自动连接,它将构造函数注入的静态信息与容器从接口到具体类的映射配置相结合。简而言之,容器本身就理解这些模式。
DI 不需要 DI 容器 - 它非常有用。
更详细的处理可以在文章何时使用 DI 容器。
There are certainly people who think that DI Containers add no benefit, and the question is valid. If you look at it purely from an object composition angle, the benefit of a container may seem negligible. Any third party can connect loosely coupled components.
However, once you move beyond toy scenarios you should realize that the third party that connects collaborators must take on more that the simple responsibility of composition. There may also be decommissioning concerns to prevent resource leaks. As the composer is the only party that knows whether a given instance was shared or private, it must also take on the role of doing lifetime management.
When you start combining various instance scopes, using a combination of shared and private services, and perhaps even scoping some services to a particular context (such as a web request), things become complex. It's certainly possible to write all that code with poor man's DI, but it doesn't add any business value - it's pure infrastructure.
Such infrastructure code constitutes a Generic Subdomain, so it's very natural to create a reusable library to address such concerns. That's exactly what a DI Container is.
BTW, most containers I know don't use names to wire themselves - they use Auto-wiring, which combines the static information from Constructor Injection with the container's configuration of mappings from interfaces to concrete classes. In short, containers natively understand those patterns.
A DI Container is not required for DI - it's just damned helpful.
A more detailed treatment can be found in the article When to use a DI Container.
我确信关于这个主题有很多话要说,希望我会编辑这个答案以稍后添加更多内容(并希望更多的人会添加更多答案和见解),但只是对您的帖子提出几点快速说明。 使用 IoC 容器是控制反转的子集
,而不是全部。您可以使用控制反转作为设计构造,而无需依赖 IoC 容器框架。最简单的是,控制反转在此上下文中可以表述为“提供,不实例化”。只要您的对象在内部不依赖于其他对象的实现,而是要求向它们提供实例化的实现,那么您就在使用控制反转。即使您没有使用 IoC 容器框架。
对于您关于接口编程的观点...我不确定您对 IoC 容器的体验如何(我个人最喜欢的是 StructureMap),但你肯定是通过 IoC 编程的。整个想法,至少在我如何使用它方面,是将接口(类型)与实现(注入的类)分开。依赖于接口的代码仅针对这些接口进行编程,并且在需要时注入这些接口的实现。
例如,您可以拥有一个
IFooRepository
,它从类型为Foo
的数据存储实例返回。所有需要这些实例的代码都从提供的 IFooRepository 类型的对象中获取它们。在其他地方,您可以创建FooRepository
的实现,并配置 IoC 以在需要IFooRepository
的任何地方提供该实现。此实现可以从数据库、XML 文件、外部服务等获取它们。无论在哪里。这种控制已经被颠倒了。使用Foo
类型对象的代码并不关心它们来自哪里。明显的好处是您可以随时更换该实现。您可以将其替换为测试版本,根据环境更改版本等。但请记住,您也不需要在任何给定时间都具有如此 1:1 的接口与实现比例。
例如,我曾经在之前的工作中使用过一个代码生成工具,它将大量的 DAL 代码生成到一个类中。将其分解会很痛苦,但是将其配置为以特定的方法/属性名称将其全部吐出来并不那么痛苦。因此,我为我的存储库编写了一堆接口,并生成了一个类来实现所有这些接口。对于那个生成的类来说,它很丑陋。但我的应用程序的其余部分并不关心,因为它将每个接口视为自己的类型。 IoC 容器只是为每个容器提供相同的类。
我们能够快速启动并运行,没有人等待 DAL 开发。当我们继续研究使用接口的领域代码时,初级开发人员的任务是创建更好的实现。这些实现后来被换入,一切都很好。
正如我之前提到的,这一切都可以在没有 IoC 容器框架的情况下完成。事实上,模式本身很重要。
I'm sure there's a lot to be said on the subject, and hopefully I'll edit this answer to add more later (and hopefully more people will add more answers and insights), but just a couple quick points to your post...
Using an IoC container is a subset of inversion of control, not the whole thing. You can use inversion of control as a design construct without relying on an IoC container framework. At its simplest, inversion of control can be stated in this context as "supply, don't instantiate." As long as your objects aren't internally depending on implementations of other objects, and are instead requiring that instantiated implementations be supplied to them, then you're using inversion of control. Even if you're not using an IoC container framework.
To your point on programming to an interface... I'm not sure what your experience with IoC containers has been (my personal favorite is StructureMap), but you definitely program to an interface with IoC. The whole idea, at least in how I've used it, is that you separate your interfaces (your types) from your implementations (your injected classes). The code which relies on the interfaces is programmed only to those, and the implementations of those interfaces are injected when needed.
For example, you can have an
IFooRepository
which returns from a data store instances of typeFoo
. All of your code which needs those instances gets them from a supplied object of typeIFooRepository
. Elsewhere, you create an implementation ofFooRepository
and configure your IoC to supply that anywhere anIFooRepository
is needed. This implementation can get them from a database, from an XML file, from an external service, etc. Doesn't matter where. That control has been inverted. Your code which uses objects of typeFoo
doesn't care where they come from.The obvious benefit is that you can swap out that implementation any time you want. You can replace it with a test version, change versions based on environment, etc. But keep in mind that you also don't need to have such a 1-to-1 ratio of interfaces to implementations at any given time.
For example, I once used a code generating tool at a previous job which spit out tons and tons of DAL code into a single class. Breaking it apart would have been a pain, but what wasn't much of a pain was to configure it to spit it all out in specific method/property names. So I wrote a bunch of interfaces for my repositories and generated this one class which implemented all of them. For that generated class, it was ugly. But the rest of my application didn't care because it saw each interface as its own type. The IoC container just supplied that same class for each one.
We were able to get up and running quickly with this and nobody was waiting on the DAL development. While we continued to work in the domain code which used the interfaces, a junior dev was tasked with creating better implementations. Those implementations were later swapped in, all was well.
As I mentioned earlier, this can all be accomplished without an IoC container framework. It's the pattern itself that's important, really.
首先什么是IOC?这意味着创建依赖对象的责任不再由主对象承担,而是委托给第三方框架。我总是使用 Spring 作为我的 IOC 框架,它给桌面带来了很多好处。
促进接口编码和解耦 - 主要好处是 IOC 促进并使解耦变得非常容易。您始终可以在主对象中注入接口,然后使用接口方法来执行任务。主对象不需要知道哪个从属对象被分配给接口。当您想使用不同的类作为依赖项时,您所需要做的就是在配置文件中将旧类与新类交换,而无需更改一行代码。现在您可以争辩说,这可以使用各种接口设计模式在代码中完成。但 IOC 框架却很轻松。因此,即使作为新手,您也可以成为利用桥接器、工厂等各种接口设计模式的专家。
干净的代码 - 由于大多数对象创建和对象生命周期操作都委托给 IOC 容器,因此您可以省去了写肉鸡点重复代码的麻烦。这样你就有了更干净、更小、更容易理解的代码。
单元测试 - IOC 使单元测试变得简单。由于剩下的是解耦代码,您可以轻松地单独测试解耦代码。您还可以轻松地在测试用例中注入依赖项,并查看不同组件如何交互。
属性配置器 - 几乎所有应用程序都有一些属性文件,用于存储应用程序特定的静态属性。现在,要访问这些属性,开发人员需要编写包装器,该包装器将读取和解析属性文件,并以应用程序可以访问的格式存储属性。现在所有的 IOC 框架都提供了一种在特定类中注入静态属性/值的方法。因此,这又成为在公园散步。
这些是我立刻就能想到的一些观点,我相信还有更多。
First of all what is IOC? It means that responsibility of creating the dependent object is taken away from the main object and delegated to third party framework. I always use spring as my IOC framework and it bring tons of benefit to the table.
Promotes coding to interface and decoupling - The key benefit is that IOC promotes and makes decoupling very easy. You can always inject an interface in your main object and then use the interface methods to perform tasks. The main object does not need to know which dependent object is assigned to the interface. When you want to use a different class as dependency all you need is to swap the old class with a new one in the config file without a single line of code change. Now you can argue that this can be done in the code using various interface design patterns. But IOC framework makes its walk in a park. So even as a newbie you become expert in levering various interface design patterns like bridge, factory etc.
Clean code - As most of object creation and object life-cycle operations are delegated to IOC container you saved from the writing broiler point repetitive code. So you have a cleaner, smaller and more understandable code.
Unit testing - IOC makes unit testing easy. Since you are left with decoupled code you can easily test the decoupled code in isolation. Also you can easily inject dependencies in your test cases and see how different component interacts.
Property Configurators - Almost all the applications have some properties file where they store application specific static properties. Now to access those properties developers need to write wrappers which will read and parse the properties file and store the properties in format that application can access. Now all the IOC frameworks provide a way of injecting static properties/values in specific class. So this again becomes walk in the park.
These are some of the points I can think right away I am sure there are more.