Aide-mémoire, Head First Design Patterns, chapitre 1

Strategy

Plusieurs façons de faire la même chose, rangées derrière un même contrat. Le code client reçoit l'une d'elles et s'en sert sans savoir laquelle.

« Définir une famille d'algorithmes, encapsuler chacun d'eux et les rendre interchangeables. Strategy laisse l'algorithme varier indépendamment des clients qui l'utilisent. » Gamma, Helm, Johnson et Vlissides, Design Patterns, 1994

Strategy appliqué aux modes de livraison Commande utilise l'interface ModeLivraison. Standard, Express et Relais implémentent cette interface. Un nouveau mode, Coursier, s'ajouterait sans modifier Commande. Commande recapitulatif(mode) «interface» ModeLivraison frais(sousTotal): number delaiJours(): number Standard 490 centimes 3 jours Express 990 + 5 % 1 jour Relais 290 centimes 4 jours Coursier demain ? Le client.Il appelle mode.frais()sans savoir qui répond. utilise Le contrat.La mêmequestionpour tous. implémentent Les variantes.Même contrat, chacunesa règle de calcul. Un mode de plus ? Une classe de plus.Commande ne change pas.

Les trois rôles

Le client, Commande
Il dépend du contrat et appelle mode.frais(...). Il ne teste pas le nom du mode.
Le contrat, ModeLivraison
Toutes les variantes répondent à la même question, avec les mêmes signatures.
Les variantes, Standard, Express, Relais
Chacune garde ses chiffres et sa règle de calcul.

Le choix de la variante se fait hors du client, au moment de l'appel : commande.recapitulatif(new Relais()).

Le reconnaître

Une stratégie est en place quand une variante peut en remplacer une autre sans toucher au client. Des petites classes qui font des travaux différents, comme imprimer une facture et décompter le stock, relèvent d'un autre principe, le découpage en responsabilités.

Le signal le plus courant est un switch sur le nom du mode, répété dans plusieurs méthodes. Le code de départ de la boutique en avait deux, dans fraisLivraison et delaiJours. Avec une seule variante, ou une liste qui ne bouge pas, un if reste plus simple à lire.

Classe ou fonction

Quand le contrat tient en une opération, un type de fonction suffit : type Frais = (sousTotal: number) => number. Avec deux opérations, les frais et le délai, un objet les garde ensemble. Il peut venir d'une classe ou d'un littéral { frais, delaiJours }.

Angular et NestJS gardent des classes, parce que leur injection de dépendances fournit des instances.

Les trois principes du chapitre

  • Isoler ce qui varie. Les règles propres à chaque mode sortent de Commande.
  • Programmer pour une interface. Commande connaît ModeLivraison et ignore Express.
  • Préférer la composition à l'héritage. Commande reçoit un mode au lieu d'avoir une sous-classe CommandeExpress.

Dans le code web

Passport.js nomme « stratégies » ses méthodes de connexion. L'application enregistre new LocalStrategy(...) ou new GoogleStrategy(...) avec passport.use, et ses routes appellent passport.authenticate de la même façon pour toutes.

Un composant qui reçoit sa fonction de formatage en prop, comme format={formatEuros}, applique la même idée.

À vous

Dans produits.sort((a, b) => a.prix - b.prix), où est la stratégie, et quel code joue le rôle de Commande ?

Voir la réponse

La stratégie est la fonction de comparaison (a, b) => a.prix - b.prix. La méthode sort joue le rôle de Commande : elle appelle cette fonction sans savoir comment elle compare. Une autre fonction trie autrement sans changer sort.