A small design excerpt from the smart-home scenario:
Buttonsince it depends directly on
Lamp. (But there are plenty of other uses for
Buttonshould not depend on the details represented by
These are symptoms of the real problem (Violation of the Dependency-Inversion Principle).
The high-level policy (detection of on/off gestures) underlying this (mini) design depends on the low-level details.
The underlying abstraction is the detection of on/off gestures and their delegation to a server object that can handle them.
If the interface of
Lamp is changed,
Button has to be at least tested or even adjusted, even though the policy that
Button represents is not changed!
To make the high-level policy independent of details we should be able to define it independent of the details of
Lamp or any other specific device.
Button only depends on abstractions; it can be reused with various classes that implement
Lamp will not affect
The dependencies have been inverted:
Lamp now has to conform to the interface defined by
Actually: both depend on an abstraction!
High-level, low-level modules. Good software designs are structured into modules.
High-level modules contain the important policy decisions and business models of an application – The identity of the application.
Low-level modules contain detailed implementations of individual mechanisms needed to realize the policy.
High-level policies and business processes is what we want to reuse. If high-level modules depend on the low-level modules changes to the lower level details will force high-level modules to be tested again/to be changed. Additionally, it becomes harder if not practically impossible to use them in other contexts. It is the high-level modules that should influence the low-level details.
We have three modules where
Regulate uses the other two:
Regulatepulls data about the current temperature from the
Furnacecomponent to increase or decrease heat.
„…all well-structured object-oriented architectures have clearly defined layers, with each layer providing some coherent set of services through a well-defined and controlled interface…“
A possible Interpretation:
The higher the module is positioned in a layered architecture, the more general the function it implements.
The lower the module, the more detailed the function it implements.
This interpretation clearly violates DIP. Higher-level modules depend on lower-level modules.
This is actually a typical structure of a layered architecture realized with structured programming (e.g., using "C").
Usually, we think of utility libraries as owning their own interfaces. (A relict from structured programming era.) Due to ownership inversion,
Policy is unaffected by changes in
All relationships in a program should terminate on an abstract class or an interface.
This heuristic is usually violated at least once in every program:
The heuristic seems naive for concrete stable classes, e.g.,
String in Java. But, concrete application classes are generally volatile and you should not depend on them. Their volatility can be isolated by keeping them behind abstract interfaces owned by clients.
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Object-orientation enables to invert the dependency: