P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ SOME BABIES WERE DROPPED ON THEIR HEADS BUT YOU WERE CLEARLY THROWN AT A WALL Articles | Connexion
 
~Le motif Memento

Précédent  
  Design Patterns  
  Suivant
 Présentation

Comme annoncé le mois dernier, nous voici arrivés au terme de cette découverte des principaux Design Patterns, énoncés dans l'ouvrage de référence du Gang Of Four. Nous ne manquerons cependant pas de vous en faire découvrir de nouveaux par la suite.
 Sommaire


 Introduction

Le motif de conception orienté objet sur lequel nous allons porter notre attention dans ces colonnes s'intitule Memento. Une fois de plus, ce dernier se révèle particulièrement utile et performant lors du développement d'une application possédant une interface graphique. La plupart des logiciels répondant à ce critère correspondent à des éditeurs (d'images, de texte, de sons.) offrant la possibilité d'annuler la ou les dernières actions effectuées par l'utilisateur. Interrogeons-nous donc sur la nature d'une annulation d'un point de vue architecture logicielle. Il s'agit en définitive de sauvegarder l'état interne d'un objet pour, éventuellement, le restaurer par la suite. Toutefois, le modèle Memento pousse ce concept un peu plus loin en instaurant la possibilité d'enregistrer et de restaurer cet état sans que l'objet n'ait à la faire lui-même et sans violer le principe d'encapsulation. Ainsi, un objet donné sera incapable de savoir s'il est affecté ou non par un Memento.


 Initiateur, Memento et Surveillant

Les objets constituant un programme exposent rarement l'ensemble de leur état interne au travers de méthodes ou membres publics. Néanmoins, le succès de l'implémentation d'une fonction d'annulation repose bien souvent sur la sauvegarde de la totalité des caractéristiques des objets. Bien entendu, vous pourrez vous contenter des propriétés accessibles publiquement dans certains situations. Prenons l'exemple d'un outil de dessin vectoriel. L'opération d'annulation du déplacement d'un objet ne requière que la sauvegarde de l'ancienne position de l'objet à l'écran, vraisemblablement accessible publiquement. Mais que pouvons-nous faire lorsque nous rencontrons une situation qui nécessite la lecture d'une donnée inaccessible ? Une première solution serait de rendre toutes les informations du système accessibles publiquement. Malheureusement, nous le rendrions alors particulièrement vulnérable à des changements induits par des acteurs externes. En outre, la notion d'encapsulation se défend d'une telle implémentation.

Notre nouveau Design Pattern essaye d'apporter une réponse élégante à ce problème en s'octroyant des accès privilégiés à l'état de l'objet que nous désirons enregistrer. Tous les autres éléments du système ne se verront offert que des autorisations restreintes, conservant ainsi l'intégrité de l'ensemble. Pour cela, nous devons mettre en place trois acteurs : l'Initiateur (ou Originator) qui désigne l'objet dont nous souhaitons enregistrer l'état, le Memento qui s'occupe du processus de sauvegarde à proprement parler et le Surveillant (ou Caretaker) qui déclenche les opérations de mémorisation et de restauration auprès du Memento.

L'ouvrage Design Patterns du GoF suggère en C++ l'utilisation du modificateur d'accès friend pour contourner l'écueil de membres exclusivement publics. En Java, le programmeur pourra opter pour le modificateur par défaut, ne possédant pas de mot-clés, appelé privilégié ou encore "private protected". Avec ceci, les membres d'une classe sont accessibles publiquement à celles appartenant au même paquetage uniquement. En organisant le système astucieusement, nous pouvons nous débrouiller pour garantir les droits d'accès suffisant au Memento.


 Implémentation

L'implémentation de référence que nous allons étudier se base sur le même programme que celui découvert les mois précédents, un éditeur de notes pour élèves. A l'aide de notre nouveau modèle de conception nous allons pouvoir ajouter la possibilité d'annuler les modifications effectuées sur un ou plusieurs élèves. La possibilité d'annuler la création ou la suppression d'un élève relèvera ensuite d'une simple formalité que vous pourrez remplir à titre d'exercice.



Schématisation de notre système.

L'éditeur conserve toutes les notes, instances de Note, au sein d'un document représenté par la classe NotesDocument. Cette dernière jouera le rôle du Surveillant tandis que la classe Note correspondra à l'Initiateur. Le listing 1 présente un extrait de celle-ci démontrant que le principe d'encapsulation reste inviolé. Tous les membres se voient attribué le modificateur "private protected", ce qui implique que notre Memento fera partie du paquetage org.jext.editor.notes.

//Listing 1
package org.jext.editor.notes;

public class Note implements EditorElement, Comparable
{
  String lastName, firstName;
  double note;
  // .
}
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
En réalité, l'implémentation de notre Memento repose sur une interface publique éponyme décrite dans le listing 2. Celle-ci sera employée ensuite pour créer diverses sortes de Memento. L'unique méthode proposée enclenche le processus de restauration de l'état sauvegardé. L'enregistrement de celui-ci pourra être effectué dans le constructeur de nos implémentations concrètes du motif.

//Listing 2
package org.jext.editor.notes;

public interface Memento
{
  public void restore();
}
       
      
JextCopier dans Jext
Ainsi, le listing 3 présente le code source de ValueMemento dont le rôle se borne à sauvegarder chaque membre d'un objet note.

//Listing 3
package org.jext.editor.notes;

public class ValueMemento implements Memento
{
  private double note;
  private Note originator;
  private String lastName, firstName;

  public ValueMemento(Note n)
  {
    this.originator = n;
    this.lastName   = n.lastName;
    this.firstName  = n.firstName;
    this.note       = n.note;
  }

  public void setOriginator(Note originator)
  {
    this.originator = originator;
  }

  public void restore()
  {
    this.originator.lastName = this.lastName;
    this.originator.firstName = this.firstName;
    this.originator.note = this.note;
  }
}
       
      
JextCopier dans Jext
Le travail de gestion de la pile d'annulation échoue à notre Surveillant. Lors de la modification d'une donnée (dans la méthode setCurrent()), un nouveau Memento apparaît dans ladite pile, ainsi que l'expose le listing 4.

//Listing 4
ValueMemento m = new ValueMemento((Note) data.elementAt(index));
m.setOriginator((Note) e);
undoList.add(m);
       
      
JextCopier dans Jext
L'exécution d'une restauration se révèle ensuite particulièrement trivial, comme en témoigne le listing 5.

//Listing 5
public void undo()
{
  if (isDirty() && !undoList.empty())
  {
    Memento m = (Memento) undoList.pop();
    m.restore();
  }
}
       
      
JextCopier dans Jext
Vous aurez constaté combien il se révèle simple d'implémenter ce Design Pattern au cour d'un programme d'édition graphique. En créant d'autres Memento spécialisés le développeur pourra autoriser la restauration de toutes les combinaisons d'états qu'il est possible d'imaginer. Notre exemple contient ainsi une classe ListValueMemento permettant d'annuler une série complète de modifications. Quand l'utilisateur rentre une expression mathématique dans la fenêtre principale, toutes les notes se trouvent modifiées. Le logiciel fait appel à ce nouveau Memento pour enregistrer sur un même niveau d'annulation l'état de toutes les notes à cet instant précis. Pour ce faire, ListValueMemento contient une agrégation de ValueMemento et de fait, le code de restauration présenté dans le listing 6 se révèle particulièrement concis.

//Listing 6
public void restore()
{
  for (int i = 0; i < list.size(); i++)
    ((Memento) list.get(i)).restore();
}
       
      
JextCopier dans Jext
Voilà donc comment s'achève notre rubrique sur les Design Patterns. Si toutefois vous souhaitez en découvrir de nouveaux, n'hésitez pas à contacter la rédaction du magazine, nous nous ferons un plaisir de satisfaire vos requêtes.


 Téléchargements

Vous pouvez télécharger l'exemple complet.



par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006


Précédent  
  Design Patterns  
  Suivant

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE