Module 5 · La conception

Les principes GRASP

Depuis deux semaines, tu prends la même décision à chaque flèche que tu traces : à qui confier ce travail ? Tu la prends déjà avec la bonne question — mais sans le nom. Ce module donne les neuf noms, et c'est exactement ce que l'examen réclame : non pas décider juste, mais savoir dire pourquoi.

💡 Le concept

Le module Le diagramme de classes & l'architecture logique a posé une phrase qui gouverne toute la conception : un modèle du domaine se fait valider, un diagramme de conception se défend. Restait une question sans réponse — se défendre avec quels mots ?

GRASP est ce vocabulaire. Neuf patrons, chacun répondant à une question d'attribution, et tous nés du même constat : les bonnes conceptions se ressemblent, et les raisons qu'on invoque pour les défendre se comptent sur les doigts de deux mains. Ce ne sont pas des recettes à appliquer : ce sont les arguments recevables.

La question que chaque flèche posait

Reprends n'importe quel diagramme du module Les diagrammes d'interaction. Chaque flèche que tu y as tracée est une décision : tu as choisi qui recevrait ce travail. Le contrôleur aurait pu calculer lui-même ; tu as préféré déléguer au livre. Rien, dans la notation, ne t'y obligeait.

Cette décision est la plus lourde de toute la conception orientée objet, parce qu'elle se répète des centaines de fois et qu'aucune ne s'annule. GRASPGeneral Responsibility Assignment Software Patterns — est la liste des neuf raisons acceptables de trancher. Neuf patrons, neuf questions.

Le patronLa question à laquelle il répond
ExpertQui possède l'information nécessaire ?
CréateurQui doit instancier cette classe ?
ContrôleurQui reçoit l'événement système ?
Faible couplageComment limiter les dépendances ?
Forte cohésionComment garder une classe focalisée ?
PolymorphismeComment traiter un comportement qui varie selon le type ?
Fabrication pureOù mettre ce qu'aucune classe du métier ne veut ?
IndirectionComment éviter que deux classes se connaissent ?
Protégé des variationsComment isoler ce qu'on sait devoir changer ?

Deux précisions avant d'entrer dans le détail, et les deux tombent en examen.

Ce ne sont pas les patrons de conception. Ceux-là — Fabrique, Singleton, Observateur, Stratégie — arrivent en semaine 10, ils viennent du « Gang of Four » et ce sont des solutions nommées, avec un diagramme de classes chacune. Les GRASP sont d'un cran au-dessus : des principes d'attribution, sans structure imposée. On ne « dessine pas un Expert », on justifie une décision par Expert.

Ils s'appliquent au moment du diagramme d'interaction, pas après. C'est là qu'on choisit un destinataire pour chaque message ; le diagramme de classes ne fait qu'enregistrer le résultat. Un GRASP invoqué après coup ne sert qu'à défendre ce qu'on a déjà écrit — ce qui est exactement l'exercice de l'examen, mais pas la façon de concevoir.

Expert — celui que tu appliques déjà

L'énoncé : confier une responsabilité à la classe qui détient l'information nécessaire pour l'assumer. C'est le plus fondamental des neuf, et de loin le plus souvent invoqué en correction.

Tu l'as appliqué sans le savoir la semaine dernière. L'exercice du module Les diagrammes d'interaction demandait pourquoi un contrôleur qui lit le montant dû d'un lecteur, le décompte dans son coin et réécrit le résultat était mal conçu. La réponse — le montant dû appartient au lecteur, donc c'est lui qui doit savoir encaisser — est Expert, mot pour mot. Il ne te manquait que le nom, et c'est le nom que le correcteur cherche.

Pourquoi ça marche : parce que l'alternative consiste à promener la donnée. Chaque information qu'une classe va chercher ailleurs pour calculer elle-même coûte un message de plus sur le diagramme et une dépendance de plus dans le code. Expert n'est donc pas une préférence esthétique : c'est le patron qui réduit mécaniquement les deux choses qu'on cherche à réduire.

📖 La formule

Responsabilité, classe, patron, raison

<responsabilité> → <classe>
   par <PATRON>, parce que <raison>

   calculerSousTotal() → LigneCommande
      par EXPERT, parce qu'elle detient la
      quantite et atteint le prix du plat

   creerEmprunt() → Lecteur
      par CREATEUR, parce qu'il enregistre
      ses emprunts

La raison nomme un FAIT du diagramme : une
donnee possedee, une agregation, un
evenement recu. Jamais « c'est plus propre ».

Le gabarit de toute justification GRASP, et la forme exacte attendue sur une copie. On le reconnaît à ce qu'il tient en une ligne et demie : si la raison prend trois phrases, c'est que le patron invoqué n'est pas le bon.

