Decorator Pattern
- Type : Structurel
- Difficulté : 6/10
- Définition succincte : Le Decorator Pattern est un design pattern structurel qui permet d'ajouter dynamiquement des responsabilités ou des fonctionnalités à un objet sans modifier sa structure. Ce pattern est souvent utilisé pour éviter la création de nombreuses sous-classes afin de gérer les différentes combinaisons de fonctionnalités. Au lieu de cela, le Decorator permet d'envelopper un objet existant avec des "décorateurs" qui ajoutent des comportements supplémentaires.
Objectif du Decorator Pattern
L'objectif du Decorator Pattern est d'ajouter des fonctionnalités à un objet de manière flexible et dynamique. Au lieu de créer des sous-classes pour chaque variante d'un objet, le Decorator Pattern permet d'envelopper un objet avec de nouveaux comportements tout en conservant son interface d'origine.
Structure du Decorator Pattern
- Component : Interface ou classe abstraite qui définit les méthodes que l'objet de base et les décorateurs doivent implémenter.
- ConcreteComponent : L'implémentation concrète de l'objet de base.
- Decorator : Classe abstraite ou interface qui implémente ou étend le Component et contient une référence à un Component pour ajouter des fonctionnalités supplémentaires.
- ConcreteDecorator : Classe concrète qui hérite du Decorator et ajoute de nouveaux comportements à l'objet encapsulé.
Implémentation avec Laravel
Imaginons que tu crées une application Laravel où tu veux générer des rapports PDF. Tu souhaites ajouter dynamiquement des fonctionnalités à ces rapports, comme la possibilité d'ajouter un en-tête ou un pied de page. Le Decorator Pattern te permet d'ajouter ces fonctionnalités à un rapport existant sans modifier sa structure de base.
a) Créer l'interface Report (Component)
Cette interface définit les méthodes de base que tous les rapports doivent implémenter.
// app/Contracts/Report.php
namespace App\Contracts;
interface Report
{
public function generate(): string;
}
b) Implémenter un rapport de base BasicReport (ConcreteComponent)
Voici une implémentation de base de l'interface Report, qui génère un rapport simple.
// app/Services/BasicReport.php
namespace App\Services;
use App\Contracts\Report;
class BasicReport implements Report
{
public function generate(): string
{
return "This is a basic report.";
}
}
c) Créer un décorateur ReportDecorator (Decorator)
Le decorator de base, qui va contenir la référence à un objet Report et permettre l'ajout de fonctionnalités.
// app/Decorators/ReportDecorator.php
namespace App\Decorators;
use App\Contracts\Report;
abstract class ReportDecorator implements Report
{
protected $report;
public function __construct(Report $report)
{
$this->report = $report;
}
public function generate(): string
{
return $this->report->generate();
}
}
d) Implémenter des décorateurs spécifiques HeaderReportDecorator et FooterReportDecorator (ConcreteDecorator)
Ces décorateurs ajoutent un en-tête et un pied de page à un rapport existant.
1. Décorateur pour ajouter un en-tête
// app/Decorators/HeaderReportDecorator.php
namespace App\Decorators;
use App\Contracts\Report;
class HeaderReportDecorator extends ReportDecorator
{
public function generate(): string
{
return "=== HEADER ===\n" . $this->report->generate();
}
}
2. Décorateur pour ajouter un pied de page
// app/Decorators/FooterReportDecorator.php
namespace App\Decorators;
use App\Contracts\Report;
class FooterReportDecorator extends ReportDecorator
{
public function generate(): string
{
return $this->report->generate() . "\n=== FOOTER ===";
}
}
e) Utilisation du Decorator Pattern dans une commande CLI
Nous allons maintenant créer une commande pour générer un rapport avec ou sans en-tête et pied de page. Grâce au Decorator Pattern, tu peux facilement combiner ces fonctionnalités sans modifier la structure de base du rapport.
// app/Console/Commands/GenerateReport.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\BasicReport;
use App\Decorators\HeaderReportDecorator;
use App\Decorators\FooterReportDecorator;
class GenerateReport extends Command
{
protected $signature = 'report:generate {--header} {--footer}';
protected $description = 'Générer un rapport avec des options de décorateurs';
public function handle()
{
// Créer un rapport de base
$report = new BasicReport();
// Ajouter un en-tête si l'option est spécifiée
if ($this->option('header')) {
$report = new HeaderReportDecorator($report);
}
// Ajouter un pied de page si l'option est spécifiée
if ($this->option('footer')) {
$report = new FooterReportDecorator($report);
}
// Générer et afficher le rapport
$this->info($report->generate());
}
}
f) Exécution de la commande
Tu peux exécuter cette commande pour générer un rapport avec différentes combinaisons d'en-tête et de pied de page.
php artisan report:generate --header --footer
Résultat attendu :
=== HEADER ===
This is a basic report.
=== FOOTER ===
Avantages du Decorator Pattern
- Flexibilité : Le Decorator Pattern permet d'ajouter des fonctionnalités à un objet de manière flexible, sans modifier la structure de base de l'objet.
- Réutilisabilité : Les décorateurs peuvent être combinés entre eux, permettant de créer différentes versions d'un objet avec des fonctionnalités supplémentaires.
- Respect du principe de responsabilité unique : Chaque décorateur est responsable d'ajouter une fonctionnalité spécifique, ce qui permet de maintenir un code propre et bien organisé.
Inconvénients du Decorator Pattern
- Complexité accrue : Ce pattern peut ajouter de la complexité si de nombreux décorateurs sont utilisés, ce qui peut rendre le code plus difficile à suivre.
- Multiplication des classes : Pour chaque fonctionnalité supplémentaire, une nouvelle classe de décorateur doit être créée, ce qui peut entraîner une prolifération de classes.
Conclusion
Le Decorator Pattern est un excellent moyen d'ajouter dynamiquement des fonctionnalités à un objet tout en respectant le principe de responsabilité unique. En Laravel, bien que ce pattern ne soit pas utilisé nativement, il peut être très utile pour des scénarios où tu souhaites personnaliser le comportement d'un service ou d'un composant de manière modulaire et réutilisable. Ce pattern est particulièrement adapté pour des services tels que la génération de rapports, l'ajout de logiques d'enregistrement, ou l'extension de fonctionnalités sans modifier le code source d'origine.