Le motif iterator

Définition

Tout l’interêt de ce motif, c’est de pouvoir accéder à une suite d’objets sans connaître la façon dont ils sont organisé. C’est à dire pouvoir parcourir des objets contenus dans un autre, sans savoir comment ils sont rangé/organisé.

Principe

Pour ce motif nous allons devoir utiliser un objet intermédiaire qui permettra d’appeler les objets “cible” un par un. Cela permet de bien séparer une série d’objets de son utilisation.

L’exemple par le code

Nous allons dans cette exemple visualiser les membres des différents clubs de la ville. Chaque club utilise sa propre façon de lister ses membres. Nous allons donc devoir utiliser le motif Iterator pour simplifier la visualisation de toutes ces personnes (pour éviter de leur faire changer tout leur code).

Commencons par définir chaque club et leurs membres.

Le Mac_club utilise un objet Array pour lister ses membres, c’est simple et apparemment ça leur suffit. L’AS400_club utilise un objet Hash, sûrement dù à l’historique. Pour simplifier, nous allons utiliser un objet Club basique, et surcharger celui-ci pour le club historique (oui, la logique aurais voulu l’inverse).

class Club
    attr :membres
    attr_reader :name
    def initialize
        @name="Mac" 
        @membres = []
    end
    def each
        @membres.each {|membre| yield(membre)}
    end
end

class Mac_club < Club; end

class AS400_club < Club
    def initialize
        @name="AS400" 
        @membres = Hash::new
    end
    def each
        @membres.values.each {|membre| yield(membre)}
    end
end
class Membre
    attr_accessor :nom, :prenom, :mail
    def initialize(attr)
        @nom = attr[:nom]
        @prenom = attr[:prenom]
        @mail = attr[:mail]
    end
end
Nous avons de la chance, les deux clubs de la ville utilisent le même type d’objet pour définir leurs membres. Par contre nous avons été obligé de spécifier deux iterateur:
  • le premier permet de parcourir le tableau des membres et utilise la methode yield pour renvoyer l’objet membre à chaque itération.
  • le deuxième permet de parcourir les valeurs de la table Hash, mais utilise le même bloc avec yield pour renvoyer l’objet membre.

Et voici la partie utilisé par la mairie pour visualiser les membres des clubs de la ville.

class Asso
    attr :clubs
    def initialize()
        @clubs=[]
    end
    def afficher_liste_membre
        @clubs.each do |club|
            puts "club: #{club.name} " 
            club.each do |membre|
                puts "   #{membre.prenom} #{membre.nom} <#{membre.mail}>" 
            end
        end
    end
end

Testons tout ça.

mac_club = Mac_club.new
mac_club.membres.push(Membre.new({
   :nom => "Undeuxtrois", 
   :prenom => "Daniel", 
   :mail => "du@disquesize.com"}))
mac_club.membres.push(Membre.new({ 
   :nom => "Tiger", 
   :prenom => "Victor", 
   :mail => "tiger@jeportebienmonnom.com"}))
mac_club.membres.push(Membre.new({ 
   :nom => "Poirbell",
   :prenom => "Helene",
   :mail => "ln@glace.fr"}))
mac_club.membres.push(Membre.new({ 
   :nom => "Iloton",
   :prenom => "Frederic",
   :mail => "file@nofound.org"}))

as400_club = AS400_club.new
membre = Membre.new({ 
   :nom => "Sor", 
   :prenom => "Laurent", 
   :mail => "ls@rubynux.org"})
as400_club.membres.store(membre.nom,membre)
membre = Membre.new({ 
   :nom => "Anri", 
   :prenom => "Henri", 
   :mail => "ha@blaguedusoir.fr"})
as400_club.membres.store(membre.nom,membre)
membre = Membre.new({ 
   :nom => "Omplete", 
   :prenom => "Cristian", 
   :mail => "complete@presquefini.org"})
as400_club.membres.store(membre.nom,membre)

asso = Asso.new
asso.clubs.push(as400_club)
asso.clubs.push(mac_club)

asso.afficher_liste_membre 

# club: AS400 
#   Laurent Sor <ls@rubynux.org>
#   Henri Anri <ha@blaguedusoir.fr>
#   Cristian Omplete <complete@presquefini.org>
# club: Mac 
#   Daniel Undeuxtrois <du@disquesize.com>
#   Victor Tiger <tiger@jeportebienmonnom.com>
#   Helene Poirbell <ln@glace.fr>
#   Frederic Iloton <file@nofound.org>

Ruby offre des possibilité pour simplifier la réalisation de ce motif: voir l’exemple sur rubygarden. Cependant (même si ça me gène un peut de critiquer un auteur de rubygarden :( ) au delà de l’inspect, nos ne pouvons pas réaliser les même action sur chacun des objets récupéré par simple each. Mais c’est vrai que dans beaucoup de cas de figure, ça suffiras amplement ;-)

L’exemple à été modifié. L’utilisation de yield permet de simplifier grandement l’ancien exemple.