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

科技回声

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

GitHubTwitter

首页

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

资源链接

HackerNews API原版 HackerNewsNext.js

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

Your DI framework is killing your code

56 点作者 mrrazz超过 9 年前

22 条评论

atemerev超过 9 年前
Yet another time it comes up. But now, after functional programming being commonplace, we at least know the answer to the &quot;why&quot; question.<p>Consider the &quot;classical OOP&quot; method signature:<p>email.send()<p>vs &quot;the anemic way&quot;<p>emailSender.send(message: Email)<p>If there is only one way to send email (SMTP), things works fine. But let&#x27;s assume we have to implement another way (e.g. Mailgun API). In the &quot;anemic&quot; case everything is simple: extract interface from EmailSender, add new implementation, all other method signatures remain unchanged. Refactorings are simple and can be done easily in any modern IDE. However, in &quot;classical OOP&quot; way, we have to do something like:<p>email.send(sender: EmailSender)<p>Which changes Email::send signature, which leads to breaking API changes and extensive manual refactoring of the entire codebase.<p>The &quot;anemic way&quot; had won because it is much more reusable and maintains API stability. Incidentally, this approach have smoothed the transition to functional languages — &quot;NounVerber&quot; objects became modules (collections of functions), and value objects became immutable data types.
评论 #10295874 未加载
评论 #10296112 未加载
评论 #10296500 未加载
评论 #10296566 未加载
评论 #10296691 未加载
评论 #10298855 未加载
评论 #10295829 未加载
评论 #10296616 未加载
deanCommie超过 9 年前
&quot;One of the properties of good OO design is that code that operates on data is located close to the data.&quot;<p>Says who?<p>I say the point of good OO design is to have very clear focused responsibilities for classes, very clear layers of abstraction, and concretely declared dependencies.<p>This way I know my business logic doesn&#x27;t sprawl, is discoverable, can be replaced modularly, and tested extensively and throughly.<p>This author sounds like he has no idea what it takes to actually build a maintainable code base of domain-driven business logic.<p>The fact that he actually thinks &quot;Order.SubmitForPicking(), UserAccount.UpdatePostalAddress(), and Basket.CalculatePriceIncludingTaxes()&quot; are examples of good OO design is LAUGHABLE. This approach couples your data layer with your business logic, negates the ability to use mocks, and makes refactoring WAY harder than it needs to be.
评论 #10296013 未加载
评论 #10298338 未加载
donatj超过 9 年前
My comments from the page, awaiting authorization:<p>&quot;See, what you are suggesting is actually a strong violation of the single responsibility principle as well as separation of concerns. This is exactly like the code I used to write. While it really is easier to manage when it&#x27;s small, it&#x27;s a naive approach that doesn&#x27;t scale well.<p>In a real life example of what you are suggesting, our User and Building classes for instance ended up over 10,000 lines long, doing things even just tangentially related to their parents. The real kickers were the cross business object ones who required more than one. Is it on Class X or Class Y? Sometimes it would end up on both accidentally causing the angels to cry.<p>About two years ago we started a massive rewrite, and now we have lots of small consice objects that perform very specific tasks. They&#x27;re logical, organized and dependency injected. I would never wish the reverse on anyone.&quot;
评论 #10296376 未加载
评论 #10297422 未加载
daxfohl超过 9 年前
This is just the anemic vs rich domain model debate, the standard retort being <a href="https:&#x2F;&#x2F;blog.inf.ed.ac.uk&#x2F;sapm&#x2F;2014&#x2F;02&#x2F;04&#x2F;the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design&#x2F;" rel="nofollow">https:&#x2F;&#x2F;blog.inf.ed.ac.uk&#x2F;sapm&#x2F;2014&#x2F;02&#x2F;04&#x2F;the-anaemic-domain...</a>.<p>TL;DR: rich domain model causes an explosion of coupling (your User class is now coupled to your database, your screen, your rendering engine, your printer, etc, when all it really is is some user data). And the rich domain model simply breaks down when you need functionality on two objects (should it be user.render(engine) or engine.render(user); either way one of your domain objects needs to know some &quot;private internals&quot; of another). With the anemic domain model these problems don&#x27;t exist.
评论 #10296687 未加载
评论 #10295823 未加载
efdee超过 9 年前
I can&#x27;t help but disagree with so many of the things he says.<p>I won&#x27;t even go into things like &quot;Does your customer know what an OrderPriceStrategyFactory is for? No, then it’s not a real thing. Its some bullshit you made up.&quot;, but statements like &quot;If we change how we contact customers then only the customer needs to change, not also the ReportBuilder&quot; sound overly simplistic to me. The whole point would be that nothing needs to be changed. You just write a new ReportBuilder that implements the same contract&#x2F;interface and switch the wiring. No changes in the existing ReportBuilder or Customer classes.<p>That, or maybe I&#x27;m just completely misunderstanding what I&#x27;m reading. Which isn&#x27;t entirely impossible :-)
评论 #10295892 未加载
评论 #10295831 未加载
valdiorn超过 9 年前
&gt; &quot;A Customer can CreateReport(), a Report can RenderAsEmail(), and an Email can Send()&quot;<p>Until you need to generate a different type of report for the customer, render a different format email, or send the email via another process. At that point, you have 3 options:<p>1. inherit and override. So you end up with<p>* SpecialReportCustomer : Customer,<p>* NewEmailStyleReport : Report,<p>* SendViaProxyEmail : Email<p>It should be pretty obvious that this won&#x27;t scale... at all.<p>2. Inject the desired sender or report generator into your object, depending on what implementation of the report builder or email sender you want. But why would the object have to know how to print itself, or send itself? Separation of concerns, people!<p>3. Break the interface of your class, make the methods accept a special reportBuilder or EmailSender... but why do that, when you can just do literally the opposite of what the author says, and have option 4:<p>4. Use an CustomerReportBuilder.Create(customer), or EmailSender.Send(email).<p>...and then you see why option #4 is your best choice, and why this article is objectively wrong in every way.
评论 #10296903 未加载
gwbas1c超过 9 年前
The author suggests a naive approach to object oriented programming. When all functionality is a method of the data it represents, these objects become hideously complicated and unmaintainable.<p>An example is comparing Objective C&#x27;s string class, to .Net&#x27;s. In objective C, path manipulation methods are on the string object itself; but in .Net, path manipulation methods are static methods in the Path class.<p>The .Net approach is better. Why? Do all strings represent paths? No. There&#x27;s an unlimited number of ways and reasons for manipulating strings; and making them all methods of the string class is unsustainable.
Asbostos超过 9 年前
How come OOP was supposed to be the best way to code and now we need dependency injection to make it practical? Does that mean that before DI, OOP was never being done properly? It was a bad idea all along and everybody thought it was just their poor OO skill that made them unable to design programs that met all the requirements?
jasonlfunk超过 9 年前
&quot;Almost certainly: if you’ve got value objects and noun-verbers, your design is rubbish.&quot;<p>If the code is readable, understandable, testable, and it does the job - who are you to say it&#x27;s rubbish?
评论 #10296331 未加载
msluyter超过 9 年前
A couple of miscellaneous thoughts:<p>1) The problem that I&#x27;ve noticed with the traditional OO designs that I&#x27;ve worked with is that bits of similar functionality tend to get scattered about the code base. For example, the author creates a CreateReport() method in the Customer class. But if later, we have, say, an Account class (not equivalent to a Customer) that also needs a report, we find ourselves with two likely similar looking methods. I&#x27;ve found this to become problematic as the codebase grows, and ultimately your code exhibits a massive DRY violation.<p>2) His complaint about singletons is interesting but I think irrelevant. In Spring, for example, everything tends to be a singleton (the default), and stateless, so indeed, your code <i>could</i> be replaced by static methods. Not that this would be a good idea, although in some cases it might make sense. (The Play framework does this: <a href="http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;5192904&#x2F;play-framework-uses-a-lot-of-statics" rel="nofollow">http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;5192904&#x2F;play-framework-us...</a>)<p>3) The author needs to read Yegge&#x27;s <a href="http:&#x2F;&#x2F;steve-yegge.blogspot.com&#x2F;2006&#x2F;03&#x2F;execution-in-kingdom-of-nouns.html" rel="nofollow">http:&#x2F;&#x2F;steve-yegge.blogspot.com&#x2F;2006&#x2F;03&#x2F;execution-in-kingdom...</a>.
kyllo超过 9 年前
<i>The other design smell with these noun-verbers is they’re almost always singletons. Oh you might not realise they’re singletons, because you’ve cleverly hidden that behind your dependency injection framework: but it’s still a singleton. If there’s no state on it, it might as well be a singleton. It’s a static method in all but name. Oh sure its more testable than if you’d actually used the word “static”. But it’s still a static method. If you’d not lied to yourself with your DI framework and written this as a static method, you’d recoil in horror. But because you’ve tarted it up and called it a “dependency”, you think it’s ok. Well it isn’t. It’s still crap code. What you’ve got is procedures, arbitrarily grouped into classes you laughably call “dependencies”. It sure as hell isn’t OO.</i><p>What&#x27;s so terrible about static methods? If you need to perform some sort of calculation that doesn&#x27;t mutate any state, isn&#x27;t a static method the correct choice, at least when you&#x27;re working in a language with no functions?<p>A procedure with no side effects is really just a function, is it not?
zaphar超过 9 年前
I&#x27;m not a big fan of DI frameworks but none of the arguments in this article ring true for me.<p>His proposals for how OO should work are half the reason I started shifting toward functional languages years ago. OO learning from Functional is a good not bad thing.
tassidevil超过 9 年前
I don’t think the author completely understood the DI frameworks. Just because a service in a DI framework is often a stateless singleton doesn’t mean they don’t allow stateful injection: EntityManager for JPA is a great example. Sure, there are other services with absolutely no state, but don’t blame the frameworks for promoting statelessness, it’s a practice promoted by many other software designs e.g. REST. I believe I have enough experience to say that state replications in a clustered environment does not scale and is not worth the pain.<p>Order.SubmitForPicking() sounds great on paper, that’s until you realize in order to submit an order for picking, you’ll need other value objects such as Customer, Account, Warehouse, Picker, Robot, Supervisor, Audit information …. the list goes on and on. How are you going to inject these info into an Order? And do they really belong in an order? I certainly don’t want to see all of these info in my JSON when I retrieve a single order on the client side.<p>The moment you move behaviors close to a value object is also the moment a plain object is no longer a plain object. Furthermore, a behavior laden value object becomes hard to evolve, extend and modify. There’s no clear boundary of where data ends and behavior begins, you certainly cannot package them up as a library and give it to your clients, you’ll have to create a separate set of value objects for that and start a maintenance nightmare.<p>It&#x27;s kinda sad that Java Code Geek rejected my comments (and many others&#x27;) and we have to come to Hacker News to express our views.
codeflo超过 9 年前
If I understand the argument correctly, this is a problem that some DI frameworks already address. And in others, there&#x27;s an easy workaround.<p>Say you have a class CustomerHandler (stupid example, pseudo-C#) with a constructor like this:<p><pre><code> public CustomerHandler(Guid customerId, ILogger logger, ISomeExtraDependency whatever) { ... } </code></pre> Now, let&#x27;s say another class needs to create CustomerHandlers for specific customers, but doesn&#x27;t want to care how they are created. Autofac, the .NET DI framework I&#x27;m most familiar with, let&#x27;s you inject factory delegates:<p><pre><code> public RequestProcessor(Func&lt;Guid, CustomerHandler) customerHandlerFactory) { ... } </code></pre> (Every type not mentioned in the Func&lt;&gt; is auto-injected.) This is very convenient because you can change the dependencies of CustomerHandler without messing up the rest of the code.<p>Of course, even in the absence of such an auto-factory feature, you can still create a dedicated CustomerHandlerFactory class and inject that. That&#x27;s not quite as convenient, but it&#x27;s still a lot better to have only one extra place that has to know CustomerHandler&#x27;s dependencies than having them all over your codebase.
estefan超过 9 年前
An article by someone who apparently doesn&#x27;t understand composition...
PaulHoule超过 9 年前
For some reason this winds up being worse in the .net world than the java world. The main trouble I see with spring is not that it messes up your code but that people use stackoverflow to &#x27;get things done&#x27; which leads to no overall strategy and thus mystery about how things get initialized.
nowprovision超过 9 年前
This talk might be of interest Stuart Sierra - Components Just Enough Structure <a href="https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=13cmHf_kt-Q" rel="nofollow">https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=13cmHf_kt-Q</a> - it is a tiny clojure framework but the ideas should be thought provoking, although granted with OO aligned type system not completely adaptable. In this example Customer could possibly just be a data-bucket&#x2F;map&#x2F;hashtable&#x2F;Dictionary, Im not entirely sure how I&#x27;d handle reporting though as the example has put me back in OO mode, but a customer report (which knows how to organize data about a customer) shouldnt be part of customer entity (used loosely) in my opinion.
choward超过 9 年前
I wonder if the author has ever worked on any relatively large projects. This has to be the worst way you can write your code. I know I wouldn&#x27;t like working on anything written in that way. That&#x27;s how you end up with 10,000 line files.
评论 #10300319 未加载
vbezhenar超过 9 年前
For me anemic model is much more natural and easy to use. Object-Oriented Programming is not a good approach to a general architecture. It has its place in some layers, e.g. GUI API usually can be successfully modeled with OOP, but otherwise just use modular procedural approach and use object-oriented constructs very conservatively, unless it fits naturally. That&#x27;s the best approach for me and it works flawlessly. Of course Functional Programming, Logic Programming has its place too, thanks to modern languages like Scala, where you don&#x27;t have to choose the only one paradigm.
dragonwriter超过 9 年前
This seems to be a complaint that some code doesn&#x27;t correspond to the author&#x27;s notions of &quot;good&quot; OO code, without a clear picture of what &quot;good&quot; OO code is except for a few vague references to fidelity to the domain without much discussion about what good domain modeling is.<p>For me, one of the clearest ways of analyzing a system in domain terms and working with it with business users to is with technology neutral specs in something like a Yourdon-style DFD, with a supporting data model and process descriptions.<p>It then becomes clear what the <i>relevant</i> nouns are in the domain: they fall in two basic groups:<p>(1) entities that correspond to named data items shown in flows or stores on the DFD (which will also, of course, be represented in the data model), and<p>(2) entities that correspond to named boxes and bubbles on the DFD, particularly processes, stores, and external interfaces.<p>A system whose implementation maps closely to this kind of requirements specification is a lot like an anemic data model system, with most of the classes being either immutable data objects or process objects (the latter of which may have &quot;NounVerber&quot; names if that convention is used), but that&#x27;s because the processor is a real noun in the domain -- in a non-automated implementation of the system, its a role that a human actor would take on in executing the business process.<p>You <i>do</i> end up with state stored in (or stored externally and managed by) objects with methods which mutate state, but these tend to be the processor objects and the stores, not the data objects that are transferred along flows.<p>It seems to me that the &quot;rich domain model&quot; approach is mostly a failure of domain modeling that apply ideas from OO&#x27;s origin in simulation modeling without recognizing that, in most other domains, the analogy to simulation modeling only works if you recognize that applications tend to be &quot;simulations&quot; of an idealized process executed by idealized technology-neutral actors acting on abstract data constructs, and that those idealized actors -- the processors -- are key elements of the domain model. Rich domain modeling ignores those and attributes their behavior somewhat arbitrarily to data objects, which end up giving data objects multiple, tangentially-related responsibilities (and provides lots of rooms for debating where certain responsibilities belong.)
verinus超过 9 年前
imho a very shallow understanding of oo. the arguments are only true for very simple models. when you have operations that manipulate multiple objects or have some other cross cutting concerns these models completely break down.<p>on the other hand: IoC containers try so solve a let-down of oo...we have to come up with something better- functional programming?
dvh超过 9 年前
My mind fused title &quot;Your DI framework&quot; into &quot;DIY framework&quot;