Aller au contenu principal

Pattern Strategy

Définissez une famille d'algorithmes, encapsulez chacun dans une fonction, et échangez-les à l'exécution.


Le Problème

Vous développez un checkout e-commerce. Les clients ont différentes réductions : les utilisateurs réguliers paient plein tarif, les membres VIP ont 20% de réduction, les codes promo donnent 15%.

L'approche naïve :

function calculatePrice(price: number, customerType: string): number {
if (customerType === "regular") return price;
if (customerType === "vip") return price * 0.8;
if (customerType === "promo") return price * 0.85;
// ... grossit avec chaque nouveau type de réduction
}

Chaque nouvelle réduction = modifier cette fonction. Ça devient un cauchemar de maintenance.


La Solution

Chaque réduction devient une fonction autonome. Choisissez la bonne par sa clé à l'exécution :

import { createStrategies } from "@pithos/core/eidos/strategy/strategy";

const pricing = createStrategies({
regular: (price: number) => price,
vip: (price: number) => price * 0.8,
promo: (price: number) => price * 0.85,
});

pricing.execute("vip", 100); // 80

Nouvelle réduction ? Ajoutez une ligne. Pas de conditions à mettre à jour.


Analogie

Une app GPS avec plusieurs options d'itinéraire : le plus rapide, le plus court, sans péages, panoramique. Chacun est un algorithme séparé, mais l'interface de navigation ne se soucie pas de celui qui est actif — elle appelle juste "calculer l'itinéraire" sur la stratégie sélectionnée.


Démo

Code
/**
* Pricing strategies using Pithos Strategy pattern.
*
* This is the core of the Strategy pattern:
* each discount rule is a standalone function, selected by key at runtime.
* No interface, no concrete classes, no context — just functions in a record.
*/
import { createStrategies } from "@pithos/core/eidos/strategy/strategy";
import type { PricingInput, PricingResult, Product } from "./types";

/** Calculate subtotal from products */
function calculateSubtotal(products: Product[]): number {
return products.reduce((sum, p) => sum + p.basePrice * p.quantity, 0);
}

/** Apply a discount percentage to a subtotal */
function applyDiscount(subtotal: number, percent: number): PricingResult {
const discount = subtotal * (percent / 100);
return {
subtotal,
discount,
total: subtotal - discount,
discountPercent: percent,
};
}

export const pricingStrategies = createStrategies({
/** Regular pricing — no discount */
regular: ({ products }: PricingInput) =>
applyDiscount(calculateSubtotal(products), 0),

/** VIP pricing — 20% off everything */
vip: ({ products }: PricingInput) =>
applyDiscount(calculateSubtotal(products), 20),

/** Promo pricing — 15% off */
promo: ({ products }: PricingInput) =>
applyDiscount(calculateSubtotal(products), 15),

/** Bulk pricing — 25% off for orders over $300 */
bulk: ({ products }: PricingInput) => {
const subtotal = calculateSubtotal(products);
return applyDiscount(subtotal, subtotal >= 300 ? 25 : 0);
},
});
Result

Quand l'Utiliser

  • Plusieurs algorithmes pour la même tâche, sélectionnés à l'exécution
  • Vous voulez ajouter de nouvelles variantes sans modifier le code existant
  • Logique conditionnelle complexe qui choisit entre des opérations similaires

API

  • createStrategies — Construire un résolveur de stratégies à partir de fonctions nommées
  • safeStrategy — Wrapper les stratégies pour retourner Result au lieu de throw
  • withFallback — Chaîner une stratégie principale avec un backup
  • withValidation — Valider l'input avant exécution