Digital solutions
Design Patterns avec Laravel

Template Method Pattern

  • Type : Comportemental
  • Difficulté : 4/10
  • Définition succincte : Le Template Method Pattern est un design pattern comportemental qui permet de définir la structure d'un algorithme dans une méthode, tout en déléguant certaines étapes spécifiques aux sous-classes. Cela permet de centraliser la logique générale tout en laissant les sous-classes définir les détails de l'implémentation pour certaines étapes spécifiques.
  • Contexte spécifique à Laravel : En Laravel, ce pattern est utilisé dans les Blade Templates, où les vues parent définissent une structure globale et les vues enfants remplissent ou modifient certaines parties spécifiques grâce aux sections et à l'héritage des layouts.

Objectif du Template Method Pattern

L'objectif principal du Template Method Pattern est de centraliser la logique commune tout en permettant aux sous-classes d'implémenter des variations spécifiques. Cela est particulièrement utile lorsque plusieurs classes ont un comportement similaire, mais qu'elles diffèrent sur certaines étapes spécifiques.

Structure du Template Method Pattern

  • Template Method : Méthode définissant la structure générale de l'algorithme, avec certaines étapes déléguées aux sous-classes.
  • AbstractClass : Classe abstraite qui implémente le Template Method et définit certaines étapes, en laissant d'autres à être définies par les sous-classes.
  • ConcreteClass : Classe concrète qui implémente les étapes spécifiques de l'algorithme.

1. Exemple d'utilisation dans Laravel

Laravel utilise le Template Method Pattern dans le système de Blade Templates, où une vue parent peut définir un layout global, et des vues enfants remplissent ou modifient certaines sections spécifiques.

<!-- resources/views/layouts/app.blade.php -->

<!DOCTYPE html>
<html>
<head>
    <title>@yield('title')</title>
</head>
<body>
    <header>
        <h1>Welcome to My App</h1>
    </header>

    <main>
        @yield('content')
    </main>

    <footer>
        <p>© 2024 My App</p>
    </footer>
</body>
</html>

Ensuite, la vue enfant remplit les sections :

<!-- resources/views/pages/home.blade.php -->

@extends('layouts.app')

@section('title', 'Home Page')

@section('content')
    <p>This is the home page content.</p>
@endsection

Cela démontre comment la structure générale est définie dans la vue parent, tandis que les sections spécifiques sont définies dans les vues enfants.

2. Implémentation complète dans Laravel

Prenons un exemple d'une application Laravel où tu veux générer différents types de rapports (comme des rapports en PDF, CSV, ou JSON). Bien que la structure générale de la génération de rapports soit la même (préparation des données, formatage, exportation), le format des données (PDF, CSV, JSON) diffère. Le Template Method Pattern permet de gérer cette situation en définissant un flux général pour la génération du rapport et en laissant les sous-classes gérer les spécificités de formatage.

a) Créer une classe de base abstraite ReportGenerator

Cette classe va définir la structure de la génération du rapport avec des étapes communes, comme la préparation des données, et laisser les sous-classes définir la manière dont les données seront formatées.

// app/Services/Reports/ReportGenerator.php

namespace App\Services\Reports;

abstract class ReportGenerator
{
    // Méthode template qui définit la structure générale de la génération du rapport
    public function generateReport($data)
    {
        // 1. Préparer les données
        $preparedData = $this->prepareData($data);

        // 2. Formatage spécifique du rapport (implémenté par les sous-classes)
        $formattedData = $this->formatReport($preparedData);

        // 3. Exporter le rapport (implémenté par les sous-classes)
        $this->exportReport($formattedData);
    }

    // Étape commune à toutes les sous-classes : préparation des données
    protected function prepareData($data)
    {
        // Exemple simple : trier les données
        return collect($data)->sort();
    }

    // Étapes spécifiques que les sous-classes doivent implémenter
    abstract protected function formatReport($data);

    abstract protected function exportReport($formattedData);
}

b) Implémenter les sous-classes spécifiques

Chaque sous-classe va implémenter les méthodes formatReport() et exportReport() pour définir comment les données sont formatées et exportées dans des formats spécifiques (PDF, CSV, JSON, etc.).

1. Générateur de rapport CSV

Cette sous-classe formate les données en CSV et les exporte dans un fichier CSV.

// app/Services/Reports/CsvReportGenerator.php

namespace App\Services\Reports;

class CsvReportGenerator extends ReportGenerator
{
    protected function formatReport($data)
    {
        // Formatage des données en CSV
        return implode(',', $data->toArray());
    }

