Pattern Strategy
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 95 di 104.
Il pattern Strategy definisce una famiglia di algoritmi, incapsula ciascuno di essi e li rende intercambiabili. Questo ti permette di cambiare il comportamento di un oggetto in fase di esecuzione senza modificarne il codice: l’algoritmo varia indipendentemente dai client che lo utilizzano.
Il pattern è composto da tre parti: un’interfaccia Strategy che dichiara il metodo dell’algoritmo, le Strategie concrete che implementano diverse varianti e un Contesto che utilizza una strategia:
#include <iostream>
#include <memory>
// Interfaccia della strategia
class PaymentStrategy {
public:
virtual void pay(int amount) = 0;
virtual ~PaymentStrategy() = default;
};
// Strategie concrete
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";
}
};
// Contesto
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);
}ShoppingCart non sa quale metodo di pagamento sta usando: chiama semplicemente pay() sulla strategia impostata. Puoi sostituire le strategie durante l'esecuzione con setPaymentMethod(), rendendo il sistema flessibile e facile da estendere con nuove opzioni di pagamento.
Usa Strategy quando hai più algoritmi per un'attività specifica e vuoi passare dinamicamente da uno all'altro, oppure quando vuoi evitare istruzioni condizionali per selezionare il comportamento.
Sfida
FacileCreiamo una calcolatrice delle spese di spedizione che usa il pattern Strategy per calcolare i costi di consegna in base ai diversi metodi di spedizione. È uno scenario pratico in cui devi poter sostituire gli algoritmi durante l'esecuzione: lo stesso pacco potrebbe essere spedito via terra, via aerea o con spedizione express, ciascuna con la propria logica di tariffazione.
Organizzerai il codice in tre file:
ShippingStrategy.h: definisci l'interfaccia della strategia e le strategie concrete di spedizione.Crea una classe astratta
ShippingStrategycon un metodo virtuale purocalculateCost(double weight)che restituisce il costo di spedizione come double, insieme a un distruttore virtuale.Poi implementa tre strategie concrete:
GroundShipping: costa1.5per unità di peso (weight * 1.5)AirShipping: costa4.0per unità di peso (weight * 4.0)ExpressShipping: costa6.5per unità di peso, più una tariffa fissa di10.0(weight * 6.5 + 10.0)
ShippingService.h: crea la classe contesto che utilizza una strategia di spedizione.La tua classe
ShippingServicedovrebbe contenere unstd::unique_ptr<ShippingStrategy>come membro privato. Implementa:- Un metodo
setStrategy(std::unique_ptr<ShippingStrategy> strategy)per cambiare il metodo di spedizione - Un metodo
calculateShipping(double weight)che utilizza la strategia corrente per calcolare e restituire il costo
Se non è impostata alcuna strategia quando viene chiamato
calculateShipping, restituisci0.0.- Un metodo
main.cpp: mostra come cambiare strategia durante l'esecuzione.Leggi due input:
- Peso del pacco (double)
- Metodo di spedizione:
ground,airoexpress
Crea un
ShippingServicee imposta la strategia appropriata in base al metodo inserito. Calcola e stampa il costo di spedizione.Poi passa a una strategia diversa (usa
airse l'input non eraair, altrimenti usaground) e calcola di nuovo il costo per lo stesso peso. Questo dimostra i vantaggi di poter sostituire le strategie durante l'esecuzione.Stampa ogni costo su una riga separata con esattamente una cifra decimale, preceduto dal nome del metodo:
[Method]: $[cost]
Per esempio, con gli input 5.0 e ground:
Ground: $7.5
Air: $20.0Con gli input 3.0 e express:
Express: $29.5
Air: $12.0Con gli input 10.0 e air:
Air: $40.0
Ground: $15.0Nota come ShippingService non debba conoscere i dettagli di ciascun algoritmo di tariffazione. Si limita a delegare alla strategia impostata in quel momento. Puoi aggiungere facilmente nuovi metodi di spedizione (come la consegna con drone o in giornata) senza modificare affatto la classe del servizio.
Provalo tu
#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;
// Imposta l'output a 1 cifra decimale
std::cout << std::fixed << std::setprecision(1);
ShippingService service;
// TODO: In base al metodo inserito ("ground", "air" o "express"):
// 1. Imposta la strategia appropriata nel servizio
// 2. Calcola e stampa il costo nel formato: "[Method]: $[cost]"
// TODO: Passa a una strategia diversa:
// - Se il valore inserito era "air", passa a GroundShipping
// - Altrimenti, passa a AirShipping
// Calcola e stampa il nuovo costo
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti10Panoramica della STL
Panoramica e filosofia della STLContenitori STLIteratoriAlgoritmi STLFuntori ed espressioni lambdaRiepilogo - Frequenza delle parole13Design Pattern Parte 1
Introduzione ai Design PatternPattern SingletonFactory e Abstract FactoryPattern BuilderPattern ObserverPattern Strategy2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online