Couplage, cohésion, connascence
Les pages précédentes donnent des règles : une classe ne doit avoir qu’une seule raison de changer, une frontière se place là où le langage change, le domaine ne doit dépendre de rien. Toutes présupposent une capacité qu’aucune n’enseigne : savoir juger si une dépendance est acceptable.
Le couplage et la cohésion sont la seule mesure réelle de cette question, et elles datent de 1974. Le problème est que « couplage » est un mot beaucoup trop grossier : dire « c’est couplé » ne dit ni à quel point, ni pourquoi, ni quoi faire. La connascence est l’instrument fin qui manque — elle gradue ce que le mot « couplage » aplatit en un seul jugement.
Le couplage n’est pas un défaut
Il faut évacuer cela d’abord, parce que c’est l’erreur de cadrage la plus commune. Un système à couplage nul n’est pas un bon système : c’est un ensemble de programmes sans rapport. Le couplage est ce qui fait qu’un programme est un programme.
L’objectif n’est donc pas de minimiser le couplage, mais de le placer : beaucoup à l’intérieur des modules, peu entre eux. Formulé en une phrase, tout l’art de la décomposition consiste à faire passer les frontières là où il y a le moins à couper.
Les deux notions se définissent de manière opérationnelle, par une question :
| La question à poser | |
|---|---|
| Couplage | Si je change ceci, que suis-je obligé de changer ailleurs ? |
| Cohésion | Ce qui est dans ce module change-t-il pour les mêmes raisons ? |
Elles sont liées mais distinctes. Une forte cohésion interne réduit généralement le couplage externe — mais un module fourre-tout dont personne ne dépend a une cohésion déplorable et un couplage nul. La cohésion se juge de l’intérieur, le couplage de l’extérieur.
Les six formes de couplage
Le classement de Stevens, Myers et Constantine, du pire au meilleur. Il a cinquante ans et il est resté exact.
| Forme | Ce que c’est | Exemple | Remède | |
|---|---|---|---|---|
| 1 | De contenu | A modifie l’état interne de B | Écrire dans un champ privé, monkey-patching | Encapsuler |
| 2 | Commun | A et B partagent une donnée globale mutable | Un singleton mutable, une variable globale | Passer la donnée, ou la rendre immuable |
| 3 | Externe | A et B dépendent d’un format imposé de l’extérieur | Un schéma de fichier, un protocole tiers | Un adaptateur, un seul point de contact |
| 4 | De contrôle | A passe un drapeau qui dicte le comportement de B | traiter(commande, true) | Deux fonctions, ou une stratégie |
| 5 | D’estampille | A passe une structure entière alors que B n’a besoin que d’un champ | envoyer(commandeComplete) pour lire l’e-mail | Passer ce dont B a besoin |
| 6 | De données | A passe exactement ce dont B a besoin | calculerTva(montant, taux) | Le meilleur atteignable |
Deux remarques d’usage. Le couplage de contrôle — le paramètre booléen qui
choisit une branche — est le plus fréquent des quatre premiers, et le plus facile à
supprimer : un appel avec true en argument ne dit rien à la lecture, et signale
presque toujours deux opérations différentes fusionnées. Le couplage
d’estampille est le plus discret : il paraît inoffensif de passer l’objet entier,
et il rend B dépendant de toute évolution de la structure de A.
Les sept formes de cohésion
Le classement dual, du meilleur au pire.
| Forme | Ce qui regroupe les éléments | |
|---|---|---|
| 1 | Fonctionnelle | Ils contribuent tous à une tâche bien définie |
| 2 | Séquentielle | La sortie de l’un est l’entrée du suivant |
| 3 | Communicationnelle | Ils opèrent sur les mêmes données |
| 4 | Procédurale | Ils s’exécutent dans un ordre imposé |
| 5 | Temporelle | Ils arrivent au même moment — initialiser(), arreter() |
| 6 | Logique | Ils sont « du même genre » |
| 7 | Coïncidentelle | Aucune relation |
Warning
La cohésion logique est le piège, parce qu’elle a l’apparence de l’ordre. Un
dossier validateurs/, une classe ChaineUtils, un module helpers/ : tout y est
rangé par nature technique, ce qui donne une arborescence propre et un couplage
détestable. Le validateur d’IBAN et celui de code postal n’ont rien en commun, ne
changent jamais ensemble, et n’ont aucune raison de vivre côte à côte — sinon
qu’ils portent le même suffixe.
C’est la même erreur, à une autre échelle, que celle du découpage par couche plutôt que par fonctionnalité, signalée dans N-Tiers.
La connascence
Le mot vient de Meilir Page-Jones (1996) et signifie « naître ensemble ». La définition est presque identique à celle du couplage :
Deux éléments sont connascents si la modification de l’un impose la modification de l’autre pour que le programme reste correct.
L’apport n’est pas la définition, c’est de la rendre mesurable sur trois dimensions.
| Dimension | Question | Effet |
|---|---|---|
| Force | Est-ce facile à repérer et à corriger ? | Plus c’est fort, plus le changement est risqué |
| Degré | Combien d’éléments sont concernés ? | Une constante magique employée en 40 endroits a un degré de 40 |
| Localité | À quelle distance les éléments sont-ils ? | C’est la dimension décisive |
De là, les trois règles de Page-Jones :
- Minimiser la connascence globale, en décomposant le système.
- Minimiser la connascence qui traverse les frontières.
- Maximiser la connascence à l’intérieur des frontières.
La troisième est la contre-intuitive, et c’est la plus utile : à l’intérieur d’un module, une connascence serrée est souhaitable — c’est précisément ce qui fait que le module est une unité et non un sac. Un module dont les éléments ne sont pas connascents n’a aucune raison d’exister.
Les formes statiques
Repérables en lisant le code, de la plus faible à la plus forte.
| Forme | Les deux éléments doivent s’accorder sur… | Exemple | Remède |
|---|---|---|---|
| Nom | un nom | Appeler calculerTva | Aucun — inévitable et bénigne, l’outillage la gère |
| Type | un type | Passer un Montant | Aucun — bénigne |
| Sens | la signification d’une valeur | statut = 2 veut dire « suspendu » | Une énumération, un objet valeur |
| Position | un ordre | creer(nom, prenom), un tuple | Arguments nommés, un objet |
| Algorithme | un algorithme | Deux côtés qui calculent le même hachage ou parsent le même format | Un seul endroit qui le fait |
Les formes dynamiques
Repérables seulement à l’exécution — donc plus fortes, puisque le compilateur et la relecture ne les attrapent pas.
| Forme | Les deux éléments doivent s’accorder sur… | Exemple | Remède |
|---|---|---|---|
| Exécution | l’ordre des appels | ouvrir() avant lire() | Rendre l’état impossible : le constructeur ouvre |
| Temporalité | le moment | Compétition d’accès, délai d’attente | Synchronisation explicite, idempotence |
| Valeur | plusieurs valeurs cohérentes ensemble | Le total doit égaler la somme des lignes | Un agrégat |
| Identité | la même instance | Deux objets devant partager le même cache | La plus forte et la plus dangereuse ; à confiner |
La localité change tout
C’est le point qui justifie l’existence de la connascence comme outil, et que le mot « couplage » est incapable d’exprimer.
La même connascence est bénigne ou catastrophique selon la distance. Prenons la connascence de position — l’ordre des arguments :
| Distance entre les deux éléments | Gravité | Ce qu’on fait |
|---|---|---|
| Deux lignes voisines | Nulle | Rien |
| Deux méthodes d’une même classe | Faible | Rien |
| Deux classes d’un même module | À surveiller | Selon le degré |
| Deux modules | À supprimer | Affaiblir |
| Deux contextes délimités | Grave | Un contrat explicite |
| Deux services déployés séparément | Critique | Interdire |
D’où l’heuristique la plus directement utilisable de toute la page :
La force acceptable décroît avec la distance. Toute forme est tolérable à quelques lignes de distance ; seules les connascences de nom et de type sont tolérables à travers une frontière de déploiement.
Et le corollaire, souvent oublié : il existe deux mouvements légitimes face à une connascence gênante. L’affaiblir — convertir une connascence de position en connascence de nom — ou la rapprocher, en déplaçant les deux éléments dans le même module sans rien changer d’autre. Le second est fréquemment le moins cher, et c’est presque toujours celui auquel on ne pense pas.
L’instrument en action
La même fonction, avant et après. L’exercice consiste à nommer chaque connascence, puis à la remplacer par une plus faible.
Le bilan du remaniement, forme par forme :
| Connascence | Avant | Après |
|---|---|---|
| Algorithme — le format de date | Chez l’appelé et tous les appelants | Supprimée : le type de date porte le format |
Sens — la valeur 1 | Entre chaque appelant et l’appelé | Devenue connascence de nom : Tarif.Reduit |
| Position — l’ordre des deux dates | Deux string interchangeables sans erreur | Devenue connascence de type : l’inversion ne compile plus |
Valeur — fin après debut | Nulle part vérifiée | Confinée dans Periode, vérifiée une fois |
C’est exactement le mouvement décrit dans Entités et objets valeur sous le nom d’obsession des primitifs — et la connascence explique pourquoi ça marche, au lieu de le poser comme une bonne pratique.
La profondeur d’un module
Une dernière notion, empruntée à John Ousterhout, qui complète les deux premières en répondant à une question qu’elles ne posent pas : ce module valait-il son interface ?
Un module est profond quand il offre beaucoup de fonctionnalité derrière une interface étroite. Il est superficiel quand son interface est presque aussi grosse que ce qu’il fait.
Un module profond est un bon investissement : on apprend peu de choses — son interface — pour obtenir beaucoup. Un module superficiel est un coût net : il faut l’apprendre, et il ne cache presque rien.
L’archétype du module superficiel est la classe qui ne fait que transmettre, avec les mêmes noms et les mêmes types que ce qu’elle enveloppe. C’est précisément la couche traversante identifiée comme deuxième signal de saturation dans N-Tiers — et la profondeur donne le critère qui manquait pour la diagnostiquer sans hésiter.
Ce qui conduit à une conclusion inconfortable et juste : ajouter une couche d’abstraction n’améliore pas nécessairement la conception. Si l’abstraction ne cache rien, elle ajoute de la connascence de nom entre deux éléments de plus, et rien d’autre. C’est le même avertissement que celui posé au sujet de l’ouverture à l’extension dans SOLID, avec un instrument pour le mesurer.
Le complément historique est le critère de David Parnas (1972), qui reste le meilleur guide de découpage jamais écrit en une phrase : un module doit cacher une décision susceptible de changer. Pas une étape de traitement, pas une couche technique — une décision.
Ce que l’instrument explique
C’est ici que cette page justifie sa position dans la section : la connascence ré-explique tout ce qui précède et tout ce qui suit avec un seul vocabulaire.
| Notion | Ce qu’elle fait, en termes de connascence |
|---|---|
| Responsabilité unique | Maximiser la cohésion : un module, une raison de changer |
| Ségrégation des interfaces | Réduire la connascence de nom et de type qui traverse une frontière |
| Inversion des dépendances | Remplacer une connascence avec une implémentation par une connascence avec une abstraction |
| Strategy | Convertir une connascence de sens — un switch sur un code — en connascence de nom |
| Observer | Supprimer la connascence d’identité entre l’émetteur et ses dépendants |
| Objet valeur | Confiner une connascence d’algorithme dans un seul type |
| Agrégat | Tracer une frontière autour d’une connascence de valeur — c’est la définition même d’un invariant |
| Contexte délimité | Une frontière où l’on n’accepte plus que les connascences de nom et de type, par contrat explicite |
| Port hexagonal | Convertir une connascence avec une technologie en connascence avec une interface |
| Règle dupliquée (signal 3 du 3-tier) | Une connascence d’algorithme entre deux services, sans propriétaire |
Le tableau se lit dans les deux sens. Il donne le pourquoi de chaque patron, et il permet d’inventer le remède quand aucun patron connu ne s’applique : nommer la forme, puis chercher à l’affaiblir ou à la rapprocher.
Comment s’en servir
En revue de code. C’est l’usage le plus immédiatement rentable. « C’est couplé » n’est pas actionnable ; « il y a une connascence de sens entre le contrôleur et le dépôt, avec un degré de sept » désigne le problème, sa gravité et son remède. Nommer la forme transforme un jugement en constat.
Pour arbitrer un remaniement. L’objectif n’est jamais « supprimer le couplage », mais convertir une forme forte en forme faible, ou améliorer la localité. Formulé ainsi, le travail a un critère d’arrêt.
Pour placer une frontière. Entre deux découpages possibles, celui qui laisse passer les connascences les plus faibles est le bon. C’est la réponse opérationnelle à la question que les autres pages présupposaient résolue.
Les limites de l’outil
Il ne mesure pas la valeur. Un programme sans connascence forte serait un programme sans structure. Certaines connascences fortes sont le cœur du système — un agrégat est une connascence de valeur assumée.
Il ne dit rien du coût du remède. Affaiblir une connascence coûte presque toujours une abstraction, donc de l’indirection. L’instrument mesure le problème, pas la rentabilité de sa solution.
Il ne s’automatise pas. Aucun outil ne mesure la connascence. Les linters détectent des indices — listes de paramètres trop longues pour la position, constantes magiques pour le sens, duplication pour l’algorithme — mais la forme et la localité restent un jugement humain.
Il ne remplace pas le discernement. La connascence d’identité est classée « la pire » ; une instance de cache partagée est parfaitement légitime. Le classement est une échelle de vigilance, pas un interdit.
Références
- Stevens, Myers & Constantine, Structured Design, IBM Systems Journal, 1974 — l’origine du couplage et de la cohésion
- David Parnas, On the Criteria To Be Used in Decomposing Systems into Modules, 1972 — le critère de dissimulation d’information
- Meilir Page-Jones, What Every Programmer Should Know About Object-Oriented Design, 1996 — l’introduction de la connascence
- https://connascence.io/ — la référence en ligne, avec les formes et leurs remèdes
- John Ousterhout, A Philosophy of Software Design, 2018 — les modules profonds et superficiels
- https://www.youtube.com/watch?v=HQXVKHoUQxY — Connascence Examined, la conférence de Jim Weirich qui a fait redécouvrir la notion