问题
I've been reading about dependency injection, and I understand the basic concept that a method should receive what it needs from its caller rather than creating such items itself. As a result, new
operators get removed from the method almost entirely (certain basic objects would be exempt, of course - one example of this I found was for things like StringBuilder
s which seem like they'd be insane to have to pass in).
My question sounds deceptively simple, but I suspect the answer is actually fairly complex: Where do all of the new
operators go?
The answer seems straightforward at first: the new
operator just gets pushed to the method which calls the method that needs the object. The problem with this, however, is that the calling method is likely also under test and so that new
gets pushed up from calling method to calling method until eventually you get to a root method (which at this point seems crazy untestable) that creates an obscene amount of objects to get used at various points down the call stack. The situation becomes even more complicated when you consider that that root method is the root of a large variety of other methods, and so would need to create objects for every possibility. This also creates a performance problem since you would quickly be left with a large number of objects which are never actually used, but which must be instantiated anyway "just in case".
It is quite obvious to me that I have missed some vital piece of knowledge which is so apparent to other developers that nobody thinks to describe it in a blog post. However, I obviously cannot know what I do not know, so I humbly ask that I be let in on the secret: Where do all the new
operators go?
I should mention that I'm developing with PHP, so every request starts at the same point, and it seems like that root "method" in index.php
would need to cover everything the application could do, in order to ensure it provides an object for everything it will do in the current request. Again, there's a fundamental misunderstanding here, and I'd really love to correct it.
回答1:
You've pretty much got it right: they all end up in your composition root. See this interview with Mark Seeman, nearer to the bottom, for an explanation of why this is a good practice and is important.
Also of note: dependency injection is meant for services, not entities or value objects. So e.g. injecting IUserRepository
makes sense, but IUser
not so much---and certainly not StringBuilder
s. That might help clarify things, in light of your parenthetical about more primitive types: the division is not really how primitive they are, but rather what their role is in the system.
回答2:
It's quite obvious - the new
operator went to the DI container, when all instantiating happens.
There also shouldn't be any performance problems, because services are instantiated on demand - when you ask for them for the first time.
For development in PHP I suggest you to look into Symfony Framework which implements DI very well and it's an example of flawless and pure architecture.
回答3:
Dependency Injection is usually used in MVC apps. In those case, the "new
", as you call it, usually go into the Controler layer.
When calling the Model, the Controler instanciate dependencies, and give them to the Model, and then calls the business methodes exposed by the Model.
But don't think DI is about "removing the new
s". It is about decoupling. The less classes instanciate (and thus "know") other classes, the less is can be harmed by changed in these other classeS.
Another aim is to make testing easier. If you have a method that needs to compose and send mail, it's hard to test if a mail is correctly composed. However, if you remove the dependency of actually sending the mail, from this class to another class, and only give an object that can send mail to this method, when you'll write your test, instead of giving this method an object that can really send mail, you'll give this method an object that only fake sending mail. Do you see the point ?
回答4:
DI is frequently used in tandem with IOC Containers (once the scale of the app exceeds a certain threshold). Containers are configured such that they can service requests for a particular type/interface and return completely constructed objects (with required dependencies).
So instead of doing a new ConcreteType()
, you ask the container container.Get<Interface/Type>()
.
That said, since I read the comments indicating you're just starting out, I'd recommend not rolling your own DI Framework (it's a common time-sink). Use and study the code for existing functional DI libraries like MEF/Unity/any other.
来源:https://stackoverflow.com/questions/6388442/when-using-dependency-injection-where-do-all-the-new-operators-go