Strategy Pattern
- Type : Comportemental
- Difficulté : 5/10
- Définition succincte : Le Strategy Pattern est un design pattern comportemental qui permet de définir une famille d'algorithmes, de les encapsuler dans des classes distinctes, et de les rendre interchangeables. Ce pattern permet de choisir dynamiquement un algorithme ou une stratégie spécifique à utiliser au moment de l'exécution, sans modifier le code du client. Cela permet une plus grande flexibilité et facilite l'ajout ou la modification d'algorithmes sans impacter le reste du système.
Objectif du Strategy Pattern
L'objectif principal du Strategy Pattern est de séparer la logique métier des algorithmes spécifiques, permettant ainsi de changer facilement d'algorithme ou de stratégie sans toucher au code existant. Il est particulièrement utile dans les situations où plusieurs algorithmes différents peuvent être appliqués à un même problème, et où le choix de l'algorithme dépend de certaines conditions.
Structure du Strategy Pattern
- Strategy : Interface ou classe abstraite qui définit une méthode commune pour tous les algorithmes.
- ConcreteStrategy : Implémentation concrète d'une stratégie spécifique.
- Context : La classe qui utilise une stratégie et délègue son exécution à la stratégie choisie.
Implémentation avec Laravel
Imaginons que tu développes une application Laravel qui gère plusieurs méthodes de paiement, comme PayPal, Stripe, et virement bancaire. Chaque méthode de paiement a une logique spécifique, mais l'application doit pouvoir choisir dynamiquement la méthode de paiement appropriée en fonction de l'utilisateur. Le Strategy Pattern permet de modéliser chaque méthode de paiement comme une stratégie distincte et de les appliquer dynamiquement.
a) Créer l'interface PaymentStrategy (Strategy)
L'interface PaymentStrategy définit les méthodes que toutes les stratégies de paiement doivent implémenter.
// app/Contracts/PaymentStrategy.php
namespace App\Contracts;
interface PaymentStrategy
{
public function pay(float $amount): string;
}
b) Implémenter des stratégies de paiement concrètes (ConcreteStrategy)
Chaque méthode de paiement sera une implémentation de l'interface PaymentStrategy.
1. Stratégie PayPal
// app/Services/Payments/PayPalStrategy.php
namespace App\Services\Payments;
use App\Contracts\PaymentStrategy;
class PayPalStrategy implements PaymentStrategy
{
public function pay(float $amount): string
{
// Logique spécifique à PayPal
return "Paid {$amount} using PayPal.";
}
}
2. Stratégie Stripe
// app/Services/Payments/StripeStrategy.php
namespace App\Services\Payments;
use App\Contracts\PaymentStrategy;
class StripeStrategy implements PaymentStrategy
{
public function pay(float $amount): string
{
// Logique spécifique à Stripe
return "Paid {$amount} using Stripe.";
}
}
3. Stratégie Virement Bancaire
// app/Services/Payments/BankTransferStrategy.php
namespace App\Services\Payments;
use App\Contracts\PaymentStrategy;
class BankTransferStrategy implements PaymentStrategy
{
public function pay(float $amount): string
{
// Logique spécifique au virement bancaire
return "Paid {$amount} via Bank Transfer.";
}
}
c) Implémenter la classe PaymentContext (Context)
La classe PaymentContext est responsable de choisir et d'utiliser la bonne stratégie de paiement.
// app/Services/Payments/PaymentContext.php
namespace App\Services\Payments;
use App\Contracts\PaymentStrategy;
class PaymentContext
{
protected $paymentStrategy;
// Méthode pour définir dynamiquement la stratégie de paiement
public function setPaymentStrategy(PaymentStrategy $strategy)
{
$this->paymentStrategy = $strategy;
}
// Méthode pour effectuer le paiement
public function executePayment(float $amount): string
{
return $this->paymentStrategy->pay($amount);
}
}
d) Utilisation du Strategy Pattern dans une commande CLI
Nous allons créer une commande CLI pour choisir une méthode de paiement dynamiquement et exécuter le paiement en fonction de cette stratégie.
// app/Console/Commands/ProcessPayment.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\Payments\PaymentContext;
use App\Services\Payments\PayPalStrategy;
use App\Services\Payments\StripeStrategy;
use App\Services\Payments\BankTransferStrategy;
class ProcessPayment extends Command
{
protected $signature = 'payment:process {method} {amount}';
protected $description = 'Traiter un paiement avec une méthode spécifique';
public function handle()
{
$method = $this->argument('method');
$amount = (float) $this->argument('amount');
// Créer le contexte de paiement
$paymentContext = new PaymentContext();
// Définir la stratégie de paiement en fonction de l'argument
switch ($method) {
case 'paypal':
$paymentContext->setPaymentStrategy(new PayPalStrategy());
break;
case 'stripe':
$paymentContext->setPaymentStrategy(new StripeStrategy());
break;
case 'bank':
$paymentContext->setPaymentStrategy(new BankTransferStrategy());
break;
default:
$this->error('Méthode de paiement inconnue.');
return;
}
// Exécuter le paiement et afficher le résultat
$result = $paymentContext->executePayment($amount);
$this->info($result);
}
}
e) Exécution de la commande
Tu peux exécuter cette commande pour traiter des paiements en utilisant différentes méthodes de paiement.
- PayPal :
php artisan payment:process paypal 100
Résultat :
Paid 100 using PayPal.
- Stripe :
php artisan payment:process stripe 50
Résultat :
Paid 50 using Stripe.
- Virement Bancaire :
php artisan payment:process bank 200
Résultat :
Paid 200 via Bank Transfer.
Avantages du Strategy Pattern
- Flexibilité : Le Strategy Pattern permet de changer dynamiquement d'algorithme ou de stratégie sans modifier le code du client.
- Séparation des préoccupations : Chaque stratégie est encapsulée dans une classe distincte, ce qui permet de mieux organiser la logique métier et d'éviter le code complexe.
- Réutilisabilité : Les stratégies peuvent être réutilisées dans différents contextes, et il est facile d'ajouter de nouvelles stratégies sans modifier le code existant.
- Testabilité : Chaque stratégie étant isolée dans sa propre classe, il est facile de les tester indépendamment.
Inconvénients du Strategy Pattern
- Multiplication des classes : L'utilisation de ce pattern peut entraîner une prolifération de classes, surtout s'il y a beaucoup de stratégies à implémenter.
- Complexité accrue : Pour des cas simples, le Strategy Pattern peut introduire une complexité inutile, en particulier si les stratégies ne varient pas beaucoup.
- Dépendance du client : Le client doit savoir quand et comment choisir la bonne stratégie, ce qui peut introduire de la complexité si les conditions de choix de la stratégie sont nombreuses.
Conclusion
Le Strategy Pattern est un outil puissant pour gérer des variations d'algorithmes dans des applications Laravel. Il permet de choisir dynamiquement entre différentes stratégies en encapsulant chaque algorithme dans une classe distincte. Ce pattern est particulièrement utile pour des cas comme la gestion de méthodes de paiement, de validation ou de transformation de données, où tu peux avoir besoin de plusieurs implémentations interchangeables. Cela permet de rendre le code plus flexible, modulaire et facile à maintenir.