La même responsabilité, deux placements
Expert ignoré — le contrôleur calcule
:ControleurCommande ──▶ :LigneCommande
il lui demande getQuantite() — une valeur brute
:ControleurCommande ──▶ :Plat
puis getPrix() au plat, et il multiplie lui-même : il connaît désormais :LigneCommande et :Plat
Expert respecté — la ligne calcule
:ControleurCommande ──▶ :LigneCommande
il lui demande calculerSousTotal() — le résultat, pas les ingrédients
:LigneCommande getPrix() ──▶ :Plat
elle seule parle au plat : le contrôleur ne connaît plus :Plat, et personne n'a transporté de donnée
Le même sous-total, calculé deux fois. En haut, le contrôleur va chercher les valeurs là où elles sont et fait l'opération ; en bas, il demande à la classe qui les possède déjà. Regarde ce qui change pour :Plat : dans le premier cas le contrôleur doit le connaître, dans le second il ignore jusqu'à son existence. Expert ne déplace pas seulement du calcul — il supprime une dépendance au passage.
🧠 Quiz éclair

Où doit vivre calculerAmende(), et quelle est la raison à écrire sur la copie ?

class Emprunt {
   private Date datePret;
   private Date dateRetourPrevue;
   private Livre livre;
}
class Lecteur {
   private List<Emprunt> emprunts;
}

Dans Emprunt, par Expert : c'est lui qui détient dateRetourPrevue, donc lui seul peut calculer le nombre de jours de retard sans rien demander à personne.

La faute tentante est Lecteur, parce que c'est « lui qui doit l'amende ». Mais un lecteur ne connaît pas les dates : il devrait les extraire de chaque emprunt pour calculer à sa place — exactement le transport de données qu'Expert évite. En revanche, Lecteur.totalAmendes() est légitime, et respecte Expert lui aussi : il détient la liste des emprunts, donc il sait faire la somme de ce que chacun lui répond.

Retiens cette paire, elle revient souvent : deux classes peuvent être expertes de deux choses différentes dans le même calcul. L'une connaît le détail, l'autre connaît l'ensemble.

Créateur — qui a le droit d'instancier

Quand tu as tracé le message «create» vers :Emprunt, la semaine dernière, tu as choisi son émetteur. Créateur dit lequel choisir. L'énoncé : confier à B la création d'un A si l'une de ces conditions est vraie — et il suffit d'une seule :

  • B contient ou agrège A — une Commande contient ses LigneCommande ;
  • B enregistre A — un RegistreEmprunts tient la liste des emprunts ;
  • B utilise A étroitement ;
  • B possède les données d'initialisation de A — c'est la condition la plus utile en pratique, parce qu'elle se vérifie en regardant le constructeur.

La première est la plus forte : quand une composition existe, la question ne se pose même pas. Le module Le modèle du domaine t'a fait tracer des losanges pleins pour dire « l'un ne vit pas sans l'autre » ; Créateur en tire la conséquence directe — celui qui contient est celui qui fabrique. Une ligne de commande n'existe pas hors de sa commande, donc rien d'autre que la commande n'a de raison de l'instancier.

Et quand plusieurs conditions désignent des classes différentes ? Larman tranche en faveur de celle qui agrège ou contient : c'est celle dont le lien durera. Les données d'initialisation, elles, ne font que passer.

🧠 Quiz éclair

Trois classes pourraient créer l'Emprunt. Laquelle, et laquelle n'a en réalité aucune condition pour elle ?

class Lecteur {
   private List<Emprunt> emprunts;   // il agrege
}
class RegistreEmprunts {
   private List<Emprunt> tous;       // il enregistre
}
class ControleurEmprunt {
   // recoit confirmerEmprunt(lecteur, livre)
}

Lecteur, et la raison est la hiérarchie des conditions : il agrège ses emprunts, ce qui est la condition la plus forte. RegistreEmprunts en a une aussi — il les enregistre —, et c'est un choix défendable si le modèle ne rattache pas les emprunts au lecteur ; sur une copie, écrire l'un ou l'autre avec sa raison passe.

ControleurEmprunt est celui qui n'a rien pour lui, et c'est le point de la question. Il ne contient pas, n'enregistre pas, n'utilise pas étroitement. Il a seulement les paramètres sous la main, ce qui ressemble à la quatrième condition — « posséder les données d'initialisation » — mais il ne les possède pas : il ne fait que les transmettre. C'est la nuance qui tranche, et elle vaut d'être retenue telle quelle.

⚠️ Piège fréquent

