开发者

DI: How much to inject?

开发者 https://www.devze.com 2023-02-05 16:48 出处:网络
I\'m writing my second real life application, which uses DI. Overall I think it have let to a better design. But there are some code smells, that I don\'t know how to solve.

I'm writing my second real life application, which uses DI. Overall I think it have let to a better design. But there are some code smells, that I don't know how to solve.

I prefer to use constructor injection and have often observed that I need about 5 or more objects to be injected in the constructor. It seems to be too many, maybe it's a design problem, not getting the SRP right. But I think that my use of DI also is to be blamed.

I'm looking for "best practices" or "rule of thumb", in general I seem to inject everything, that开发者_运维问答 isn't in the .Net framework, is that overdoing it?

To get things started, here are two examples of objects that I inject, but am uncertain about.

Objects that are true singletons like application configuration or those small util classes, do you inject them? They seems to be injected very often, the only reason to inject them seems to be allow to change the value for testing, but Ayende seems to have solved the problem in another way: http://ayende.com/Blog/archive/2008/07/07/Dealing-with-time-in-tests.aspx.

Common objects such as logging, that are used in almost every object, should they be injected?


The rule of thumb I often use is that I inject things that are in the way of properly writing unit tests. When doing this you will sometimes end up abstracting away BCL classes (such as DateTime.Now, File, etc), and sometimes your own stuff. Good things to inject are services (such as ICustomerService, ICustomerUnitOfWorkFactory, or ICustomerRepository). Don't inject things like entities, DTOs and messages.

There are other reasons for injecting objects however, such as to be able to replace modules at a later time (for instance switch implementations for validation, UI, or O/RM), to allow parallel development within or across teams, and to lower maintenance.

I prefer to use constructor injection and have often observed that I need about 5 or more objects to be injected in the constructor.

As you already noted yourself, having many dependencies could be caused by not adhering to the SRP. What you can do however, is grouping common dependencies with there logic into an aggregate service and inject that into consumers. Also see Mark Seemann's article about Aggregate Services.

Objects that are true singletons like application configuration or those small util classes, do you inject them?

I am personally not a fan of the way Ayende proposes it. This is an Ambient Context, which is a specific sort of service locator construct. Doing this hides the dependency, because classes can call that static class without you having to inject it. Explicitly injecting it makes it much clearer that you need to unit test time. Besides that, it makes it hard to write tests for frameworks such as MSTest who tend to run tests in parallel. Without any countermeasures, it makes your tests very unreliable. A better solution -for the DateTime.Now example- is to have an IClock interface, as is suggested here. As you can see, that answer scores much higher than Ayende approach, that is shown in the same SO question.

Common objects such as logging, that are used in almost every object, should they be injected?

I inject them in my code, because that makes the dependencies clear. Note however, that in my code I still hardly ever have to inject a logger. Think hard about every line you want to log and it isn't really a failure (or a cross-cutting concern that should be placed elsewhere). I usually throw an exception when something happened that I didn't expect. It allows me to find bugs fast. Or to put it in other words: Don't filter, but fail fast. And please ask yourself: "Do I log too much?"

I hope this helps.


My personal rule of thumb is this:

  • inject it if you want it to be immutable
  • inject it if you want to be able to substitute it for testing purposes

Things like services can meet both of those criteria - the consumer should never change it, and you want to be able to substitute it come testing time. With the immutable items, you would still possibly have a property on the consuming object, but that property would only have a getter, not a setter. If you wanted to change the value you have to create a new instance of the object.

Should loggers be injected?

There is no reason to. Loggers are typically exposed via a static class, and are new'ed up from configuration entries, so even for testing purposes there is no need to inject them.

Should true singletons like application configuration be injected?

Once again, it is a globally accessible object which is easily modified for test purposes, so no need to inject. The only time i would inject this is if the consumer was 'disconnected'; i.e. created via reflection or called as a web service or remote object.

While DI is a nice pattern, too much of a good thing can still be unhealthy. If you sense a growing code smell, then examine each item you are injecting and ask yourself the question: Do i NEED to inject this parameter?


A good starting point is to inject Volatile Dependencies.

You may want to also inject Stable Dependencies for further loose coupling, but if you need to prioritize, Volatile Dependencies is the best place to start.

Concerning constructor over-injection, it's really just a symptom of breaking SRP: see this related question: How to avoid Dependency Injection constructor madness?

0

精彩评论

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