5

Suppose I have MasterPackage containing a Master class, and BlasterPackage containing Blaster class. Since Master needs a Blaster to work, the higher level MasterPackage depends directly on lower-level BlasterPackage.

Using classic Dependency Inversion, one would add IBlaster (or AbstractBlaster) to MasterPackage, thus inverting dependency between the packages.

But while facing this problem, I realized I could create a third package - let's name it MasterBlasterInterfaces - and put IBlaster there. That would "delegate" the dependency to that third, common package, and the original two packages would be now decoupled from one another.

But then, if I have a higer-level package called ThunderDome, it will have to deal directly with BlasterPackage, wouldn't it? I feel like it sort of violates encapsulation, while with direct dependency Master could possibly abstract away the very existence of Blaster.

So the question is:

Is there a good set of criteria to choose between inversion vs. delegation of dependencies? And how one vs. other impacts use of the packages by higher-level application layers?

heltonbiker
  • 1,038
  • 1
  • 9
  • 17

1 Answers1

3

Inverting the dependency as you have described it is only OK if you control the other package, and if this direct dependency is acceptable. For a lot of software, that is entirely OK. But most obviously, this doesn't work if your BlasterPackage is an external library you rely on.

The magic of introducing an IBlaster interface is that MasterPackage and BlasterPackage can work together without having a direct dependency relationship, and without being written by the same person/team/organization. Usually, this is not done by putting IBlaster into a separate package that both modules depend on. Instead, IBlaster would be part of MasterPackage, and you would introduce an adapter module. The dependencies can be illustrated like this:

an UML diagram illustrating that MasterPackage and BlasterPackage do not have to be directly connected

The adapter depends on both packages, but the Master only depends on IBlaster. Such adapters get extremely useful when writing unit tests, and wanting to mock out the BlasterPackage. Of course, the MasterPackage will require some conforming adapter to be provided, but there is no concrete dependency on the BlasterAdapter. Even when you make all dependencies explicit and configurable, at some point the interface consumers and interface providers will have to be connected (dependency injection).

amon
  • 132,749
  • 27
  • 279
  • 375
  • Nice! So do you thing it is correct to say that `ThunderDome` module, using a `Master` class, could also inject the `Blaster` dependency into it? And what would be a sensible way for the `ThunderDome` module to get/create an instance of `Blaster` in the first place? Mind that am not currently using any dependency injection / IoC framework, and don't plan to do so unless unavoidable. – heltonbiker Dec 15 '15 at 15:32