Laisser le contrôleur tout créer. C'est le réflexe qui vient naturellement en dessinant : le contrôleur est déjà là, il a reçu l'opération système, il a les paramètres sous la main — autant qu'il fasse les new.

Sauf qu'aucune des quatre conditions ne le désigne. Il ne contient rien, n'enregistre rien, et ne garde pas ce qu'il crée : il le passe à quelqu'un d'autre juste après. Ce « juste après » est le signal — si l'objet créé part immédiatement chez un autre, c'est cet autre qui aurait dû le créer. Et le coût est réel : chaque création confiée au contrôleur lui ajoute une dépendance qu'il n'aurait pas eue.

Contrôleur — ce que GRASP ajoute

Celui-ci, tu le connais : deux modules l'ont déjà installé, du stéréotype «controller» jusqu'au premier message de chaque diagramme d'interaction. L'énoncé : confier la réception d'un événement système à une classe qui représente soit le système entier, soit un cas d'utilisation. Ce que GRASP ajoute tient en deux choses.

Premièrement, les deux variantes ont des noms et un critère de choix. Le contrôleur de façade est une classe unique pour tout le système — Bibliotheque, SystemeDeCaisse : acceptable tant que les cas d'utilisation se comptent sur une main. Le contrôleur de cas d'utilisation en donne un par scénario — ControleurEmprunt, ControleurRetour —, et c'est le choix par défaut dès qu'un système grossit. Le critère n'est pas le goût : c'est le nombre d'opérations système que la classe finirait par porter.

Deuxièmement, le défaut a un nom officiel : le contrôleur surchargé (bloated controller). Tu l'as déjà croisé deux fois sans qu'on te le nomme, dans le quiz des stéréotypes et dans le piège du diagramme en peigne. Trois symptômes le révèlent, et le troisième est le plus révélateur : la classe reçoit des opérations venues de cas d'utilisation sans rapport ; elle exécute au lieu de déléguer ; et elle garde des attributs qui dupliquent ce que les entités savent déjà.

⚠️ Piège fréquent

Prendre le contrôleur GRASP pour une classe d'interface. Le mot est piégé : dans un cadriciel web, un « contrôleur » reçoit des requêtes HTTP et rend des pages. Ici, non. Le contrôleur GRASP est un objet de la couche application, sans aucun rapport avec l'affichage.

La conséquence pratique : il ne doit contenir aucune trace d'écran — pas de champ de saisie, pas de fenêtre en paramètre, pas de message afficher…. Une fenêtre lui parle ; lui ne parle jamais à une fenêtre. C'est la règle des couches du module Le diagramme de classes & l'architecture logique, vue depuis l'autre bout.

Couplage et cohésion, la paire qu'on confond

Ces deux-là vont ensemble dans ce module parce que c'est ensemble qu'on les confond. Ils diffèrent d'ailleurs des autres patrons par leur nature : les sept autres désignent une classe, ceux-ci jugent le résultat. Ils ne répondent pas à « qui fait quoi » mais à « est-ce que ce que je viens d'écrire tient debout ».

Faible couplage : minimiser les dépendances entre classes. Une classe est couplée à toutes celles qu'elle doit connaître pour fonctionner — celles dont elle garde une référence, celles qu'elle reçoit en paramètre, celles dont elle appelle une méthode. Plus elle en connaît, plus un changement ailleurs peut la casser.

Forte cohésion : une classe, un objectif. Une classe est cohésive quand tout ce qu'elle contient sert la même chose. Quand elle porte la logique métier, l'accès à la base, l'envoi de courriels et la génération de PDF, elle n'est plus une classe mais un dossier.

💡 Le test qui les sépare

Une seule chose à retenir, et elle tranche toutes les questions d'examen sur le sujet : le couplage se regarde en SORTANT d'une classe, la cohésion se regarde en RESTANT dedans.

Pour le couplage, tu comptes les flèches qui partent : combien d'autres classes celle-ci doit-elle connaître ? Pour la cohésion, tu ne sors pas : tu lis la liste de ses opérations et tu comptes les raisons de changer. Une classe qui change quand la base de données change, et quand le format du courriel change, et quand la règle métier change, en a trois. C'en est deux de trop.

Le même calcul, deux répartitions de dépendances
Ce que le contrôleur doit connaître, version 1
:ControleurCommande ──▶ :Commande
:ControleurCommande ──▶ :LigneCommande
:ControleurCommande ──▶ :Plat
le contrôleur atteint chaque classe du calcul : toute modification de l'une d'elles peut le casser
Version 2 — chacun ne parle qu'à son voisin
:ControleurCommande ──▶ :Commande
:Commande ──▶ :LigneCommande
:LigneCommande ──▶ :Plat
le contrôleur n'atteint plus que :Commande, et la chaîne se poursuit sans lui
Les mêmes classes et le même résultat, reliées autrement. En haut, le contrôleur va chercher lui-même chaque morceau ; en bas, il pose une question et laisse la chaîne se dérouler. Le total de flèches ne change pas — ce qui change, c'est qu'elles ne partent plus toutes du même endroit. C'est très exactement ce que « faible couplage » veut dire, et pourquoi Expert le produit tout seul.
🧠 Quiz éclair

