Builder Pattern
- Type : Créationnel
- Difficulté : 5/10
- Définition succincte : Le Builder Pattern est un design pattern de création qui permet de construire des objets complexes étape par étape. Ce pattern est particulièrement utile lorsque la création d'un objet nécessite plusieurs étapes ou lorsqu'un objet a de nombreuses options ou variantes. Le Builder Pattern permet de séparer la construction de l'objet de sa représentation finale, en facilitant la création de différents types d'objets avec une logique de construction flexible.
- Contexte spécifique à Laravel : En Laravel, le Query Builder utilise une forme simplifiée de ce pattern pour construire des requêtes SQL de manière fluide et flexible, sans avoir à écrire directement le SQL.
Objectif du Builder Pattern
L'objectif principal du Builder Pattern est de fournir un moyen de créer des objets complexes en plusieurs étapes, en séparant la logique de construction de l'objet de son implémentation finale. Ce pattern est particulièrement utile lorsque la création d'un objet nécessite un grand nombre de paramètres, dont certains peuvent être facultatifs.
1. Exemple d'utilisation dans Laravel
Laravel utilise une version simplifiée du Builder Pattern via son Query Builder, qui te permet de construire des requêtes SQL de manière fluide, sans avoir à écrire manuellement du SQL. Voici un exemple d'utilisation classique du Query Builder en Laravel :
// Utilisation du Query Builder en Laravel pour récupérer les utilisateurs
$users = DB::table('users')
->where('active', 1)
->orderBy('name')
->get();
Dans cet exemple, le Query Builder permet de construire une requête SQL complexe étape par étape de manière fluide.
2. Implémentation complète dans Laravel
Prenons un exemple dans une application Laravel où tu dois construire des objets complexes, comme des commandes (orders) pour une boutique en ligne. Une commande peut avoir différents attributs (client, produits, livraison, mode de paiement), et certaines parties de la commande peuvent être facultatives. Le Builder Pattern te permettra de construire cet objet pas à pas, sans avoir à utiliser un long constructeur avec de nombreux paramètres.
a) Créer la classe Order (objet à construire)
Tu vas d'abord créer une classe Order qui représente une commande. Cette classe sera complexe, avec plusieurs attributs qui peuvent être optionnels.
// app/Models/Order.php
namespace App\Models;
class Order
{
public $customer;
public $items = [];
public $shippingAddress;
public $paymentMethod;
public $discount;
public $status;
public function __construct()
{
$this->status = 'pending'; // Status par défaut
}
// Méthode pour afficher les détails de la commande (juste pour l'exemple)
public function getOrderSummary()
{
return [
'customer' => $this->customer,
'items' => $this->items,
'shipping_address' => $this->shippingAddress,
'payment_method' => $this->paymentMethod,
'discount' => $this->discount,
'status' => $this->status,
];
}
}
b) Créer la classe OrderBuilder
Le builder va permettre de construire l'objet Order étape par étape. Chaque méthode du builder ajoute ou modifie un aspect de la commande, et le builder retourne l'instance finale de Order lorsque la construction est terminée.
// app/Builders/OrderBuilder.php
namespace App\Builders;
use App\Models\Order;
class OrderBuilder
{
protected $order;
public function __construct()
{
$this->order = new Order(); // Créer une nouvelle instance d'Order
}
public function setCustomer($customer)
{
$this->order->customer = $customer;
return $this;
}
public function addItem($item)
{
$this->order->items[] = $item;
return $this;
}
public function setShippingAddress($address)
{
$this->order->shippingAddress = $address;
return $this;
}
public function setPaymentMethod($paymentMethod)
{
$this->order->paymentMethod = $paymentMethod;
return $this;
}
public function applyDiscount($discount)
{
$this->order->discount = $discount;
return $this;
}
public function setStatus($status)
{
$this->order->status = $status;
return $this;
}
public function build()
{
// Retourner l'objet Order entièrement construit
return $this->order;
}
}
c) Utilisation du Builder dans une commande CLI
Nous allons maintenant utiliser le OrderBuilder dans une commande CLI pour construire des objets Order. Cette commande va simuler la création d'une commande avec des données fournies directement dans la ligne de commande.
// app/Console/Commands/CreateOrder.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Builders\OrderBuilder;
class CreateOrder extends Command
{
protected $signature = 'order:create
{customer : Le nom du client}
{items* : Les produits commandés}
{--shipping-address= : Adresse de livraison}
{--payment-method= : Méthode de paiement}
{--discount= : Pourcentage de réduction}';
protected $description = 'Créer une commande avec des produits, un client, une adresse de livraison, une méthode de paiement et des réductions';
public function handle()
{
// Créer une instance de OrderBuilder
$builder = new OrderBuilder();
// Récupérer les arguments et options de la commande
$customer = $this->argument('customer');
$items = $this->argument('items');
$shippingAddress = $this->option('shipping-address');
$paymentMethod = $this->option('payment-method');
$discount = $this->option('discount');
// Construire la commande étape par étape
$order = $builder->setCustomer($customer)
->setShippingAddress($shippingAddress ?? 'Adresse non spécifiée')
->setPaymentMethod($paymentMethod ?? 'Paiement non spécifié')
->applyDiscount($discount ?? 'Aucune réduction')
->setStatus('pending'); // Statut par défaut
// Ajouter les articles à la commande
foreach ($items as $item) {
$order->addItem($item);
}
// Finaliser la commande
$order = $builder->build();
// Afficher le récapitulatif de la commande dans la console
$this->info(json_encode($order->getOrderSummary(), JSON_PRETTY_PRINT));
}
}
d) Exécution de la commande
Voici comment tu peux exécuter cette commande pour créer une commande avec des produits, un client et d'autres détails optionnels :
php artisan order:create "John Doe" "Product A" "Product B" --shipping-address="123 Main St" --payment-method="Credit Card" --discount="10%"
Cela renverra le récapitulatif de la commande créée dans la console sous forme de JSON :
{
"customer": "John Doe",
"items": ["Product A", "Product B"],
"shipping_address": "123 Main St",
"payment_method": "Credit Card",
"discount": "10%",
"status": "pending"
}
Avantages du Builder Pattern
- Flexibilité : Le Builder Pattern permet de construire des objets complexes étape par étape, en rendant la construction flexible et modulaire.
- Clarté : Ce pattern rend le code plus lisible, surtout lorsque l'objet à construire a de nombreux attributs ou étapes de création.
- Séparation des préoccupations : Il sépare la logique de construction de l'objet de son utilisation, rendant le code plus maintenable.
- Facilité d'extension : Il est facile d'ajouter de nouvelles étapes dans la création de l'objet sans affecter le code existant.
Inconvénients du Builder Pattern
- Complexité : Pour des objets simples, l'utilisation du Builder Pattern peut ajouter une complexité inutile, en particulier si l'objet ne nécessite pas une création en plusieurs étapes.
- Surcharge en mémoire : Si chaque étape de la construction génère une nouvelle instance, cela peut entraîner une légère surcharge en termes de mémoire et de performance.
Conclusion
Le Builder Pattern est particulièrement utile lorsque tu as besoin de construire des objets complexes en plusieurs étapes, en offrant une interface fluide pour gérer cette construction. En Laravel, le Query Builder offre un exemple natif de ce pattern, mais tu peux aussi utiliser ce pattern pour d'autres scénarios comme la gestion de commandes, la génération de rapports, ou la construction de configurations. Ce pattern améliore la lisibilité et la flexibilité du code, tout en rendant la création d'objets plus modulaire.