P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ GO AWAY OR I WILL REPLACE YOU WITH A VERY SMALL SHELL SCRIPT Articles | Connexion
 
~Itérateur et Chaîne de responsabilités

 Présentation

Les motifs de conception se présentent dans de nombreuses circonstances, souvent insoupçonnées. Nous ne dérogerons pas à la règle énoncée précédemment en nous penchant à présent sur le cas de deux nouveaux motifs de conception. Notre étude se portera sur deux motifs de comportement. Le premier motif de conception étudié se nomme Itérateur et le second Chaîne de responsabilités.
 Sommaire


 Le motif Itérateur

Le groupe de motifs de conception de comportement comprend onze motifs distincts : Chaîne de responsabilité, Commande, Interpréteur, Itérateur, Médiateur, Memento, Observateur, Etat, Stratégie, Template et Visiteur.

Le motif Itérateur fournit un moyen d'accès séquentiel aux éléments d'un agrégat d'objets, une liste. Le rôle principal de ce motif consiste à masquer la représentation interne de l'agrégat d'objets considéré. Pour un développeur, il apparaît nécessaire de pouvoir aisément parcourir une liste et d'accéder à ses éléments. Les parcours de l'agrégat peuvent notamment suivre des itinéraires variés. On parlera ainsi d'itérateurs à filtre.

Imaginons le cas d'une liste d'élèves. Un itérateur filtré pourrait octroyer la chance de ne parcourir que les éléments dont le nom commence par une lettre donnée. De ce fait, il se révèle important de dissocier l'implémentation des parcours de l'agrégat lui-même.

En outre, une telle dissociation engendre la possibilité de pouvoir réaliser plusieurs parcours en même temps, aucun d'entre eux n'étant inclus dans l'implémentation de la liste. Typiquement, un itérateur sera défini de la sorte (nous employons ici le langage Python) :

class Iterateur:
  def first(self): pass
  def next(self): pass
  def isDone(self): pass
  def getCurrentItem(self): pass
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Le constructeur de l'itérateur devra prendre en paramètre une instance de la liste à parcourir. En réalisant une abstraction au niveau de la liste et de l'itérateur, nous sommes en mesure d'offrir des itérations polymorphes. Nous pouvons imaginer une classe abstraite ListeAbstraite mère des classes Liste et ListeAleatoire. Pour permettre leur parcours, nous créerons donc l'interface Iterateur et deux classes IterateurDeListe et IterateurDeListeAbstraite. Voici le listing exposant une implémentation d'un itérateur pour une liste Python classique :

class Iterateur:
    def __init__(self, l):
        self.l = l
        self.index = 0
    def first(self):
        self.index = 0
        return self.getCurrentItem()
    def next(self):
        item = self.getCurrentItem()
        self.index = self.index + 1
        return item
    def isDone(self):
        return self.index >= len(self.l)
    def getCurrentItem(self):
        return self.l[self.index]
       
      
JextCopier dans Jext
Une proposition d'utilisation de cet itérateur se formulerait de la sorte :

liste = ['caroline', 'claire', 'laure', 'noémie', 'laëtitia']
it = Iterateur(liste)
while not it.isDone():
    print it.next()
       
      
JextCopier dans Jext
Le parcours des prénoms de cette liste commençant par la lettre 'l' s'effectuera très simplement en assignant à la variable it une instance de la nouvelle classe IterateurFiltreLettre. Nous présentons la définition de l'itérateur à filtre au sein de ce listing :

class IterateurFiltreLettre(Iterateur):
    def __init__(self, l, lettre):
        Iterateur.__init__(self, l)
        self.lettre = lettre
    def getCurrentItem(self):
        while not self.isDone() and self.l[self.index][0] != self.lettre:
            self.index = self.index + 1
        return (self.isDone() and [None] or [self.l[self.index]])[0]
       
      
