Command Pattern
- Type : Comportemental
- Difficulté : 5/10
- Définition succincte : Le Command Pattern est un design pattern comportemental qui encapsule une requête en tant qu'objet, permettant ainsi de paramétrer des actions, de les mettre en file d'attente ou de les annuler. Ce pattern fournit une manière flexible et extensible de gérer des actions qui doivent être exécutées à des moments spécifiques ou de manière différée.
- Contexte spécifique à Laravel : En Laravel, le Command Pattern est utilisé de manière native dans les Artisan Commands, où chaque commande est encapsulée en tant que classe, permettant de définir des tâches que l'on peut exécuter via la CLI ou planifier pour une exécution ultérieure.
Objectif du Command Pattern
L'objectif du Command Pattern est de permettre d'encapsuler des actions dans des objets, ce qui permet de découpler l'invocation de la commande de son exécution. Cela permet de créer des systèmes flexibles où les actions peuvent être paramétrées, planifiées ou même annulées sans que le client ait besoin de connaître les détails de leur implémentation.
Structure du Command Pattern
- Command : L'interface ou la classe abstraite qui définit l'action à exécuter.
- ConcreteCommand : La classe concrète qui implémente la commande et la logique associée à son exécution.
- Invoker : La classe qui exécute la commande lorsqu'elle est requise.
- Receiver : L'objet qui exécute l'action en réponse à la commande.
- Client : L'objet qui crée la commande et configure le receiver.
1. Exemple d'utilisation dans Laravel
En Laravel, les Artisan Commands utilisent le Command Pattern pour encapsuler des tâches exécutables via la CLI. Voici un exemple d'utilisation classique d'une commande Laravel.
Création d'une commande Artisan
php artisan make:command SendEmails
Implémentation de la commande
// app/Console/Commands/SendEmails.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class SendEmails extends Command
{
// Signature et description de la commande
protected $signature = 'emails:send';
protected $description = 'Send emails to users';
public function handle()
{
// Logique d'envoi d'e-mails
$this->info('Emails have been sent.');
}
}
La commande peut ensuite être exécutée avec :
php artisan emails:send
Ici, l'appel de la commande est complètement découplé de son exécution, ce qui est un exemple typique du Command Pattern.
2. Implémentation complète dans Laravel
Prenons un exemple plus général, où nous voulons encapsuler des actions dans des commandes sans utiliser la fonctionnalité Artisan. Imaginons que tu développes un système pour exécuter plusieurs actions (comme l'ajout de produits dans un panier et l'envoi de notifications) de manière flexible.
a) Créer l'interface Command
Nous définissons d'abord l'interface Command qui contiendra la méthode execute() que toutes les commandes devront implémenter.
// app/Contracts/Command.php
namespace App\Contracts;
interface Command
{
public function execute(): void;
}
b) Implémenter des commandes concrètes
Nous allons créer deux commandes : l'une pour ajouter un produit au panier, et l'autre pour envoyer une notification.
1. Commande AddProductToCartCommand
// app/Commands/AddProductToCartCommand.php
namespace App\Commands;
use App\Contracts\Command;
use App\Services\Cart;
class AddProductToCartCommand implements Command
{
protected $cart;
protected $product;
public function __construct(Cart $cart, $product)
{
$this->cart = $cart;
$this->product = $product;
}
public function execute(): void
{
$this->cart->add($this->product);
echo "Product {$this->product} added to cart." . PHP_EOL;
}
}
2. Commande SendNotificationCommand
// app/Commands/SendNotificationCommand.php
namespace App\Commands;
use App\Contracts\Command;
class SendNotificationCommand implements Command
{
protected $message;
public function __construct(string $message)
{
$this->message = $message;
}
public function execute(): void
{
echo "Notification sent: {$this->message}" . PHP_EOL;
}
}
c) Créer l'Invoker
L'invoker est responsable d'exécuter les commandes. Il peut aussi les mettre en file d'attente ou les exécuter dans un certain ordre.
// app/Services/Invoker.php
namespace App\Services;
use App\Contracts\Command;
class Invoker
{
protected $commands = [];
public function addCommand(Command $command)
{
$this->commands[] = $command;
}
public function executeCommands()
{
foreach ($this->commands as $command) {
$command->execute();
}
}
}
d) Utiliser le Command Pattern dans une commande CLI
Nous allons maintenant encapsuler l'exécution de plusieurs commandes dans une commande CLI.
// app/Console/Commands/ManageCart.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\Invoker;
use App\Services\Cart;
use App\Commands\AddProductToCartCommand;
use App\Commands\SendNotificationCommand;
class ManageCart extends Command
{
protected $signature = 'cart:manage';
protected $description = 'Manage cart and send notifications';
public function handle()
{
$invoker = new Invoker();
// Créer un panier
$cart = new Cart();
// Ajouter des commandes à l'invoker
$invoker->addCommand(new AddProductToCartCommand($cart, 'Product A'));
$invoker->addCommand(new AddProductToCartCommand($cart, 'Product B'));
$invoker->addCommand(new SendNotificationCommand('Products have been added to your cart.'));
// Exécuter les commandes
$invoker->executeCommands();
}
}
e) Exécution de la commande
Tu peux maintenant exécuter cette commande pour voir les actions encapsulées être exécutées dans l'ordre.
php artisan cart:manage
Cela produira quelque chose comme ceci :
Product Product A added to cart.
Product Product B added to cart.
Notification sent: Products have been added to your cart.
Avantages du Command Pattern
- Découplage : Le Command Pattern permet de séparer l'invocation de la commande de sa logique d'exécution, facilitant ainsi la flexibilité et l'extensibilité.
- Modularité : Les commandes peuvent être ajoutées, supprimées ou modifiées sans affecter les autres parties du système.
- Réutilisabilité : Les commandes peuvent être réutilisées dans différents contextes et combinées dans des séquences d'exécution variées.
- File d'attente : Le pattern permet facilement d'ajouter des commandes à une file d'attente pour une exécution différée.
Inconvénients du Command Pattern
- Complexité accrue : Il peut introduire une complexité supplémentaire lorsque des tâches simples pourraient être réalisées directement sans avoir à encapsuler la logique dans une commande.
- Multiplication des classes : Chaque action doit être encapsulée dans une classe distincte, ce qui peut entraîner un grand nombre de petites classes à gérer.
Conclusion
Le Command Pattern est un outil puissant pour encapsuler des actions complexes et les exécuter de manière flexible et modulable. En Laravel, il est utilisé de manière native dans les Artisan Commands, mais tu peux également l'appliquer à d'autres scénarios, comme la gestion d'actions multiples dans une application complexe. Ce pattern permet de maintenir une architecture découpée, modulable, et réutilisable tout en offrant la possibilité d'exécuter des actions différées ou planifiées.