Version sans JavaScript, en lecture seule : les ateliers, les quiz et l’exécution du code n’y sont pas. Version complète · Catalogue · Se connecter
Support de cours · chapitre 8
Couplage, cohésion, God Object
Le couplage et la cohésion, deux critères pour évaluer une conception, et le God Object, qui ne respecte ni l'un ni l'autre.
Ce chapitre prolonge les séquences 5 et 6.
Pourquoi s'intéresser à l'architecture
Un projet ne se réduit pas à un ensemble de classes qui compilent. On attend aussi du code les qualités suivantes.
- Maintenabilité : il y aura toujours des bugs, et un code clair est plus facile à corriger.
- Réutilisabilité : un module de vol doit pouvoir servir ailleurs.
- Évolutivité : quand on vous demandera le « CanardSolaire 3000 », il ne faudra pas avoir à tout reprendre.
Couplage
Le couplage mesure à quel point deux classes dépendent l'une de l'autre. Quand il est fort, on ne peut pas modifier l'une sans casser l'autre. Le chapitre 5 traite le sujet en détail ; en voici un résumé.
class Dresseur {
private CanardColvert canard; // couplage fort
Dresseur(CanardColvert c) { this.canard = c; }
void entrainer() { canard.voler(); }
}
class Dresseur {
private ICanard canard; // couplage faible
Dresseur(ICanard c) { this.canard = c; }
void entrainer() { canard.voler(); }
}
On repère le couplage en regardant les import et les new. Une classe qui importe quinze types concrets et en instancie six dépend de vingt et un choix faits ailleurs.
Cohésion
La cohésion mesure l'unité de responsabilité d'une classe. Une classe fortement cohésive a une seule responsabilité, et toutes ses méthodes y contribuent.
class Canard {
private String nom;
public void voler() { }
public void nager() { }
public void chanter() { }
public void sauvegarderEnXML() { } // persistance
public void envoyerFacture() { } // facturation
public void calculerImpot() { } // comptabilité
}
Ce canard vole, nage, chante, se sauvegarde en XML, envoie des factures et calcule des impôts. Une telle classe, qui accumule des responsabilités sans rapport entre elles, s'appelle un God Object.
La solution passe par la composition : chaque responsabilité est placée dans sa propre classe, et le canard les combine.
class Canard {
private String nom;
private VolStrategy vol;
private ChantStrategy chant;
public void effectuerVol() { vol.voler(); }
public void effectuerChant() { chant.chanter(); }
}
La persistance est déplacée dans un DepotCanard, la facturation dans un service. Chaque classe peut alors être testée séparément.
Les principes SOLID
Vous avez vu ces principes l'an dernier. Deux d'entre eux sont importants pour ce module ; les trois autres sont simplement à connaître.
SRP : une seule raison de changer. La classe Canard doit être modifiée quand le comportement d'un canard change, et non quand le format XML change. S'il y a deux raisons de changer, il faut deux classes.
LSP : une sous-classe doit pouvoir remplacer sa classe mère. L'exemple classique ne fait pas intervenir de canard :
class Rectangle {
protected int largeur, hauteur;
void setLargeur(int l) { largeur = l; }
void setHauteur(int h) { hauteur = h; }
}
class Carre extends Rectangle {
@Override void setLargeur(int l) { largeur = l; hauteur = l; }
@Override void setHauteur(int h) { largeur = h; hauteur = h; }
}
Un code qui fixe la largeur à 5 puis la hauteur à 4 et attend une aire de 20 obtient 16. En géométrie, un carré est un rectangle, mais il ne respecte pas le contrat de cette classe Rectangle. Le LSP est violé : l'héritage est mal choisi, comme dans les cas étudiés au chapitre 4.
O, I, D désignent le principe ouvert/fermé, la ségrégation des interfaces et l'inversion des dépendances. Le chapitre 5 en applique deux sans les nommer. Comme S et L, ils sont au socle (N13) : l'épreuve donne un code, et demande de nommer le principe violé et de dire ce que coûtera la prochaine évolution.
KISS, DRY, YAGNI
KISS : Keep It Simple. Écrire un CanardFactorySingletonManagerProxy pour créer un canard est excessif quand new Canard("coincoin") suffit.
DRY : Don't Repeat Yourself. Du code copié-collé devra être corrigé à chaque endroit au premier bug.
void afficherCanard(Canard c) { System.out.println(c.getNom() + " : coin !"); }
void afficherCanardColvert(CanardColvert c) { System.out.println(c.getNom() + " : coin !"); }
void afficherCanard(ICanard c) { System.out.println(c.getNom() + " : coin !"); }
YAGNI : You Aren't Gonna Need It. Une interface ajoutée « au cas où », une fabrique pour une seule implémentation ou une couche d'abstraction qui n'abstrait rien ajoutent de la complexité sans rien apporter.
Question d'épreuve
Qu'est-ce qui distingue le couplage de la cohésion ?
Le couplage est une propriété qui concerne plusieurs classes : il mesure à quel point l'une dépend de l'autre. La cohésion est une propriété d'une seule classe : elle mesure à quel point ses méthodes servent une même responsabilité. On cherche un couplage faible et une cohésion forte.