Memento Pattern
- Type : Comportemental
- Difficulté : 6/10
- Définition succincte : Le Memento Pattern est un design pattern comportemental qui permet de capturer et de stocker l'état interne d'un objet, afin que cet objet puisse être restauré à cet état plus tard sans violer l'encapsulation. Ce pattern est particulièrement utile pour implémenter des fonctionnalités d'annulation (undo) ou de restauration dans les systèmes où il est nécessaire de revenir à un état antérieur de l'objet.
Objectif du Memento Pattern
Le Memento Pattern permet de capturer l'état d'un objet sans exposer ses détails internes et de restaurer cet état plus tard. Ce pattern est utile pour créer des systèmes qui nécessitent des fonctionnalités d'annulation (undo) ou pour restaurer des états précédents, comme dans des éditeurs de texte ou des systèmes de gestion de versions.
Structure du Memento Pattern
- Memento : Objet qui capture et stocke l'état interne de l'objet d'origine.
- Originator : L'objet dont l'état est sauvegardé dans le Memento.
- Caretaker : Gère l'historique des mementos et demande la restauration de l'état de l'Originator.
Implémentation avec Laravel
Imaginons que tu développes une application Laravel qui gère un système d'édition de texte. Chaque fois qu'un utilisateur modifie le contenu d'un document, tu souhaites permettre à l'utilisateur de revenir à un état antérieur (fonctionnalité undo). Le Memento Pattern permet de capturer l'état du document à chaque modification, et de le restaurer si nécessaire.
a) Créer la classe Document (Originator)
La classe Document représente l'objet dont l'état sera capturé. Elle peut sauvegarder et restaurer son état à partir d'un memento.
// app/Services/Document.php
namespace App\Services;
use App\Services\Memento;
class Document
{
protected $content;
public function setContent(string $content): void
{
$this->content = $content;
}
public function getContent(): string
{
return $this->content;
}
// Crée un memento qui capture l'état actuel
public function saveToMemento(): Memento
{
return new Memento($this->content);
}
// Restaurer l'état depuis un memento
public function restoreFromMemento(Memento $memento): void
{
$this->content = $memento->getSavedContent();
}
}
b) Créer la classe Memento (Memento)
Le Memento capture l'état de l'objet Document sans exposer son contenu interne. Cet objet peut ensuite être utilisé pour restaurer l'état du document.
// app/Services/Memento.php
namespace App\Services;
class Memento
{
private $content;
public function __construct(string $content)
{
$this->content = $content;
}
// Récupérer le contenu sauvegardé
public function getSavedContent(): string
{
return $this->content;
}
}
c) Créer la classe DocumentHistory (Caretaker)
La classe DocumentHistory est responsable de gérer l'historique des états sauvegardés du document. Elle permet de stocker et de restaurer les mementos.
// app/Services/DocumentHistory.php
namespace App\Services;
class DocumentHistory
{
private $history = [];
// Ajouter un memento à l'historique
public function saveMemento(Memento $memento): void
{
$this->history[] = $memento;
}
// Récupérer le dernier memento sauvegardé
public function undo(): ?Memento
{
return array_pop($this->history);
}
}
d) Utilisation du Memento Pattern dans une commande CLI
Nous allons créer une commande pour simuler l'édition d'un document et la fonctionnalité d'annulation (undo).
// app/Console/Commands/EditDocument.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\Document;
use App\Services\DocumentHistory;
class EditDocument extends Command
{
protected $signature = 'document:edit';
protected $description = 'Simuler l\'édition d\'un document avec la fonctionnalité undo';
public function handle()
{
$document = new Document();
$history = new DocumentHistory();
// Simuler l'édition du document
$this->info("État initial du document");
$document->setContent("Version 1");
$this->info("Contenu: " . $document->getContent());
$history->saveMemento($document->saveToMemento());
// Modifier le document
$document->setContent("Version 2");
$this->info("Contenu: " . $document->getContent());
$history->saveMemento($document->saveToMemento());
// Modifier encore le document
$document->setContent("Version 3");
$this->info("Contenu: " . $document->getContent());
// Annuler la dernière modification
$this->info("Annuler la dernière modification...");
$document->restoreFromMemento($history->undo());
$this->info("Contenu après undo: " . $document->getContent());
// Annuler encore une fois
$this->info("Annuler encore...");
$document->restoreFromMemento($history->undo());
$this->info("Contenu après undo: " . $document->getContent());
}
}
e) Exécution de la commande
Tu peux exécuter la commande pour simuler l'édition d'un document et la restauration des états précédents.
php artisan document:edit
Résultat attendu :
État initial du document
Contenu: Version 1
Contenu: Version 2
Contenu: Version 3
Annuler la dernière modification...
Contenu après undo: Version 2
Annuler encore...
Contenu après undo: Version 1
Avantages du Memento Pattern
- Préservation de l'encapsulation : Le Memento Pattern permet de sauvegarder et de restaurer l'état d'un objet sans exposer ses détails internes, ce qui respecte le principe d'encapsulation.
- Fonctionnalités d'annulation : Ce pattern est idéal pour implémenter des fonctionnalités d'annulation (undo) ou de restauration dans les systèmes où l'état des objets change fréquemment.
- Facilité de restauration : Il permet de restaurer facilement des états antérieurs d'un objet sans avoir à maintenir manuellement plusieurs copies de cet objet.
Inconvénients du Memento Pattern
- Surcharge en mémoire : Si les objets sont volumineux ou si l'état est souvent sauvegardé, cela peut entraîner une surcharge en termes de mémoire, car chaque memento contient une copie complète de l'état.
- Complexité accrue : Le Memento Pattern peut ajouter une certaine complexité au système, surtout si l'historique d'objets devient long ou si plusieurs types d'objets doivent être sauvegardés.
Conclusion
Le Memento Pattern est un outil puissant pour gérer les modifications d'état d'un objet de manière non intrusive. En Laravel, ce pattern peut être utile dans des fonctionnalités d'édition ou d'annulation, où tu dois capturer des états successifs d'un objet et permettre à l'utilisateur de revenir à un état antérieur. Ce pattern respecte l'encapsulation tout en offrant une solution flexible pour la gestion des états.