I’ve been pondering properties of self-documenting code. Comparing self-documenting properties against SOLID lead me to realize Information Hiding, or conceptual scope, is a central theme of SOLID.
- Single Responsibility Principle: Any given piece of code (variable, function, class, etc) should have one reason for change.
- This is about conceptual problem scope. The unit should isolate a focused design force that would cause code to change.
- Open-Closed Principle: You should be able to extend a system/component without modifying it.
- This principle is the mechanism for decoupling conceptual scopes while allowing composition. It pushes components to define flexibility on their own terms so that they can be used flexibly rather than incorporating knowledge about different consumers. This enables components to isolate their own domain and keep tight conceptual scope. Constructs like tags and callbacks are good examples of open extensibility mechanisms. I have a series of examples of this principle if it still seems unclear.
- Liskov Substitution: Derivative types should be indistinguishable when used as their parent type.
- This principle targets sneaky implicit semantic coupling where a caller depends on unenforced details of specific implementations. Such coupling greatly increases the scope a programmer must consider. The consumer must unintuitively consider implementations behind an abstraction or different implementations must be careful to mimic each other’s behaviors.
- Interface Segregation: No code should be forced to depend on methods it does not use.
- This limits the scope a component knows about through dependency contracts. It also pushes towards Dependency Inversion and Port and Adapters architecture to achieve this level of dependency focus.
- Dependency Inversion: Depend on abstractions, not concrete types. Callers own the abstractions
- Dependency Inversion is the cornerstone for isolating components from the concepts of their dependencies. The calling component defines dependency abstractions on its own terms and lets someone else provide concrete implementations. This dramatically reduces the current component’s conceptual scope. The calling component, if also obeying Liskov Substitution, knows nothing of its dependencies other than the contracts the calling components itself defined. The component’s conceptual scope is reduced only to it’s own defintions. The rule or flow is conceptually self-contained and the lowest layer in a dependency chain. This allows those rules to be reused in many different configurations rather than having application-specific concerns baked into the rules. Some examples follow below if this principle is still unclear.
Each and every principle in SOLID is a tool for limiting conceptual scope!