Menu
Coddy logo textTech

Modèle Strategy

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 95 sur 104.

Le pattern Strategy définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. Cela vous permet de modifier le comportement d’un objet à l’exécution sans modifier son code : l’algorithme varie indépendamment des clients qui l’utilisent.

Le modèle se compose de trois parties : une interface Strategy déclarant la méthode de l’algorithme, des stratégies concrètes implémentant différentes variantes, et un contexte qui utilise une stratégie :

#include <iostream>
#include <memory>

// Interface Strategy
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Stratégies concrètes
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via Credit Card\n";
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via PayPal\n";
    }
};

// Contexte
class ShoppingCart {
    std::unique_ptr<PaymentStrategy> strategy;
public:
    void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
        strategy = std::move(s);
    }
    
    void checkout(int total) {
        if (strategy) strategy->pay(total);
    }
};

int main() {
    ShoppingCart cart;
    
    cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
    cart.checkout(100);
    
    cart.setPaymentMethod(std::make_unique<PayPalPayment>());
    cart.checkout(50);
}

Le ShoppingCart ne sait pas quelle méthode de paiement il utilise : il appelle simplement pay() sur la stratégie configurée. Vous pouvez remplacer les stratégies à l’exécution avec setPaymentMethod(), ce qui rend le système flexible et facile à étendre avec de nouvelles options de paiement.

Utilisez Strategy lorsque vous disposez de plusieurs algorithmes pour une tâche spécifique et que vous souhaitez basculer dynamiquement entre eux, ou lorsque vous voulez éviter les instructions conditionnelles pour sélectionner un comportement.

challenge icon

Défi

Facile

Construisons un calculateur de frais d'expédition qui utilise le modèle Strategy pour calculer les coûts de livraison en fonction de différentes méthodes d'expédition. Il s'agit d'un scénario pratique dans lequel vous devez remplacer des algorithmes à l'exécution : le même colis peut être expédié par voie terrestre, aérienne ou express, chacune de ces méthodes ayant sa propre logique tarifaire.

Vous organiserez votre code dans trois fichiers :

  • ShippingStrategy.h : définir votre interface de stratégie et les stratégies d'expédition concrètes.

    Créez une classe abstraite ShippingStrategy avec une méthode virtuelle pure calculateCost(double weight) qui renvoie le coût d'expédition sous forme de double, ainsi qu'un destructeur virtuel.

    Implémentez ensuite trois stratégies concrètes :

    • GroundShipping : coûte 1.5 par unité de poids (weight * 1.5)
    • AirShipping : coûte 4.0 par unité de poids (weight * 4.0)
    • ExpressShipping : coûte 6.5 par unité de poids, plus des frais fixes de 10.0 (weight * 6.5 + 10.0)
  • ShippingService.h : créer la classe de contexte qui utilise une stratégie d'expédition.

    Votre classe ShippingService doit contenir un std::unique_ptr<ShippingStrategy> en tant que membre privé. Implémentez :

    • Une méthode setStrategy(std::unique_ptr<ShippingStrategy> strategy) pour modifier la méthode d'expédition
    • Une méthode calculateShipping(double weight) qui utilise la stratégie actuelle pour calculer et renvoyer le coût

    Si aucune stratégie n'est définie lorsque calculateShipping est appelée, renvoyez 0.0.

  • main.cpp : montrer le changement de stratégie à l'exécution.

    Lisez deux entrées :

    1. Poids du colis (double)
    2. Méthode d'expédition : ground, air ou express

    Créez un ShippingService et définissez la stratégie appropriée en fonction de la méthode saisie. Calculez et affichez le coût d'expédition.

    Passez ensuite à une autre stratégie (utilisez air si l'entrée n'était pas air, sinon utilisez ground) et calculez à nouveau le coût pour le même poids. Cela démontre la puissance du remplacement des stratégies à l'exécution.

    Affichez chaque coût sur sa propre ligne avec exactement une décimale, précédé du nom de la méthode :

    [Method]: $[cost]

Par exemple, avec les entrées 5.0 et ground :

Ground: $7.5
Air: $20.0

Avec les entrées 3.0 et express :

Express: $29.5
Air: $12.0

Avec les entrées 10.0 et air :

Air: $40.0
Ground: $15.0

Remarquez que ShippingService n'a pas besoin de connaître les détails de chaque algorithme tarifaire. Il délègue simplement à la stratégie actuellement définie. Vous pouvez facilement ajouter de nouvelles méthodes d'expédition (comme la livraison par drone ou la livraison le jour même) sans modifier la classe de service.

Essayez vous-même

#include <iostream>
#include <string>
#include <iomanip>
#include <memory>
#include "ShippingStrategy.h"
#include "ShippingService.h"

int main() {
    double weight;
    std::string method;
    
    std::cin >> weight;
    std::cin >> method;
    
    // Définir la sortie à 1 décimale
    std::cout << std::fixed << std::setprecision(1);
    
    ShippingService service;
    
    // TODO: En fonction de la méthode d'entrée ("ground", "air", or "express") :
    // 1. Définir la stratégie appropriée sur le service
    // 2. Calculer et afficher le coût au format : "[Method]: $[cost]"
    
    // TODO: Passer à une stratégie différente :
    // - Si l'entrée était "air", passer à GroundShipping
    // - Sinon, passer à AirShipping
    // Calculer et afficher le nouveau coût
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet

Entraînez-vous par vous-même : Compilateur C++ en ligne