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
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.
CommandeconnaîtModeLivraisonet ignoreExpress. - Préférer la composition à l'héritage.
Commandereçoit un mode au lieu d'avoir une sous-classeCommandeExpress.
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.