Robert C. Martin a écrit un intéressant article sur un ensemble de métriques permettant de mesurer la qualité d'une conception orientée objet en termes d'interdépendance entre les sous-systèmes de cette conception.
Voici ce qu'il dit dans l'article à propos de l'interdépendance entre modules :
What is it that makes a design rigid, fragile and difficult to reuse. It is the interdependence of the subsystems within that design. A design is rigid if it cannot be easily changed. Such rigidity is due to the fact that a single change to heavily interdependent software begins a cascade of changes in dependent modules. When the extent of that cascade of change cannot be predicted by the designers or maintainers the impact of the change cannot be estimated. This makes the cost of the change impossible to estimate. Managers, faced with such unpredictability, become reluctant to authorize changes. Thus the design becomes rigid.
Et pour lutter contre la rigidité, il introduit des métriques comme le couplage afférent, le couplage efférent, le caractère abstrait et l'instabilité.
Couplage afférent : Le nombre de types extérieurs à ce projet qui dépendent de types au sein de ce projet.
Couplage efférent : Le nombre de types extérieurs à ce projet utilisés par les types de ce projet.
Le couplage efférent et le couplage afférent peuvent aussi s'appliquer aux espaces de noms et aux types. Par exemple, le couplage efférent d'un type particulier est le nombre de types dont il dépend directement. Les types où TypeCe est très élevé dépendent de trop d'autres types. Ils sont complexes et ont en général plus d'une responsabilité.
Caractère abstrait
Le rapport entre le nombre de types abstraits internes (c'est-à-dire classes abstraites et interfaces) et le nombre de types internes. La plage de cette métrique va de 0 à 1, A=0 indiquant un projet complètement concret et A=1 un projet complètement abstrait.
A = Na / Nc
Where:
A = abstractness of a module
Zero is a completely concrete module. One is a completely abstract module.
Na = number of abstract classes in the module.
Nc = number of concrete classes in the module.Instabilité
Le rapport du couplage efférent (Ce) au couplage total. I = Ce / (Ce + Ca). Cette métrique est un indicateur de la résilience du projet face au changement. La plage de cette métrique va de 0 à 1, I=0 indiquant un projet complètement stable et I=1 un projet complètement instable.
I = Ce/(Ce + Ca)
I represent the degree of instability associated with a project.
Ca represents the afferent coupling, or incoming dependencies, and
Ce represents the efferent coupling, or outgoing dependenciesLe graphe Abstrait vs Instable et la zone de douleur
Voici, à titre d'exemple, le graphe Abstrait vs Instable de la bibliothèque C++ POCO.

L'idée derrière ce graphe est que plus un élément de code est largement utilisé dans un programme, plus il devrait être abstrait. Autrement dit, évitez de trop dépendre d'implémentations concrètes ; dépendez plutôt d'abstractions. Par élément de code populaire, j'entends un projet (mais l'idée fonctionne aussi pour les packages et les types) massivement utilisé par d'autres projets du programme.
Ce n'est pas une bonne idée d'avoir des types concrets largement utilisés dans toute votre base de code. Cela crée des zones de douleur dans votre programme, où modifier les implémentations peut potentiellement affecter une grande partie du programme. Et les implémentations sont connues pour évoluer plus souvent que les abstractions.
La ligne de la séquence principale (en pointillés) dans le diagramme ci-dessus montre comment le caractère abstrait et l'instabilité devraient être équilibrés. Un composant stable serait positionné à gauche. Si vous regardez la séquence principale, vous pouvez voir qu'un tel composant devrait être très abstrait pour être proche de la ligne souhaitable — en revanche, si son degré d'abstraction est faible, il est positionné dans une zone appelée la « zone de douleur ».
Comment lutter contre la rigidité avec l'approche POO ?
Comme l'a écrit Robert C. Martin dans son article, nous devons utiliser des classes abstraites et des interfaces pour rendre nos projets plus flexibles et réduire le couplage élevé entre les éléments de code.
Le couplage en POO peut être introduit par :
- L'héritage : il est souvent surutilisé lorsqu'on adopte le paradigme POO, et malheureusement dans de nombreux cas il rend le code plus rigide. Certains design patterns sont utiles pour résoudre la rigidité introduite par l'héritage, comme le pattern Adapter, qui minimise la rigidité introduite par l'héritage.
- L'utilisation directe d'une implémentation concrète : dans ce cas, le code devient aussi rigide car il est difficile à changer si nous devons utiliser une autre bibliothèque ou un autre framework pour une raison quelconque. Comme pour l'héritage, il existe des design patterns pour minimiser la rigidité, comme le Bridge ou le Proxy.
Avec l'approche POO, il est recommandé de maîtriser les patterns structurels du GoF ; ils aident à réduire la rigidité introduite par le couplage.