Cette classe viole-t-elle la forte cohésion, le faible couplage, ou les deux ?

class CalculateurTaxe {
   private Commande commande;
   private LigneCommande ligne;
   private Plat plat;
   private Client client;
   private Province province;

   double calculer() { /* ... */ }
}

Le couplage seul. La cohésion est bonne : la classe ne fait qu'une chose, calculer une taxe, et elle n'a qu'une raison de changer — que la règle de taxation change. Une seule opération, un seul objectif.

Mais pour la faire, elle doit connaître la moitié du système. Chacune de ces cinq références est une porte par laquelle une modification étrangère peut entrer : renommer une méthode de Plat, et le calculateur casse.

Le remède n'est pas de la découper — ce serait soigner la cohésion, qui va déjà bien. C'est de rendre à chacun sa part du calcul, par Expert : la ligne connaît son sous-total, la commande sa somme, et le calculateur n'a plus besoin que d'un montant et d'une province. Deux dépendances au lieu de cinq, sans qu'aucune classe n'ait été créée ni scindée.

⚠️ Piège fréquent

Croire qu'il faut maximiser les deux. On lit « faible couplage » et « forte cohésion » comme deux curseurs à pousser à fond. Ils tirent alors dans des sens opposés, et le résultat est absurde dans les deux cas.

Pousse le couplage au minimum absolu : une seule classe qui contient tout le programme ne dépend de personne — couplage zéro, cohésion anéantie. Pousse la cohésion à l'extrême : une classe par opération, chacune parfaitement focalisée, et un réseau de dépendances impossible à suivre.

Ce sont des forces à équilibrer, et c'est la formulation à employer sur une copie. La bonne conception n'est pas celle qui optimise l'une des deux : c'est celle où l'on peut nommer ce qu'on a échangé contre quoi.

Polymorphisme, et là où il s'arrête

Le mécanisme, tu le connais depuis le cours de programmation : quand un comportement dépend du type de l'objet, chaque sous-classe redéfinit la méthode, et l'appelant ne teste plus rien. Ce que GRASP y ajoute est un point de vue : ce n'est pas une astuce de langage, c'est une attribution de responsabilité. Au lieu de confier la décision à un aiguilleur central qui connaît tous les cas, on la confie à chaque type, qui ne connaît que le sien.

D'où l'énoncé, et son bénéfice : ajouter un cas devient ajouter une classe, sans rouvrir le code existant. Un if/else sur un type, lui, se rouvre à chaque nouveau cas — et on oublie toujours l'un des endroits où il avait été recopié.

Le vrai sujet de cette section est ailleurs, parce que c'est là qu'on se trompe : tous les if ne sont pas des polymorphismes qui s'ignorent. Le patron vise une variation de type, et le test tient en une phrase :

💡 Le test de la borne

Un objet ne change jamais de classe pendant sa vie. Alors demande-toi si la condition porte sur quelque chose qui pourrait changer sans que l'objet cesse d'être ce qu'il est.

La province d'une commande ? Elle ne change pas : c'est une catégorie, une hiérarchie se justifie. Le total d'une commande ? Il change à chaque article ajouté : en faire un type obligerait l'objet à changer de classe en cours de route, ce qu'aucun langage objet ne permet. Une variation de valeur reste un if, et c'est très bien ainsi.

🧠 Quiz éclair

Un coéquipier veut remplacer ce if par une hiérarchie CommandeGratuite / CommandePayante, « parce que GRASP dit d'éviter les if ». Que réponds-tu ?

double fraisLivraison(Commande c) {
   if (c.total() >= 50) return 0.0;
   else                 return 7.95;
}

Non : la condition porte sur une valeur, pas sur un type. Le total d'une commande change chaque fois qu'on ajoute un article. Avec la hiérarchie proposée, une commande devrait changer de classe en franchissant 50 $ — et redevenir l'autre si on retire l'article. Aucun langage objet ne le permet, et ce serait absurde même s'il le permettait.

La bonne question n'est jamais « y a-t-il un if ? » mais « sur quoi porte-t-il ? ». Sur un type ou une catégorie stable — province, moyen de paiement, format d'export —, le polymorphisme s'applique. Sur un montant, une date, un état qui évolue, il ne s'applique pas.

