开发者

When using dependency injection, where do all the new operators go?

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 StringBuilders 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 i开发者_运维问答t 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.


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 StringBuilders. 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.


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.


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 news". 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 ?


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