This article will present the SW Component Coupling Principles. Along with the introduction of this topic, examples from the MechLynx project will be used (https://github.com/CharlieGearsTech/MechLynx).
The following articles mentioned the SOLID Principles, which were discussed previously:
- SRP: http://gearstech.com.mx/blog/2020/07/01/design-principles-single-responsibility-principle/
- OCP: http://gearstech.com.mx/blog/2020/07/15/design-principles-open-close-principle/
- LSP: http://gearstech.com.mx/blog/2020/07/15/design-pattern-interface-segregation-principle/
- ISP: http://gearstech.com.mx/blog/2020/07/15/design-pattern-interface-segregation-principle/
- DIP: http://gearstech.com.mx/blog/2020/08/01/design-principle-dependency-inversion-principle/
In contrast with the SOLID principle which determines how a SW Component should be internally built. The SW Component Design Principles (set of SW Component Cohesion and set of SW Component Coupling) determines the arrangement of SW Components within a SW system.
SW Component Cohesion principles indicate how to build SW Components (what is put inside), while Component Coupling principles dictate the relationship between SW Components.
Acyclic Dependencies Principle
SW Systems are a collection of versioning of SW Components as the REP principle dictates (http://gearstech.com.mx/blog/2020/10/01/sw-component-cohesion-releasereuse-equivalency-principle/). So SW Components relationship depends on the versioning of each of them. For example, SW Component A version 1.0 can use the SW Component B version 9.2. The problem here is that following REP is not enough to manage the “packages” since it is also required to manage the dependencies between those packages. For example, there should not be cyclic dependencies between packages to avoid unexpected fails at a SW Component which was stable but the cycling causes that changes redirect for itself.
The first principle of SW Component Coupling is Acyclic Dependencies Principles (ADP) which indicates that: “There must be no cycles in the dependency structure of SW Components.
To illustrate REP, consider the following diagram which shows the dependencies between packages of MechLynx.
This is a Direct Acyclic Graphic due there is no dependency cycle between packages. When a new version is delivered from SE Presenter, then SE View, and ASW are affected; Inference Kernel, Compiler and Workspace are unaffected and this is good due no revalidation, redeployment and recompilation are needed.
Consider that a new requirement arrived which makes Inference Kernel depend from SE View. This situation is illustrated in the following image:
This is no longer a DAG, since there is a cycle from Inference Kernel, to SE View, and SE Presenter and later winding up again to Inference Kernel. This can cause several problems: Version dependency, Validation dependency and Build Disorder.
Version dependency happens when packages only work together with specific versions, in this case, changes from Inference Kernel provokes changes to SE Presenter and SE View; when SE Presenter and SE View changes are released, Inference Kernel will need to change again to resolve its own original version.
Before this change Inference Kernel validation did not depend on any package, so the written test should not be modified. Now with this dependency cycle, Inference Kernel depends on SE Presenter and SE View, and they need new revalidation; moreover this revalidation will be rigid to the versioning of these packages due the version dependency.
This cycle in the dependency graph makes the order of build to be blurred, this can cause difficult to solve problems for statically typed languages (C++, C, Java, …).
The Stable Dependencies Principle
Stable Dependency Principle (SDP) dictates that SW Components that are intended to be easy to change should not have dependencies from SW Components that are intended to be difficult to change… SW Components should depend in the direction of stability.
A way to make a SW Component “A” difficult to change, is by making other SW Components “Bs” to depend upon “A”, “Bs” SW Components then by definitions are dependent. This dependency ratio will increase as “Bs” depend more on other SW Components (either indirectly by “A” or directly from a first level SW Component dependency). If “A” has no SW Component to depend on, then by definition “A” is an independent SW Component.
Stability of a SW Component is the ratio between incoming and outcoming dependencies as follows:
I = Instability; I= 0 means stability, I= 1 means instability
Fo= Outcoming classes dependencies.
Fi= Incoming classes dependencies.
Taking the Inference Kernel from the following diagram:
Three SE Presenter classes depend on Inference Kernel, 2 Compiler classes depend on Inference Kernel and Inference Kernel depend on only one class on Workspace. In this case the I of Inference Kernel isI = ⅕, therefore Inference Kernel is relatively stable.
Stability is not an attribute to strive to all SW Components, if all SW Components are stable, then the System will be incapable to change, In contrast, SDP strives to sort SW Components from the highest rate of volatility and gradually move until the highest rate of stability.
For example, the next system violates SDP since stable SW Components are depending from a volatile SW Component:
One solution for this system is to use SOLID principles to redesign the SW Component relationship, specially using the Dependency Inversion Principle (DIP).
The Stable Abstraction Principle
The Stable Abstraction Principle (SAP) dictates that SW Components shall be as abstract as they are stable; then SW Components should depend in the direction of abstraction.
SAP yields to correct the design as follows: Abstractness allows stable SW Components to be extended. Concreteness (opposite of Abstractness) allows unstable SW Components to be easy to change.
Abstractness ratio for a SW Component can be calculated by the following formula:
A = Abstractness; A= 0 means full concreteness, A= 1 means full abstractness
Na= Number of abstract and interfaces class within the SW Component
Nc= Number of classes in the SW Component.
For example, if Inference Kernel contains 6 classes which 2 are interfaces, then A = 1/3 , meaning that is relatively concrete.
Robert C. Martin’s Clean Architecture introduces the concept of main sequence. Main sequence aids the design of SW Components based on their needed abstractness and stability.
The “Too Rigid” section is when a SW Component is highly stable (I = 0) and is highly concrete (A = 0). Too rigid is not a section that a SW Component should stay since it cannot be extended and it is very difficult to change.
The “Useless” section is when a SW Component is highly unstable( I =1) and is highly abstract (A = 1). Useless is not a section that a SW Component should stay since it is abstract but no other SW Component is using it, which yields to unused code.
The Main Sequence is the line that crosses from [0,1] to [1,0]. Main Sequence is where SW Components should strive to stand, Here, the relation stability-abstraction will not cause side effects to the SW Component relation with other Components; yielding to a good design of the system.