Ce qui se dit tout de même, si tu veux marquer un point de plus : la valeur 50 et la valeur 7,95 codées en dur, elles, méritent une critique — mais c'est un autre sujet que GRASP.

Reste la question qui revient toujours en examen : polymorphisme, ou protégé des variations ? Les deux, et il faut le dire. Protégé des variations est l'intention — isoler ce qu'on sait devoir changer derrière une interface stable ; le polymorphisme est le mécanisme qui la réalise le plus souvent. Une copie qui nomme les deux et explique lequel sert lequel vaut mieux qu'une copie qui parie sur l'un des deux. On y revient dans la section « Indirection et protégé des variations ».

Et garde ceci pour la semaine 10 : quand le cours arrivera aux patrons du « Gang of Four », tu retrouveras exactement cette structure sous le nom de Stratégie. Le polymorphisme GRASP en est le principe ; Stratégie en est la forme nommée, avec son diagramme de classes.

Fabrication pure — inventer une classe

Les six patrons précédents désignent tous une classe qui existe déjà. Celui-ci traite le cas où aucune ne convient. L'énoncé : quand aucune classe du domaine n'est un hôte naturel pour une responsabilité, en inventer une, sans équivalent dans le monde réel, dédiée à cette tâche.

Le nom dit exactement ce qu'il faut comprendre : fabrication, parce qu'on la fabrique ; pure, parce qu'elle ne représente rien — le client ne la reconnaîtrait pas, aucun modèle du domaine ne la contient.

Le cas d'école est la persistance. Il faut ranger un Emprunt dans une base SQL. Expert désignerait Emprunt, puisque c'est lui qui détient les données — et ce serait une faute : une entité du métier qui sait parler SQL perd sa cohésion, devient impossible à tester sans base, et le module Le diagramme de classes & l'architecture logique a déjà montré que cet appel-là traverse une couche interdite. On invente donc EmpruntDAO, que tu as croisé sans savoir d'où il venait : c'est une fabrication pure, et c'est la plus fréquente de toutes.

Remarque au passage ce que ce patron vient de faire : il a arbitré contre Expert. C'est normal et c'est examinable — les GRASP ne s'appliquent pas en file indienne, ils se contredisent parfois, et une bonne copie dit lequel l'emporte et pourquoi. Ici, la cohésion des entités pèse plus lourd que la proximité des données.

La persistance n'est pas son seul emploi, et connaître les autres évite de croire que « fabrication pure » est un synonyme de « DAO ». Trois familles reviennent : la traduction d'un format vers un autre — un ConvertisseurJSON, un LecteurCSV ; la communication avec l'extérieur — une passerelle de paiement, un service de courriel ; et l'algorithme lourd qui ne tient dans aucune entité sans l'alourdir — un PlanificateurHoraire, un CalculateurItineraire. Toutes ont la même signature : une responsabilité que le métier n'a jamais eu à nommer.

D'où le critère qui autorise l'invention, et il est plus utile que la liste : une fabrication pure légitime est celle dont on peut dire ce qu'elle fait sans jamais nommer une classe du domaine. « Elle traduit un objet en JSON », « elle range des entités dans une base » — ces phrases tiennent seules, et c'est pourquoi ces classes sont réutilisables ailleurs. Une classe dont la description commence par « elle gère les emprunts » échoue au test : elle a besoin du domaine pour se définir, donc sa place était dans le domaine.

Ne le confonds pas avec Créateur, ce qui est l'erreur de vocabulaire la plus courante du chapitre : Créateur choisit qui instancie une classe qui existe ; Fabrication pure décide d'en inventer une qui n'existe pas. L'un désigne, l'autre crée.

⚠️ Piège fréquent

En faire un réflexe. La fabrication pure est le patron le plus facile à invoquer — il suffit d'inventer une classe et de lui donner un nom en « -eur ». GestionnaireEmprunt, CalculateurAmende, ProcesseurRetour : trois classes, et les entités n'ont plus rien à faire.

Le résultat porte un nom dans l'industrie : un modèle anémique. Des entités réduites à des sacs de données avec des accesseurs, et toute l'intelligence partie dans des classes en « -eur » qui se repassent les objets. C'est la négation d'Expert, appliquée à grande échelle.

La règle : la fabrication pure est un recours, pas un premier choix. Avant d'inventer, cherche l'expert naturel. On n'invente que si la responsabilité est technique — persistance, sérialisation, communication réseau — ou si l'y mettre détruirait la cohésion d'une entité.

Indirection et protégé des variations

Les deux derniers vont ensemble parce qu'ils décrivent souvent la même ligne de code, vue sous deux angles. Les séparer proprement est un point de barème.

