Menu
Coddy logo textTech

Wzorzec Strategy

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 95 z 104.

Wzorzec strategii definiuje rodzinę algorytmów, hermetyzuje każdy z nich i umożliwia ich wzajemną wymianę. Pozwala to zmieniać zachowanie obiektu w czasie działania bez modyfikowania jego kodu — algorytm zmienia się niezależnie od klientów, którzy z niego korzystają.

Wzorzec składa się z trzech części: interfejsu strategii deklarującego metodę algorytmu, konkretnych strategii implementujących różne warianty oraz kontekstu, który korzysta ze strategii:

#include <iostream>
#include <memory>

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

// Konkretne strategie
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";
    }
};

// Kontekst
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);
}

Obiekt ShoppingCart nie wie, z jakiej metody płatności korzysta — po prostu wywołuje pay() na aktualnie ustawionej strategii. Możesz zmieniać strategie w czasie działania programu za pomocą setPaymentMethod(), dzięki czemu system jest elastyczny i łatwo go rozszerzać o nowe opcje płatności.

Użyj wzorca Strategia, gdy masz wiele algorytmów do wykonania określonego zadania i chcesz dynamicznie przełączać się między nimi lub gdy chcesz uniknąć instrukcji warunkowych służących do wyboru zachowania.

challenge icon

Wyzwanie

Łatwy

Stwórzmy Kalkulator kosztów wysyłki, który wykorzystuje wzorzec Strategia do obliczania kosztów dostawy na podstawie różnych metod wysyłki. To praktyczny scenariusz, w którym trzeba zmieniać algorytmy w czasie działania programu: tę samą paczkę można wysłać transportem lądowym, lotniczym lub ekspresowym, a każda z tych metod ma własną logikę naliczania opłat.

Rozmieścisz kod w trzech plikach:

  • ShippingStrategy.h: Zdefiniuj interfejs strategii i konkretne strategie wysyłki.

    Utwórz abstrakcyjną klasę ShippingStrategy z czysto wirtualną metodą calculateCost(double weight), która zwraca koszt wysyłki jako wartość typu double, oraz wirtualnym destruktorem.

    Następnie zaimplementuj trzy konkretne strategie:

    • GroundShipping: koszt wynosi 1.5 za jednostkę masy (weight * 1.5)
    • AirShipping: koszt wynosi 4.0 za jednostkę masy (weight * 4.0)
    • ExpressShipping: koszt wynosi 6.5 za jednostkę masy plus stała opłata 10.0 (weight * 6.5 + 10.0)
  • ShippingService.h: Utwórz klasę kontekstu, która korzysta ze strategii wysyłki.

    Klasa ShippingService powinna przechowywać std::unique_ptr<ShippingStrategy> jako prywatne pole. Zaimplementuj:

    • Metodę setStrategy(std::unique_ptr<ShippingStrategy> strategy), która zmienia metodę wysyłki
    • Metodę calculateShipping(double weight), która korzysta z bieżącej strategii, aby obliczyć i zwrócić koszt

    Jeśli podczas wywołania calculateShipping nie ustawiono żadnej strategii, zwróć 0.0.

  • main.cpp: Zademonstruj zmianę strategii w czasie działania programu.

    Wczytaj dwa dane wejściowe:

    1. Masę paczki (double)
    2. Metodę wysyłki: ground, air lub express

    Utwórz obiekt ShippingService i ustaw odpowiednią strategię na podstawie wybranej metody. Oblicz i wypisz koszt wysyłki.

    Następnie przełącz się na inną strategię (użyj air, jeśli podana metoda nie była air; w przeciwnym razie użyj ground) i ponownie oblicz koszt dla tej samej masy. Zademonstrujesz w ten sposób zaletę zmieniania strategii w czasie działania programu.

    Wypisz każdy koszt w osobnym wierszu, z dokładnością do jednego miejsca po przecinku i z nazwą metody na początku:

    [Method]: $[cost]

Na przykład dla danych wejściowych 5.0 i ground:

Ground: $7.5
Air: $20.0

Dla danych wejściowych 3.0 i express:

Express: $29.5
Air: $12.0

Dla danych wejściowych 10.0 i air:

Air: $40.0
Ground: $15.0

Zauważ, że ShippingService nie musi znać szczegółów poszczególnych algorytmów naliczania opłat. Po prostu przekazuje obliczenia do aktualnie ustawionej strategii. Możesz łatwo dodać nowe metody wysyłki (takie jak dostawa dronem lub dostawa tego samego dnia) bez wprowadzania jakichkolwiek zmian w klasie usługi.

Spróbuj swoich sił

#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;
    
    // Ustaw wyświetlanie wyniku z 1 miejscem po przecinku
    std::cout << std::fixed << std::setprecision(1);
    
    ShippingService service;
    
    // TODO: Na podstawie wybranej metody ("ground", "air" lub "express"):
    // 1. Ustaw odpowiednią strategię w obiekcie service
    // 2. Oblicz i wyświetl koszt w formacie: "[Method]: $[cost]"
    
    // TODO: Zmień strategię na inną:
    // - Jeśli wprowadzono "air", przełącz na GroundShipping
    // - W przeciwnym razie przełącz na AirShipping
    // Oblicz i wyświetl nowy koszt
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online