Dependency Inversion Principle
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
This principle seeks to "invert" the conventional notion that high level modules in software should depend upon the lower level modules. The principle states that high level or low level modules should not depend upon each other, instead they should depend upon abstractions. This is best illustrated by an example.
We will consider the design of a three layer protocol stack. The discussion is divided into three steps:
- Original Design
- Modified Design (Conforms to the Dependency Inversion Principle)
- Advantages of Dependency Inversion
Original Design
The following code shows the design of a three layered radio link control (RLC) protocol stack. The three layers of the protocol are:
- RLC Physical Layer
- RLC Datalink Layer
- RLC Network Layer
All three layers are modeled as classes. A code skeleton for these classes is presented below. This is not a good design. It has the following limitations:
- All layers are dependent upon each other. The code for each layer needs to include the header files for all other layers.
- Layer relationships are hard-coded. You cannot assemble new protocol by just assembling a new set of layers.
- Design changes in one layer could impact the design of other layers.
The solution to these problems is to modify the design of layers by basing it on a common abstraction. This is the subject of the next section.
Layers
Modified Design (Conforms to the Dependency Inversion Principle)
We now apply the Dependency Inversion Principle to the above code. We define an abstract class Protocol_Layer that represents a generic layer. Important elements of the Protocol_Layer abstraction are:
- The constructor allows you to associate a layer two more layers, viz. the upper layer and the lower layer.
- A layer receives a message for transmission and invokes the layer specific processing. After layer specific processing, the message is passed to the lower layer.
- When a message is received from the lower layer, layer specific processing is invoked. Once layer handling of the message is completed, the upper layer is invoked.
The three layers in the above example, will now inherit from the Protocol_Layer. This design completely decouples the three layers. All the layers depend upon the abstraction but not on each other. The advantages are described in the next section.
Protocol_Layer Abstraction
Advantages of Dependency Inversion
Application of the Dependency Inversion Principle has given us the following advantages:
- There is no coupling between the three layers. Each layer's implementation does not even need to include the header files for other layers.
- You have flexibility in mixing and matching different layers. You could use the physical layer implementation with a different datalink layer. This can be accomplished without writing a single line of code in the physical layer class.
- You can sandwich layers between two layers. For example, you can add a debugging layer between physical and datalink layers. This layer could trap messages between the two layers.
For more details about the design, please refer to the Protocol Layer Design Pattern article.