Indirection : pour que deux classes cessent de se connaître, intercaler un intermédiaire. L'application doit envoyer des courriels et une bibliothèque externe sait le faire. Si dix classes l'appellent directement, dix classes dépendent d'un produit qu'on ne contrôle pas. On intercale ServiceCourriel : les dix parlent à lui, lui seul parle à la bibliothèque. En changer devient une modification à un seul endroit.

Protégé des variations : repérer ce qu'on sait devoir changer, et poser une interface stable autour. On sait que les moyens de paiement vont se multiplier. On définit donc Paiement avec payer(montant), et le reste du système ne parle qu'à cette interface ; ajouter un moyen n'oblige à rouvrir personne.

Ce qui les distingue : l'indirection est un GESTE, protégé des variations est une INTENTION. Intercaler une classe est un geste qu'on peut faire pour bien d'autres raisons — un cache, un journal des appels, une conversion de format —, et ce n'est alors pas du protégé des variations. Inversement, protéger des variations n'exige pas toujours d'intercaler quoi que ce soit : une hiérarchie polymorphe suffit souvent, et c'est le lien avec la section précédente.

Une façon rapide de trancher sur une copie : cherche dans l'énoncé s'il annonce un changement à venir. « Le client voudra d'autres moyens de paiement », « on changera peut-être de fournisseur » — dès qu'un futur est nommé, c'est protégé des variations, et l'indirection ou le polymorphisme n'en sont que la réalisation.

🔗 Pont — le principe qui contenait tout le reste

Protégé des variations n'est pas le neuvième patron d'une liste : c'est celui dont presque tout le cours est une application, et le voir change la façon dont les modules précédents se relisent. Le DSS traitait le système en boîte noire — l'intérieur peut changer, l'interface tient. Les paquetages publient peu et se réservent le reste. Les couches font pointer l'instable vers le stable. La visibilité privée d'un attribut protège sa représentation.

Quatre notions vues séparément, un seul principe dessous : trouver ce qui va bouger, et mettre une frontière autour. C'est aussi la raison pour laquelle ce réflexe sert bien au-delà d'un cours d'UML — une API, un format de fichier, le contrat d'un module : à chaque fois qu'on décide ce qu'on publie, on décide ce qu'on s'autorise à changer plus tard sans prévenir personne.

✍️ Exercice de lecture

Pour chaque situation, nomme le patron GRASP — et écris la raison dans la forme de la formule : responsabilité → classe, par PATRON, parce que…

(A) Une classe Voiture porte 30 méthodes :
    consommation, sauvegarde en base,
    affichage du tableau de bord, calcul
    d'assurance.

(B) Une classe Compte calcule son solde à
    partir de ses propres transactions.

(C) Une classe Cache s'intercale entre
    l'application et une base lente.

(D) Une classe FactureDAO est créée pour
    stocker des Factures : aucune classe
    métier ne voulait de cette tâche.

(E) Une Commande instancie ses LigneCommande.
Voir le corrigé

(A) Forte cohésion — violée. Quatre raisons de changer dans une seule classe. Le remède est de répartir : Voiture garde le métier, VoitureDAO la persistance (fabrication pure), l'affichage remonte à la présentation.

(B) Expert, parce que Compte détient les transactions nécessaires au calcul.

(C) Indirection, parce que le cache s'intercale pour que l'application cesse de dépendre de la base. Note que ce n'est pas protégé des variations : rien n'annonce que la base va changer, le motif est la performance.

(D) Fabrication pure, parce que la persistance est une responsabilité technique qu'aucune classe du domaine ne peut prendre sans perdre sa cohésion.

(E) Créateur, parce que Commande contient ses lignes — c'est la condition la plus forte des quatre, celle de la composition.

Citer le bon patron, sur une copie

Les questions d'examen sur GRASP prennent trois formes, et une seule compétence les couvre toutes : identifiez le patron applicable, justifiez le choix de cette classe, corrigez cette conception en citant le patron violé. Aucune ne demande de réciter un énoncé. Toutes demandent de nommer, puis de motiver.

La méthode tient en trois gestes, et elle vaut aussi bien pour analyser que pour corriger.

  1. Nomme la responsabilité, avec un verbe. « Calculer l'amende », « créer l'emprunt », « stocker les factures ». Tant que la responsabilité n'est pas nommée, il n'y a rien à attribuer — et beaucoup de copies échouent là, en parlant des classes avant de parler du travail.
  2. Demande qui détient l'information. Commence toujours par Expert : c'est la bonne réponse dans la majorité des cas, et les autres patrons se justifient le plus souvent comme des exceptions à Expert — la fabrication pure quand l'expert naturel y perdrait sa cohésion, le contrôleur quand l'événement vient de l'extérieur.
  3. Écris la raison en nommant un fait du diagramme. Une donnée possédée, une composition, un événement reçu. « Parce que c'est plus propre » ne vaut rien ; « parce qu'elle détient la quantité » vaut le point.
