I’m sure you use dependency injection in many of your projects. And usually, it’s a very good thing to do.
However, I found out that many people use it somehow blindly: every time they need to extract a new class just to remove code duplication from two other classes they say that the common one becomes a new service, prepare an interface and do separate unit tests for it, while they update other classes to become partial wrappers for the new one, reusing the common one through dependency injection of course, and update unit tests as well to only verify calling the common one. Their argument: inheritance (another choice for removing code duplication) is bad, and composition is good.
But who says that you need composition though dependency injection in order to avoid inheritance?
Instead, in many cases, I would suggest to use composition indeed, but do not turn the new class into a full service (especially if it has no use alone, but just when called through the wrappers). I would say it’s enough to create a new class to remove the code duplication (otherwise maintenance would be horrible, that’s true), but it’s not always necessary to use dependency injection to instantiate it in the wrapper classes. You can construct instances of the common class in the classic way too, with the new keyword!
Moreover, when you do unit testing it tests don’t need to be per minimum possible object. A “unit” can also be made of a couple (or even chain) of related classes that together form a functional group!
Of course, sometimes you would really want to write separate tests for the common logic, to avoid test duplication as well. That is a good point, and in this case you do need an interface for the core class to be able to mock it when testing the remaining wrappers. But in this case I’m still against dependency injection for the “main” app code – I don’t see any value added – creating the core class instance with new should be fine too.
I may be wrong, but in my opinion too much dependency injection tends to hide some of the necessary tight dependencies away, increasing – and not decreasing – maintenance time on the long term. What do you think?
Update: I also think that sometimes having different roots (and containers) for dependency injection is good as well. For example, you can have a main dependency injection container for your app, and a few secondary ones for some of your features. Keeping configurations separated for these containers will help a new developer that tries to understand how the app has been build do it easier: he or she can see how the app parts get constructed separately from how some features inject dynamic content loaded themselves.