Design patterns: Strategy

Mauto 3 min

Lo Strategy Pattern è un pattern comportamentale che consente di definire una famiglia di algoritmi, incapsularli in classi separate e renderli intercambiabili tra loro. L’algoritmo da utilizzare può essere selezionato dinamicamente a runtime, rendendo il codice più flessibile e facilmente estendibile. Questo pattern è particolarmente utile quando si desidera evitare lunghi blocchi condizionali (come if-else o switch-case) per scegliere tra diversi algoritmi.

Punti di Forza

  1. Separazione del codice: Ogni algoritmo è incapsulato nella propria classe, separando la logica dell’algoritmo dal contesto in cui viene usato.
  2. Intercambiabilità degli algoritmi: Gli algoritmi possono essere cambiati a runtime senza modificare il codice del client.
  3. Aumento della manutenibilità: Aggiungere o modificare algoritmi non impatta il resto del codice.
  4. Estensibilità: È facile aggiungere nuovi algoritmi implementando semplicemente una nuova classe che segue la stessa interfaccia.
  5. Favorisce la coesione: Il contesto gestisce solo la logica di alto livello, mentre le strategie si occupano dell’implementazione specifica degli algoritmi.

Caso d’uso: Sistema di pagamento

Immagina un sistema di pagamento che supporta diversi metodi: Carta di Credito, PayPal e Bonifico Bancario. A seconda del metodo di pagamento scelto dall’utente, l’algoritmo per processare il pagamento cambia.

Codice PHP

<?php
// Interfaccia Strategia di pagamento
interface PaymentStrategy {
    public function pay($amount);
}

// Strategia pagamento con Carta di Credito
class CreditCardPayment implements PaymentStrategy {
    private $cardNumber;

    public function __construct($cardNumber) {
        $this->cardNumber = $cardNumber;
    }

    public function pay($amount) {
        echo "Pagamento di $amount effettuato con Carta di Credito numero: {$this->cardNumber}.\n";
    }
}

// Strategia pagamento con PayPal
class PayPalPayment implements PaymentStrategy {
    private $email;

    public function __construct($email) {
        $this->email = $email;
    }

    public function pay($amount) {
        echo "Pagamento di $amount effettuato con PayPal all'indirizzo: {$this->email}.\n";
    }
}

// Strategia pagamento con Bonifico Bancario
class BankTransferPayment implements PaymentStrategy {
    private $bankAccount;

    public function __construct($bankAccount) {
        $this->bankAccount = $bankAccount;
    }

    public function pay($amount) {
        echo "Pagamento di $amount effettuato tramite Bonifico Bancario sul conto: {$this->bankAccount}.\n";
    }
}

// Classe Contesto per il pagamento
class PaymentContext {
    private $paymentStrategy;

    public function __construct(PaymentStrategy $paymentStrategy) {
        $this->paymentStrategy = $paymentStrategy;
    }

    public function setPaymentStrategy(PaymentStrategy $paymentStrategy) {
        $this->paymentStrategy = $paymentStrategy;
    }

    public function pay($amount) {
        $this->paymentStrategy->pay($amount);
    }
}

// Esempio d'uso
$context = new PaymentContext(new CreditCardPayment("1234-5678-9876-5432"));
$context->pay(100);  // Pagamento con Carta di Credito

$context->setPaymentStrategy(new PayPalPayment("user@example.com"));
$context->pay(200);  // Pagamento con PayPal

$context->setPaymentStrategy(new BankTransferPayment("IT60X0542811101000000123456"));
$context->pay(300);  // Pagamento con Bonifico Bancario

Spiegazione

  • Interfaccia PaymentStrategy: Definisce il metodo pay(), che ogni strategia di pagamento deve implementare.
  • Strategie Concrete (CreditCardPayment, PayPalPayment, BankTransferPayment): Implementano l’interfaccia e gestiscono l’algoritmo di pagamento per il rispettivo metodo.
  • Classe PaymentContext: Gestisce l’algoritmo di pagamento corrente e permette di cambiare strategia a runtime.
  • Uso del contesto: L’utente può cambiare il metodo di pagamento senza modificare il contesto stesso; la strategia viene semplicemente sostituita.

Conclusione

Lo Strategy Pattern separa chiaramente gli algoritmi dal contesto in cui sono utilizzati, migliorando la modularità e permettendo l’intercambiabilità degli algoritmi a runtime. Questo è particolarmente utile in sistemi complessi che richiedono una varietà di algoritmi o logiche.

TORNA ALLA GUIDA

content_copy Copiato