Higher Level Modules should Not Depend on Lower Level Module’s Dependencies
Lower Level Modules should Depend on Higher Level Modules Abstractions
Both Should Depend on the Abstraction Details
All Software Entities are created for a purpose.
An Entity can manage State, as in a Data Transfer Object (DTO).
A DTO is a Plain Old CLR Object (POCO) Class
An Entity can also manage Behavior and consume State Entities as dependencies such as a Class with behavior methods.
Software Entities should have a single responsibility that relies on one or more dependencies to carry out the responsibility of the entity.
The Dependency Inversion Principle states that within the hierarchy of an assembly’s layering structure Type Instantiation should be managed at the highest level of abstraction.
The Consuming Entity that Understands the Dependencies it Requires
… Should Create and Manage those Dependencies
We have to create a Class that will to print to a designated output.
The consuming Client will pass a parameter set that defines the following:
The Data: A SQL query that defines the data required for the printed output
The Page Format: Selects the page size of 8.5” x 11” or 11” x 17”
A Printer: A printer name to output the printed information
The Client class Single Responsibility is to request the page be printed by delivering the output requirements to the Print Class using its PrintCustomerInformation() method.
The Print class Single Responsibility is to execute the PrintPage() method as defined by the parameters.
The dependencies required by the Print Class are the page size, the data and the printer.
The Single Responsibility is to make sure it is printed not select the page size, make the database call and select the physical printer to print the results.
We create Helper Classes that comply with the Single Responsibility Principle (SRP) for each of the required dependencies.
This allows us to comply with the Open / Closed Principle (OCP) by giving us the ability to change the Page Format within its own type and leave the Print Class open for the change but closed for modification within the Print Type.
This also applies to a change in the data source for the SQL query and to add additional Printers to the Print Class without the Print Class knowing anything about them.
Here is where DIP becomes important:
The Print Class’s responsibility is to take the dependency information and pass it to the responsibility classes.
It should not know or care about those dependencies. It assumes that the Client knows what it wants done.
The Print Class should not care about the details of any of the dependency responsibilities.
It assumes that the lower level modules know how to fulfill its single responsibility. The Print Class has the responsibility to make sure the correct dependencies are called to do the work and the dependencies they require are valid and are passed to them.
In this scenario it is clear that the Client Class has the knowledge of ALL the dependencies needed for a successful print out, not the lower level modules: The Print Class, the Format Class, the Data Class and the Printer Class.
Therefore it is the responsibility of the Client Class, the highest level of abstraction, to manage the instantiation of the lower module types.
It should pass it into the Method Constructor of the Print Class’s PrintPage() method as Parameter Dependency object.
A Word about Inversion of Control Containers:
Managing the dependencies at this high level allows the application to define the types as Interface abstractions and allow Inversion of Control (IOC) containers to define the concretes.
IOC Containers can Manage all of your Assembly Dependencies
This allows for run time selection of dependencies used throughout the entire application.
By typing to the Interface Abstraction Type rather than a Single Concrete Type changes in the state at run time can dynamically change the concrete types being injected into the constructors.
Let an IOC Container Manage the Lifetime of an Instantiated Type for You
… It Knows More about the Environment than You Do
The following two tabs change content below.
I am a Principal Architect at Liquid Hub in the Philadelphia area specializing in Agile Practices as a Certified Scrum Master (CSM). I use Test Driven Development (TDD) and Acceptance Test Driven Development (ATDD) with Behavior Driven Development (BDD) as my bridge to Agile User Stories Acceptance Criteria in a Domain Driven Design (DDD) implementing true RESTful services