Dependency inversion principle
The most flexible systems are those where dependencies only refer to abstractions - not concretions. In a statically typed language - use, import, include statements should only refer to modules containing interfaces or some kind of abstract declaration. This also applies to dynamic languages - but it's harder to define what a concrete module is.
This should not be treated as a rule - e.g. String in java is concrete, but it is very stable. Programmers do not have to worry about frequent changes to String.
It is the volatile concrete elements that we want to avoid depending on - actively developed modules which are changing frequently.
Stable abstractions
Every change to an interface requires changes to the implementations, but changes to the implementations do not affect the interface. Work hard to reduce the volatility of interfaces.
- Don't refer to volatile concrete classes - refer to abstract interfaces, need to use abstract factories for object creation
- Don't derive from volatile concrete classes - worse than just referring to it
- Don't override concrete functions - concrete functions often require source code dependencies, you inherit the dependencies. Make the function abstract and create multiple implementations
- Never mention the name of anything concrete and volatile
Factories
The creation of volatile concrete objects requires special handling. In most OO languages you would use an abstract factory
- The application uses the concrete implementation through the Service interface
- But it needs to somehow create an instance of the concrete implementation without referring to it - need a factory
- The curved line represents an architectural boundary, all arrows crossing it point towards the abstract side
- The arrows point in the opposite direction to if we just referred to the dependencies - they're inverted.
Concrete components
The service factory implementation requires a single concrete dependency - the ConcreteImpl and violates DIP. Violations can not be completely removed, but they can be gathered into a small number of classes and kept separate.
The concrete implementation could be referenced in main and dependency injected into the factory.