3

I'm working with dependency injection at the moment. Effectively this involves the UI layer (e.g. a web application) including a DI container which has a whole bunch of data about interfaces it will work with, and which implementation to use for each.

However, because of the need to satisfy dependencies, the DI container also needs to know about interfaces and classes it would not otherwise need access to. For example:

The UI layer needs to work with an IWidgetManager interface. The configured implentation is ConcreteWidgetManager. This is registered in the DI container.

The ConcreteWidgetManager has a dependency on IWidgetRepository. The desired implementation for this is ConcreteWidgetRepository. Thus the DI container must also know about IWidgetRepository and ConcreteWidgetRepository.

Had I simply hardcoded ConcreteWidgetManager's dependency on ConcreteWidgetRepository, then ConcreteWidgetRepository could be made internal and therefore invisible to my UI layer. No UI code could bypass the manager layer and work directly with the repository layer.

Therefore it seems that the use of DI, although an awesome pattern in many respects, defeats encapsulation from the point of view of the UI layer.

Am I right in thinking this, and is there any way to mitigate the situation?

asked Sep 14, 2011 at 11:25
7
  • 2
    The answer is yes, DI does break encapsulation. It's a duplicate of stackoverflow.com/questions/1005473/…, See also bryancook.net/2011/08/… Commented Sep 14, 2011 at 11:29
  • How is ConcreteWidgetRepository visible to the UI layer? The fact that it's mentioned in the framework's configuration doesn't mean that it's "visible," unless your UI code has an auto-wired reference to IWidgetRepository. And that's a coding problem, one that's exactly equivalent to your UI code containing a hard-coded reference to ConcreteWidgetRepository Commented Sep 14, 2011 at 11:30
  • @parsifal - the DI container is part of the UI layer. If it needs to know about the ConcreteWidgetRepository (which it does, to inject one into ConcreteWidgetManager), than that knowledge is also freely available elsewhere in the UI layer. Commented Sep 14, 2011 at 12:52
  • @David - in my view, the DI container is the application: it associates the UI with the business logic, and wires any internal dependencies, but it is not part of either. Commented Sep 14, 2011 at 13:13
  • @parsifal - I don't know what platform you develop on so I'm not quite sure how to frame the question, so I'm going to give it to you .NET style. How do you create your solution then? The DI container is in a different project to the UI project (e.g. web application)? Commented Sep 14, 2011 at 14:13

3 Answers 3

2

No, Dependency Injection is not breaking encapsulation. It is the way you layered your application that is breaking encapsulation. By separating the interfaces and implementations in different assemblies and putting the container configuration in its own assembly, you can prevent the UI layer from having to take a dependency on the concrete ConcreteWidgetManager, or even the IWidgetRepository if you wish.

However, adding extra assemblies has cost. Both in maintenance and in time it takes to compile your solution. Instead of enforcing architectural rules using assemblies, I would enforce them with clear and simple guidelines, or perhaps with a tool such as NDepend. However, when the team is small enough, using code reviews will be effective enough to prevent architectural erosion.

answered Sep 15, 2011 at 10:09
Sign up to request clarification or add additional context in comments.

3 Comments

There's a lot of interesting stuff to think about here. Thank you.
I would like to spend some time setting up a quick solution and seeing if I can get this approach to work. If it does work, I'll give you the points!
Okay, I've experimented with your suggestion about placing interfaces and implementations in different assemblies and it works! The UI assembly needs a reference to the interface assembly, and to a 'wiring' assembly that does the DI resolving. The wiring assembly has a reference to both the interface assembly and the implementation assembly. Thus, the UI assembly has no direct reference to the implementation assembly, but still gets the implementations injected in! This is absolutely the answer to my question.
2

The way you're describing this implies that the UI layer is responsible for creating the DI container.

This might be true in your particular application. But remember that the initialization part is a tiny part of the overall code.

Yes, there is SOME code that creates the DI container and the UI layer in some particular order. It might be the case that the UI layer invokes a function CreateDIContainer which initializes all of the components. But this one function is the only instance where the implementations are mentioned; all other aspects of the UI layer deals with the abstract interfaces. A purist might take issue, but really, in the 99.5 percent of the code that ISN'T CreateDIContainer the UI does not know what the implementations are. Move the initialisation function into a separate static class in a separate library if that makes you happier.


I'm also intruiged about you saying

Had I simply hardcoded ConcreteWidgetManager's dependency on ConcreteWidgetRepository ....,

That you brought it up as an option makes me wonder: do you have any reason NOT to hardcode this relationship? I use dependency injection when I have to - the end benefit is that I can mock parts of the code away in my automated tests.

Are you going to want to mock out the ConcreteWidgetRepository? If you are not, then go ahead and hardcode the relationship. Otherwise you're just introducing architecture for the sake of architecture.

(See You Aren't Gonna Need It.)

answered Sep 14, 2011 at 11:47

5 Comments

the DI container does only comprise a small fraction of the UI layer code, but the fact it needs to know about these classes means that the rest of the UI layer does too.
The point you make about whether or not to hardcode a dependency is an interesting one. The question is how likely am I to want another implementation? Occasionally there are 'operational' reasons why I meet need another implementation (e.g. change of provider) but more often it's for unit testing purposes. My instinct is to use an interface and DI for any service class unless I'm pretty sure I won't need another implementation.
By 'service' class I mean anything that exposes behaviour rather than simply state.
@David - About whether or not to hardcode a dependency - here's where I follow the YAGNI principal. (c2.com/cgi/wiki?YouArentGonnaNeedIt) There's no reason why you can't make it an injected dependency later on when you know that will solve a problem.
Cool, thanks for your thoughts. This question has really made me think about DI again.
1

Yes, It does break encapsulation. But that's the price you need to pay for using DI. It makes your code more testable but yes breaks encapsulation.

From an API perspective of a class, your class that asks for dependencies to be injected becomes as dumb as possible but the class that calls this class now knows too much. In your case the framework. If you use DI in general in your code in terms of say, constructor injection, you calling class needs to know about several classes.

answered Sep 15, 2011 at 11:23

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.