JextCopier dans Jext
En exécutant l'instruction it = IterateurFiltreLettre(l, 'l') le code précédent n'affichera que les prénoms "laure" et "laëtitia".



L'itérateur à filtre fonctionne à merveille.


 Le motif Chaîne de responsabilités

Le motif Chaîne de responsabilités trouve sa place dans tout système d'information émetteur de requête. A l'émission, l'émetteur ne connaît pas le destinataire et se contente de propager l'information tout au long d'une chaîne de récepteurs. Différentes implémentations s'envisagent dans une telle situation. On peut ainsi mettre en ouvre une chaîne de responsabilités dont chacun des membres va appliquer des modifications à l'information. A l'occasion d'une application amenée à générer une page HTML nous pourrions inventer une chaîne de filtres. Un filtre transformerait les accents, un autre éliminerait les éléments inutiles et un dernier générerait le code HTML en lui-même. Dans une telle situation, tout maillon de la chaîne se voit susceptible d'agir.

Une seconde approche consiste à propager l'information jusqu'à l'aboutissement d'une opération. Imaginons une console de commande incluse dans une application. Lorsque l'utilisateur tape une commande, la console va propager la commande le long de la chaîne. Chaque élément de la chaîne correspondra à une commande connue du programme. Les commandes vont une par une traiter la requête. La première d'entre elles l'acceptant arrêtera la propagation. Un tel système apporte une grande souplesse dans la gestion de la console. Ajouter ou retirer une commande ne demandera que l'ajout ou la suppression d'une ligne de code. Réaliser le même type de traitement par l'intermédiaire de structures de type if demanderait des efforts bien plus importants.

La réalisation d'une chaîne de responsabilités prend bien souvent la forme d'une liste chaînée simple. Vous trouverez dans ce listing la description de la classe Command qui marque un élément de la chaîne :

public abstract class Command
{
  public Command next;
  public abstract String getCommandName();
  public abstract boolean handleCommand(String command);
}
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
En parcourant la chaîne, la console appellera la méthode handleCommand() de chaque élément. Lors du renvoi de la valeur vrai, le processus s'arrêtera. En premier lieu, nous devons initialiser la liste de commandes disponibles ("cd", "eval" et "clear") :

private void initCommands()
{
  firstCmd = currentCmd = new ClearCommand();
  addCommand(new ChangeDirCommand());
  addCommand(new EvalCommand());
}
       
      
JextCopier dans Jext
L'opération d'ajout d'une commande à la liste se veut véritablement trivial :

currentCmd.next = command;
currentCmd = command;
       
      
JextCopier dans Jext
Enfin notre application nécessite une méthode de propagation de l'information au sein de la chaîne :

while (cmd != null)
{
  if (cmd.handleCommand(command))
    return true;
  cmd = cmd.next;
}
       
      
JextCopier dans Jext
La variable command désigne ici la chaîne de caractères tapée par l'utilisateur. La mise en place d'un tel système ne demande au final que très peu de lignes de code. De plus chaque commande possède sa classe propre, ceci offrant une grande lisibilité du programme.

L'adjonction d'une nouvelle commande ne demandera que l'ajout d'un appel à addCommand() au cour de la méthode initCommands(). L'accès publique de méthodes telles que addCommand() offre de surcroît l'opportunité d'étendre les capacités de votre programme par l'entremise de processus externes (plugins.). Le modèle de gestion d'une chaîne de responsabilités présenté ici pourrait se voir grandement amélioré en l'employant conjointement avec. le motif Itérateur !

Nous pourrions à cet effet concevoir un IterateurDeCommande dont le rôle consisterait à parcourir la liste de commandes. En ce cas, nous allégerions le code source de la console en séparant la gestion de la console même du parcours des commandes. Nous sommes en présence d'un cas d'école concernant l'application du motif Itérateur. Le code source fourni en annexe vous permettra de vous lancer dans ce petit exercice.



Notre console rejette les commandes inconnues.


 Annexes




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



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