TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Dependency Injection is Evil (2011)

75 点作者 paralelogram大约 10 年前

23 条评论

spion大约 10 年前
What is evil in our profession is that everyone professes some technique or another that has worked for them, without mentioning any specifics about the exact situation in which that technique worked. Infact most programers go out of their ways to avoid mentioning specific experiences, confident that their grand designs generalize across all projects, platforms and languages. Just look up the recent TDD debate with Bob Martin and DHH and see if anyone mentioned one specific project &#x2F; example.<p>As a result we end up with long diatribes of barely useful &quot;best practice&quot; advice that is spread by means of cargo-culting and that causes problems down the line.<p>What we need to collectively do is get off our high horses and start talking about specific projects, situations and problems. Start asking ourselves: Why did our solution work? What was the specific problem and situation? Which properties of the problem made our solution the right one?
awinder大约 10 年前
From the post:<p><pre><code> I do not use mock objects when building my application, and I do not see the sense in using mock objects when testing. If I am going to deliver a real object to my customer then I want to test that real object and not a reasonable facsimile This is because it would be so easy to put code in the mock object that passes a particular test, but when the same conditions are encountered in the real object in the customer&#x27;s application the results are something else entirely. You should be testing the code that you will be delivering to your customers, not the code which exists only in the test suite. </code></pre> I don&#x27;t think the argument for unit testing is that you should ONLY do unit testing. By stubbing&#x2F;mocking and removing dependent systems from unit tests, you&#x27;re usually left with:<p><pre><code> 1. Tests that run very well in isolation 2. Tests that run very quickly 3. Tests that are very stable (as long as the code isn&#x27;t changing, results shouldn&#x27;t change from run to run) </code></pre> All of these are great qualities for tests that will help you, the developer, continue to safely refactor code as your application ages. Functional tests where you&#x27;re not stubbing &#x2F; mocking will help you ensure your application continues to function as expected from a user interface perspective. Both are valuable to different audiences and for different purposes :-).
评论 #9363397 未加载
评论 #9364738 未加载
评论 #9364273 未加载
benvan大约 10 年前
I would argue that &quot;dependency injection&quot; at its purist is little more than the side-effect of designing with intent to minimise module responsibility.<p>When writing a class, as a rule of thumb I&#x27;d posit there&#x27;s a general advantage to outsourcing units of behaviour or complexity to other modules. Those other modules could be constructed by the class itself, but this is likely to make the current class concerned with the construction details of those modules. One of the simplest things to do, for example, is to ask for those modules to be provided to the class on construction.<p>This sounds like a great idea until somebody comes along and calls it &quot;dependency injection&quot; and a bunch of us lose our minds.
评论 #9363166 未加载
评论 #9363320 未加载
_pmf_大约 10 年前
I&#x27;m not sure whether this whole article is just an elaborate troll or if the author has never worked on anything but the greenest of greenfield applications.<p>DI is not a pattern.<p>The argument that DI breaks encapsulation does not make any sense at all; in fact, it&#x27;s an argument in favor of DI, since it cleanly allows configurable behavior (the alternative requiring some way of configuring the host object to behave in a certain way using constructor arguments or setters).<p>Dependency injection is a direct application of coding to interfaces instead of implementations (or to abstractions). In the absence of polymorphic constructions, the only way to achieve this is to either inject the dependency or a factory for the dependency.
pornel大约 10 年前
The article is overly dramatic about DI. Parts of it sound like a mere &quot;get off my lawn!&quot; rant against OOP in general (godwinning just few paragraphs in).<p>There&#x27;s praise for the Singleton antipattern, variables with an &quot;obj&quot; prefix as if objects were an odd thing to watch out for. There&#x27;s an example of polymorphism achieved by setting a global variable before including a script. PHP (v4!) written like that would be icky even in 2011.<p>There is some good advice in there too, but overall design it advocates for seems to reduce OOP to mere namespacing of global variables and functions. I&#x27;m assuming the author doesn&#x27;t use unit tests either (given that TDD is &quot;poppycock!&quot;). It&#x27;s not surprising that DI doesn&#x27;t fit that way of building programs, but I don&#x27;t think that makes DI evil. It&#x27;s just a tool for a different job.
nicksellen大约 10 年前
The conclusion is a lot more moderate than the title, the author actually uses dependency injection:<p><i>While DI has benefits in some circumstances, such as shown in the Copy program, I do not believe in the idea that it automatically provides benefits in all circumstances. This is why I consider that the application of DI in the wrong circumstances should be considered as being evil and not a universal panacea or silver bullet. In fact, in the wrong circumstances it could even be said that Dependency Injection breaks encapsulation. Although I have found some places in my framework where I have made use of DI because of the obvious benefits it provides, there are some places where I could use DI but have chosen not to do so.</i><p>I think using the word <i>evil</i> is far too dramatic, evil should mean <i>do not ever use</i>.
评论 #9363411 未加载
评论 #9363339 未加载
_throwmaybe_大约 10 年前
There is a good reason to force people to use dependency injection. At least you&#x27;re sure that their code will use an interface to describe a dependency which is a huge advantage compared to letting everybody write their own code as they want.
评论 #9363240 未加载
KyeRussell大约 10 年前
&gt; Those statements imply that if you are not using Dependency Injection (DI) then you are not doing OO &quot;properly&quot;, that you are an idiot. I take great exception to that arrogant, condescending attitude.<p>Stuff like this makes me question if this guy has some deeper issues he needs to work out before he can write blog posts about design patterns. The entire thing reeks of clickbait and narcissism.
hoodoof大约 10 年前
Really thoughtful, clearly articulated, backed with facts and diagrams, whining.
elchief大约 10 年前
Mr. Marsten is the type of writer &#x2F; programmer I dislike.<p>Brevity is the soul of wit.<p>Here&#x27;s a summary:<p>Dependency injection&#x27;s primary benefit is to aid unit testing.
评论 #9364754 未加载
ane大约 10 年前
&quot;If you know you will never change the implementation or configuration of some dependency, there is no benefit in using dependency injection.&quot;.<p>It all boils down to this.<p>Here&#x27;s the catch, though: with dependencies, it is really, <i>really</i> hard to know whether dependency implementations change or not. What is more, in most languages, implementing dependency injection is so trivial that it is always worth it. Conversely, the work associated with changing implementations that aren&#x27;t built with some form of loose coupling, that is, designing around DI, is in most cases non-trivial.
评论 #9363562 未加载
PaulHoule大约 10 年前
I dunno.<p>I write a lot of scripty programs that make subjective decisions that, when they work well enough, go into production.<p>Consistent use of DI means that I never check my AWS keys into my source code. In particular I can do experiments that change out any module without having to touch the source code and that is pretty important.
barrkel大约 10 年前
I have some sympathy for the idea that DI is harmful to good software design, but this article isn&#x27;t an argument for it.<p>My specific issue is that DI, and a number of other things, including single-implementation interfaces and mocks in testing, are normally used as a means to an end: testable fragments of code. Individually testable fragments of code, taken to its logical conclusion, converts every function into a class, possibly implementing an interface, and taking dependencies (i.e. the other methods it calls) as instance arguments, either directly to the method, or as arguments to the constructor (in a kind of OO partial application).<p>You then end up with an atomized library of classes with names like ThingDoer and methods like doTheThing(). All the methods are now testable in isolation, since you can mock all the dependencies, and there&#x27;s no risk of any pesky static references reaching out and pulling in stuff you can&#x27;t easily mock.<p>Splitting everything up so aggressively means that somebody now needs to put all the pieces back together. Some automated help (DI, IoC) is used. That&#x27;s where the DI comes in.<p>Some of the problems created by this style:<p>* Cognitive overload: turning every dependency into a pluggable modularization point greatly inflates the number of concepts required to understand the code, especially from outside a library, because all the subcomponent parts all too often end up in the same namespace as the outer coordinating parts.<p>* Far harder to understand without debug stepping: runtime composition of code and extra levels of indirection impede IDE code navigation - go to definition on a method, and you find out it&#x27;s actually just on an interface, then you have to look up the class hierarchy, find the concrete implementation - only one if you&#x27;re lucky - before you can trace things through.<p>* Over-modularization &#x2F; over-abstraction: since the code is split up into so many tiny bits, there&#x27;s an illusion that reuse or modification of the code is possible by simply adding an extra implementation of one of the single-implementation interfaces. But extensibility needs to be designed in; pervasive, mandatory abstraction boundaries are unlikely to be good fits for ad-hoc future extension.<p>* Brittle tests: because module boundaries go all the way down, and are individually tested, a refactoring that modifies the implementation of a library is made far more painful. Slightly chunkier tests - not quite integration, but unit-testing at the library level, the semantics that library clients actually care about - go a long way to reduce this. But once you go in this direction, the whole reason for the edifice&#x27;s existence - individually unit-testable atoms of code - is called into question.<p>This is also my problem with mainstream Java code style.<p>My preferred style is to write support libraries that are individually testable at a slightly higher level, or are functional-style static methods that are generic and wholly testable with simple stubs, and write the main business code such that it uses the libraries in a fashion that&#x27;s they&#x27;re close to obviously correct as possible. Isolate any complicated logic into a testable functional static method, or a testable general (but not necessarily complete) library. Then integration-test this higher-level business logic.<p>A common problem I see with many junior Java devs is that they write effectively procedural code split into method-per-class classes, and they zip together business logic and more complex implementation logic alongside one another. Rather than building abstractions that make their business logic simple and free of complex implementation, you end up with a procedural call tree that has a gestalt - the complex implementation - spread across and intermixed with business logic, and all of it tied together via indirected runtime composition, because testing.<p>That&#x27;s fairly abstract, so I&#x27;ll make it concrete. Consider a spreadsheet report generator over data coming from entities in a database. Using a spreadsheet library (e.g. Apache POI) is typically quite thorny because it needs to try and support all the features, so you end up with complex logic dealing with each master row, then other methods that have complex logic dealing with each detail row. Code that has detailed knowledge about the business domain is intermixed with code that has detailed knowledge about the spreadsheet library&#x27;s model. Let&#x27;s not even talk about tests.<p>An alternative approach - and a refactoring I made - was to create a reporting-oriented write-only facade for the spreadsheet manipulation. The business logic was then conflated from multiple complex classes to a single simple class that had straightforward code using the spreadsheet writer.
评论 #9363447 未加载
评论 #9363279 未加载
评论 #9363739 未加载
评论 #9363209 未加载
pohl大约 10 年前
I suspect that — late at night, tucked in for a slumber after toiling all day in the processor — injected dependencies dream they are first class functions, their service interfaces dream they are function signatures, and the objects into which they are injected dream of being curried functions to which some dependency arguments have already been partially applied.
评论 #9363846 未加载
V-2大约 10 年前
<p><pre><code> I do not use mock objects when building my application, and I do not see the sense in using mock objects when testing. If I am going to deliver a real object to my customer then I want to test that real object and not a reasonable facsimile </code></pre> This reveals a fundamental misunderstanding of the purpose of mocking.<p>If I test object A (and mock B for this purpose), I&#x27;m testing A - not the mock.<p>Another test verifies behavior of concrete object B (while mocking A).<p>Mocks themselves are not what is being tested.<p>Of course it is possible that both A and B behave correctly in isolation (measured against expectations defined by unit tests), but they don&#x27;t integrate well.<p>In other words, expectations, assumptions verified by both unit test suites do not cover everything that&#x27;s actually required for the components to form a functional system - there is a &quot;gap&quot;.<p>This is however not a flaw of Dependency Injection, but a natural shortcoming of unit tests as such. Obviously they are not a replacement for integration tests - but that works both ways. Different beasts.<p>The advantage that unit tests have over integration tests is that they make it much easier to pinpoint sources of failures.
BobTheCoder大约 10 年前
- DI doesn&#x27;t add a lot of overhead. You don&#x27;t really explain where it is EVIL, just that it is NOT STRICTLY NECESSARY ALL THE TIME.<p>- It handles complex cases such as dependency X should be a singleton, or inserting a layer of caching in front of dependency Y.<p>- It handles cases where dependency Z requires runtime logic to determine which implementation to use. You can do things like in PerformanceCriticalPackage use the HighPerformanceLogger and use some other logger else where. Want to switch these around, only need to touch the DI wiring logic.<p>- I find it keeps modules that use other modules &quot;cleaner&quot; without having any kind of dependency construction logic in them.<p>&gt; Design patterns are an option, not a requirement<p>Using in DI is a design pattern and I agree all usage of design patterns need to be justified. However not using DI and manually creating dependencies is ALSO a design pattern! You need to justify using either, or something different.<p>Of the available options, using DI is generally the safest bet, especially if you want consistency throughout an application, since it&#x27;s likely you will want this power somewhere.
shadowmint大约 10 年前
&gt; I do not believe in the idea that it automatically provides benefits in all circumstances<p>Where &#x27;it&#x27; is dependency injection, or, hey, anything else.<p>How about we have an interesting conversation instead of rethrashing a stupid one (here is my straw man: &#x27;you use DI for everything without thinking.&#x27; Watch as a bash it...)?<p>Why do dynamic languages like python, ruby and javascript typically not use dependency injection?<p>You could argue that some of them (python) don&#x27;t have a great unit testing background, but modern projects like node do, and you still don&#x27;t see the DI pattern thrown around a lot.<p>In fact, you tend to only see it in languages that implement a native interface-based polymorphism, like java, c++ and c#. Even folks using go (<a href="http:&#x2F;&#x2F;openmymind.net&#x2F;Dependency-Injection-In-Go&#x2F;" rel="nofollow">http:&#x2F;&#x2F;openmymind.net&#x2F;Dependency-Injection-In-Go&#x2F;</a>). Maybe rust too, but since the single ownership makes singletons an anti-pattern in rust, maybe not (at least I haven&#x27;t seen it).<p>Could it be you can get by just fine for all your testing needs without DI?
评论 #9363694 未加载
评论 #9363900 未加载
dicroce大约 10 年前
DI is a good and useful thing, but like a lot of fads in software development it is being overused in some places...<p>Actually what DI brings to mind for me is functional programming. By injecting your dependencies (as opposed to simply constructing them) you are moving your code just slightly in the functional direction. This brings many well known benefits, but also its own costs.
kazinator大约 10 年前
Let&#x27;s look at Dependency Injection using a functional analogy.<p>Imagine you wrote a function &quot;add1&quot; which adds 1 to every element of a list and returns a new list. This is inferior because it has a hard-coded dependency on the specific function object (lambda (arg) (+ 1 arg)).<p>The dependency inversion way is to write a function &quot;map&quot; which is &quot;configured&quot; by taking a functional argument. Then you call (map (lambda (arg) (+ 1 arg)) ...) if you want to add 1 to every element, but of course you can use other functions.<p>Now if we actually read the source code of this map, it is confusing compared to the code of add1. &quot;What does this do? It calls the function that is passed in, but that could be anything! Why can&#x27;t this damn thing just call a specific function? All this program ever needs is to add 1 to a list; why build this whole useless pattern?&quot;
twa927大约 10 年前
I think things like dependency injection popularity can be explained by looking at communities created around programming languages. Particularly, the levels of abstraction a given community encourages is largely constant.<p>Java is probably the leader in the amounts and levels of abstraction it&#x27;s community uses. J2EE&#x27;s EJB are a thing of the past, nowadays it&#x27;s IC. And PHP for a long time is battling it&#x27;s inferiority complex by imitating &quot;serious&quot; Java.<p>I&#x27;m not saying that using a lot and deep abstractions is necessarily bad. You can write programs with the same functionality using different levels of it. What I&#x27;m saying that discussions like that are more &quot;cultural wars&quot; and there&#x27;s no rational proof what&#x27;s better.
oldmanjay大约 10 年前
this article is awful. insecurity theater, lots of wiki copy-paste, and a fundamental misunderstanding of testing object oriented systems.<p>maybe this was meant to be humorous?
keredson大约 10 年前
Yes it is. One of the many reasons I&#x27;ve migrated away from the Java world. (There are no non-spring&#x2F;non-hibernate jobs left!)<p>Years ago I wrote a &quot;get me the hell out of spring&quot; tool: <a href="https:&#x2F;&#x2F;code.google.com&#x2F;p&#x2F;unsprung&#x2F;" rel="nofollow">https:&#x2F;&#x2F;code.google.com&#x2F;p&#x2F;unsprung&#x2F;</a>
评论 #9364574 未加载
jdpanderson大约 10 年前
A colleague and I had a discussion about this particular author. The conclusion was that he&#x27;s an older programmer that doesn&#x27;t like when the technology changes&#x2F;evolves. Anything new is EVIL, and sometimes the people evolving the technology get slandered in the process. This is visible in this and many of his other articles.<p>Edit: shouldn&#x27;t have used the word &quot;older&quot;. It has nothing to do with it. He&#x27;s a programmer that doesn&#x27;t like when technology changes&#x2F;evolves.
评论 #9363248 未加载
评论 #9363181 未加载