    protected function exportReport($formattedData)
    {
        // Exporter le rapport CSV (par exemple, sauvegarder dans un fichier)
        file_put_contents(storage_path('reports/report.csv'), $formattedData);
    }
}
2. Générateur de rapport JSON

Cette sous-classe formate les données en JSON et les exporte dans un fichier JSON.

// app/Services/Reports/JsonReportGenerator.php

namespace App\Services\Reports;

class JsonReportGenerator extends ReportGenerator
{
    protected function formatReport($data)
    {
        // Formatage des données en JSON
        return json_encode($data->toArray());
    }

    protected function exportReport($formattedData)
    {
        // Exporter le rapport JSON (par exemple, sauvegarder dans un fichier)
        file_put_contents(storage_path('reports/report.json'), $formattedData);
    }
}
3. Générateur de rapport PDF

Cette sous-classe utilise une bibliothèque PDF pour formater et exporter les données dans un fichier PDF.

// app/Services/Reports/PdfReportGenerator.php

namespace App\Services\Reports;

use PDF; // Supposons qu'on utilise une bibliothèque PDF

class PdfReportGenerator extends ReportGenerator
{
    protected function formatReport($data)
    {
        // Formatage des données pour un rapport PDF (en HTML par exemple)
        return view('reports.pdf', ['data' => $data])->render();
    }

    protected function exportReport($formattedData)
    {
        // Exporter le rapport PDF (sauvegarder un fichier PDF)
        $pdf = PDF::loadHTML($formattedData);
        $pdf->save(storage_path('reports/report.pdf'));
    }
}

c) Utiliser le Template Method Pattern dans une commande CLI

Nous allons maintenant créer une commande CLI pour permettre la génération de différents types de rapports.

// app/Console/Commands/GenerateReport.php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\Reports\CsvReportGenerator;
use App\Services\Reports\JsonReportGenerator;
use App\Services\Reports\PdfReportGenerator;

class GenerateReport extends Command
{
    protected $signature = 'report:generate {type}';
    protected $description = 'Generate a specific report in CSV, JSON or PDF format';

    public function handle()
    {
        $type = $this->argument('type');

        $data = [3, 1, 2]; // Exemple de données à traiter
        $reportGenerator = $this->getReportGenerator($type);

        if ($reportGenerator) {
            $reportGenerator->generateReport($data);
            $this->info("Report generated successfully in {$type} format!");
        } else {
            $this->error('Invalid report type. Please choose between csv, json, or pdf.');
        }
    }

    protected function getReportGenerator($type)
    {
        return match ($type) {
            'csv' => new CsvReportGenerator(),
            'json' => new JsonReportGenerator(),
            'pdf' => new PdfReportGenerator(),
            default => null,
        };
    }
}

d) Exécution de la commande

Tu peux exécuter la commande suivante pour générer des rapports dans différents formats :

php artisan report:generate csv
php artisan report:generate json
php artisan report:generate pdf

Exemple de sortie

Lorsque tu exécutes php artisan report:generate csv, le fichier report.csv est généré avec les données en format CSV. De même pour les commandes JSON et PDF, les fichiers report.json et report.pdf seront générés dans le répertoire de stockage.

Avantages du Template Method Pattern

  1. Réutilisation de code : La structure générale de l'algorithme est définie une seule fois dans la classe de base, ce qui permet de réutiliser la logique commune sans la dupliquer.
  2. Flexibilité : Les sous-classes peuvent personnaliser certaines étapes spécifiques de l'algorithme tout en respectant la structure générale imposée par la méthode template.
  3. Clarté : Le pattern rend le flux de l'algorithme plus clair, en définissant quelles étapes sont fixes et lesquelles peuvent être modifiées.
  4. Facilité de maintenance : Il est plus facile de maintenir ou de modifier la logique commune dans une seule classe de base, sans avoir à modifier chaque sous-classe.

Inconvénients

  1. Complexité potentielle : Si l'algorithme devient trop complexe, ou si trop d'étapes doivent être personnalisées dans les sous-classes, le pattern peut ajouter de la complexité inutile.
  2. Rigidité de la structure : Si la structure générale de l'algorithme change souvent, ce pattern peut être contraignant, car toutes les sous-classes doivent suivre la même structure.

Conclusion

Le Template Method Pattern est une solution élégante pour définir la structure générale d'un algorithme tout en permettant aux sous-classes de personnaliser certaines étapes spécifiques comme les détails propres à chaque cas. Ce pattern est très utilisé dans Laravel via les Blade Templates, mais il peut aussi être appliqué à d'autres scénarios comme la génération de rapports ou la gestion de workflows complexes.