✍️ Exercice de lecture

Voici du code qui fonctionne parfaitement. Relève les patrons violés — il y en a plusieurs, et ils tombent en cascade — puis dis à quoi ressemblerait la correction.

class ControleurCommande {

   double calculerTotal(Commande c) {
      double total = 0;
      for (LigneCommande l : c.getLignes()) {
         Plat p = l.getPlat();
         total += p.getPrix() * l.getQuantite();
      }
      return total;
   }
}
Voir le corrigé

Expert, d'abord. Le contrôleur va chercher les lignes, puis le plat de chaque ligne, puis le prix de chaque plat — et fait l'opération lui-même. Aucune de ces données ne lui appartient.

Faible couplage, en conséquence. Pour écrire cette boucle, il a fallu connaître Commande, LigneCommande et Plat. Trois classes dont un changement peut casser le contrôleur, là où une seule aurait suffi.

Forte cohésion, enfin. Un contrôleur orchestre ; celui-ci calcule. La logique métier a quitté le métier.

La correction se fait en une phrase par classe, et c'est ce qui la rend démontrable : Plat donne son prix — il le détient ; LigneCommande calcule son sous-total — elle détient la quantité et atteint le plat ; Commande additionne les sous-totaux — elle détient ses lignes ; ControleurCommande appelle c.calculerTotal() et rien d'autre. Quatre fois Expert, et le couplage tombe tout seul.

Ce qu'il faut voir au-delà du corrigé : les trois violations n'en font qu'une. Ignorer Expert produit mécaniquement le couplage et détruit la cohésion. C'est pour ça qu'Expert se traite en premier — les deux autres se réparent d'elles-mêmes.

⚠️ Piège fréquent

Nommer trois patrons pour se couvrir. Devant une question à un seul point, on écrit « Expert, faible couplage et forte cohésion » en espérant que l'un des trois tombe juste. Un correcteur lit ça comme ce que c'est : l'aveu qu'on ne sait pas lequel.

La nuance qui compte : citer deux patrons est bon quand ils s'articulent, mauvais quand on les juxtapose. « Protégé des variations, réalisé par polymorphisme » est une réponse complète — l'intention et le mécanisme, reliés par réalisé par. « Polymorphisme, indirection, protégé des variations » est une liste, et une liste ne montre aucun raisonnement.

La règle de sécurité, quand tu hésites vraiment : choisis-en un, et écris la raison. Un patron discutable avec une raison juste rapporte presque toujours plus que trois noms sans raison.

🎓 À l'examen

Cette matière est celle de la semaine 6, évaluée à l'examen intra du 24 octobre — la révision de la semaine 7 porte sur les modules 1 à 5 du cours, dont celui-ci. C'est aussi la dernière matière enseignée avant la remise du Livrable 2 — Modèle de conception et Architecture logique, le 13 octobre (10 %) : ce sont ces neuf patrons qui te permettront de défendre les choix de ton modèle plutôt que de les subir. Chez Larman, ce sont les chapitres 17-18 et 25-26.

IFT-2007 n'autorise aucun matériel. Les neuf noms et leur question doivent donc être en tête — mais surtout ce qui départage les paires qu'on confond, car c'est là que les points se perdent : couplage contre cohésion, Créateur contre fabrication pure, polymorphisme contre protégé des variations.

« Pour chacune de ces responsabilités, indiquez la classe qui doit la recevoir et justifiez par un patron GRASP. »

La forme la plus courante, et elle se traite mécaniquement avec la formule : responsabilité → classe, par PATRON, parce que… Une ligne par responsabilité, pas un paragraphe. Le correcteur cherche trois choses dans chaque ligne, et rien d'autre : la classe, le nom du patron, un fait qui le justifie.

Le réflexe qui rapporte : commencer par se demander qui détient l'information. Si la réponse existe, c'est Expert et c'est fini. Sinon, c'est qu'on est dans une exception — un événement venu de l'extérieur (Contrôleur), une tâche technique (Fabrication pure), une création (Créateur).

« Identifiez les violations GRASP dans cette conception, puis corrigez-la. »

Cherche d'abord Expert : une classe qui manipule des données qu'elle ne possède pas. Elle se repère à un signe visible sans réfléchir — une suite d'appels get… vers d'autres objets, suivie d'un calcul.

