4

If I got it right, the Dependency Inversion principle says that high level modules should use interfaces to the low level modules and low level modules should implement these interfaces. The purpose is to reduce the coupling between low-level modules and high-level modules and make the low-level modules easier to replace. This UML diagram makes the concept simple easier to understand.

However, the DIP Wikipedia says that "the inversion of the dependencies and ownership encourages the re-usability of the higher/policy layers". How does it do that? I can only see it encouraging the re-usability of lower layers.

1
  • 1
    The answer is in the sentence immediately after the one you quoted: "Upper layers could use other implementations of the lower services."
    – John Wu
    Commented Dec 4, 2020 at 8:26

3 Answers 3

7

Low level modules are "low level" because they have no dependencies, or no relevant dependencies. Very often, they can be easily reused in different contexts without introducing any separate, formal interfaces - which means, reusing them is straightforward, simple and does not require any Dependency Inversion.

High level modules, however, are "high level", because they require other, lower level modules to work. But if they are tied to a specific low-level implementation, this often prevents to reuse them in a different context.

Let us make an example: take a "high-level" module which encapsulates a sorting algorithm. It will usually require two other low-level modules:

  • an ordered container for the items to be sorted, like a list or array of a certain type

  • a module encapsulating a comparison function (a "predicate") for two items

If the sorting module is implemented in a way which directly ties it to a specific kind of list, with a specific item type, and a specific comparison, it will be hard to reuse in a different context. By applying DIP, however, one would:

  • implement the sorting module in a more generic fashion, depending only on an abstract list interface and an abstract comparison interface, and

  • make the "lower level modules" implement these interfaces.

By this approach, the sorting module can be reused for several kind of lists and all kind of ordering requirements.

2

When implementing DIP, the higher layer (which exposes the interface that the lower layer will implement), can therefore write reusable code that handles any implementation of this exposed interface.

At best, Wikipedia could've better said "encourages code reuse inside the higher/policy layers", but that is what it already intends to convey and the two variants are synonymous enough.

In the current phrasing on Wikipedia, I suspect you've misinterpreted it to imply that the higher layer gets reused by multiple consumers. It doesn't.

But DIP does enable the higher layer to write its own code more reusably, and that's what Wikipedia means.


As a very basic example, if your domain (higher layer) exposes a IFooRepository that will be implemented by one or more database projects (lower layer projects), then your domain can write IFooRepository-handling logic once, no matter how many implementations of IFooRepository exist, in no matter how many lower layer projects.

Therefore, the domain logic for IFooRepository is more reusable than it would've been without DIP.

Because without DIP, each lower layer project would expose its own interface to the domain, and the domain would've had to handle these different interfaces individually. That's less reusable.

3
  • "In the current phrasing on Wikipedia, I suspect you've misinterpreted it to imply that the higher layer gets reused by multiple consumers. It doesn't." - So are you saying it's more about a single consumer (containing class) being able to view multiple implementations as the same base type than it is about multiple consumers each being able to view a different implementation as the base type (though that's certainly a use as well, but less important).
    – ToddR
    Commented Dec 4, 2020 at 14:35
  • @ToddR: I'm not quite sure I understand the distinction you're pointing out. What I'm saying is that when the domain exposes an interface (to be used as an inverted dependency), then the domain only has to write one handling logic, i.e. for that interface. Whereas if the libraries all expose their own interface, and the domain depends on those libraries, the domain will have to write one handling logic per library (i.e. for that library interface). The former is more reusable-friendly since you don't have to implement many different libraries' interfaces individually.
    – Flater
    Commented Dec 4, 2020 at 14:38
  • Yeah, I probably worded it poorly, but I believe I understand what you're saying now. Thank you for the additional clarification.
    – ToddR
    Commented Dec 4, 2020 at 15:13
2

Reusability means that, theoretically, you could pluck some unit of code out and drop it somewhere else and reuse it. However, this rarely ever happens so why do we care so much?

Because achieving reuseability means I don’t have to read much code to understand what I’m looking at. Reusable code has a protective wall around it that you don’t have to look beyond to see how it works.

If you’ve ever found yourself jumping through file after file trying to follow how something works you know what a god send that wall is.

Abstractions (interfaces, abstract classes, whatever) are what you build that wall with. These hide the concrete details of either low level modules or high level modules.

I can see why you’d more readily accept that low level modules can have their details hidden like this but high level modules have details as well.

Sure they are supposed to be more abstract but that doesn’t make them abstractions. They have details like business rules and policies. But, ideally, they only express those to the outside world through abstractions.

So you should be able to sit with either a low level or high level modules file in front of you and understand what’s happening without having to pull up other modules at the same time. You don’t even need to know which exact ones you’re module is working with. That means it’s reusable. That means it’s working through abstractions. It doesn’t matter if you’re low or high.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.