Ensuite, ne relève pas les autres violations comme si elles étaient indépendantes : dis qu'elles en découlent. Le couplage augmente parce que la classe a dû connaître tout le monde pour aller chercher ces données ; la cohésion tombe parce qu'elle fait un travail qui n'est pas le sien. Une copie qui montre la cascade vaut mieux qu'une copie qui énumère.

« Quelle est la différence entre le patron Créateur et la Fabrication pure ? »

En une phrase : Créateur choisit, parmi les classes existantes, laquelle instancie une autre ; Fabrication pure décide d'inventer une classe qui n'existe dans aucun modèle du domaine. L'un désigne, l'autre crée.

Ajoute un exemple de chaque, c'est ce qui distingue une réponse apprise d'une réponse comprise : une Commande crée ses LigneCommande parce qu'elle les contient (Créateur) ; un EmpruntDAO est inventé parce que la persistance n'a d'hôte naturel nulle part (Fabrication pure).

À retenir

GRASP ne t'apprend pas à concevoir : il te donne les mots pour défendre ce que tu conçois déjà. Neuf patrons, neuf questions d'attribution, et un seul qui répond dans la majorité des cas — Expert, qui confie le travail à qui détient l'information. Les autres se comprennent mieux comme ses exceptions : le Contrôleur pour ce qui vient de l'extérieur, Créateur pour ce qui s'instancie, la Fabrication pure quand aucun expert naturel ne peut prendre la tâche sans y perdre sa cohésion.

Deux d'entre eux ne désignent personne et servent à juger : le couplage se mesure en sortant d'une classe, la cohésion en restant dedans, et ils s'équilibrent au lieu de se maximiser. Les deux derniers se confondent avec le polymorphisme et méritent l'attention qu'on donne à une paire piégeuse : l' indirection est un geste, protégé des variations une intention — et cette intention-là est le principe dont les couches, les paquetages et la boîte noire du DSS étaient déjà des applications sans le dire.

Et ailleurs : la question « qui devrait porter cette responsabilité ? » ne disparaît jamais, et elle ne concerne pas que des classes. Elle se pose entre deux fonctions, entre deux modules, entre deux services d'une architecture, entre deux équipes. Le raisonnement est identique : celui qui détient l'information fait le travail, on n'expose que ce qui devra tenir, et on met une frontière autour de ce qu'on sait devoir changer. Savoir nommer ces raisons, c'est pouvoir défendre une décision technique devant quelqu'un qui l'aurait prise autrement — ce qui est, à peu de chose près, la définition du métier.

🗂️ L'aide-mémoire
Le patron à essayer en premier, devant n'importe quelle attribution
Expert : la classe qui détient l'information fait le travail. C'est la bonne réponse dans la majorité des cas, et les autres patrons se justifient comme ses exceptions
Les quatre conditions de Créateur, et celle qui l'emporte
B contient/agrège A · B enregistre A · B utilise A étroitement · B possède les données d'initialisation de A. En cas de conflit, celle qui contient gagne
Ce qui distingue le couplage de la cohésion
le couplage se compte en sortant d'une classe (combien d'autres doit-elle connaître) ; la cohésion se compte en restant dedans (combien de raisons de changer)
Ce qu'on répond quand on demande de « maximiser » les deux
qu'ils s'équilibrent : couplage minimal absolu = une seule classe qui fait tout ; cohésion maximale absolue = une classe par opération et un réseau illisible
Les trois symptômes du contrôleur surchargé
il reçoit des opérations de cas d'utilisation sans rapport · il exécute au lieu de déléguer · il garde des attributs que les entités possèdent déjà
Quand le polymorphisme ne s'applique PAS
quand la condition porte sur une valeur qui évolue — un total, une date, un état — et non sur un type. Un objet ne change jamais de classe pendant sa vie
Ce qui distingue Créateur de Fabrication pure
Créateur désigne laquelle des classes existantes instancie ; Fabrication pure invente une classe qui n'est dans aucun modèle du domaine
Ce qui distingue Indirection de Protégé des variations
l'indirection est le geste (intercaler un intermédiaire), protégé des variations l'intention (isoler ce qu'on sait devoir changer). Un futur annoncé dans l'énoncé désigne le second
Le seul patron qui arbitre contre Expert, et à quelle condition
Fabrication pure, quand la responsabilité est technique — persistance, sérialisation, réseau — et que l'expert naturel y perdrait sa cohésion
Le symptôme d'un abus de fabrication pure
le modèle anémique : des entités réduites à des données avec des accesseurs, et toute l'intelligence partie dans des classes en « -eur »
La forme d'une justification qui vaut le point
responsabilité → classe, par PATRON, parce que <fait du diagramme>. Un fait, pas une préférence : une donnée possédée, une composition, un événement reçu