Le motif composite
Définition
Ce motif permet de représenter des objets selon une hiérarchie composant/composé. C’est donc typiquement une représentation en arbre, avec ses branches et ses feuilles. Elle permet de mettre une couche d’abstraction entre le client et les éléments qu’il manipule, cela que ces éléments soient des composants ou des composés.
Principe
Pour réaliser ce motif, il faut une structure commune : une série d’opération que pourront effectuer aussi bien les composants que les composés. C’est une manière de faire abstraction des différences entre deux types d’objet (l’un qui compose l’autre) et donc d’avoir une arboresence d’objet, une grappe.
L’exemple par le code
Pour illustrer ce motif, j’ai repris l’exemple simple utilisé sur ruby garden [en] en une version légèrement adaptée.
Voici donc le premier élément, qui est un point commun au couple composant/composé.
class Component
def initialize(name="", description="")
@name = name
@description = description
end
def to_s(indent=0)
("-" * indent) + "#{@name} : #{@description}"
end
end
Voici ensuite le composé en lui-même.
class Composite < Component
def to_s(indent=0)
s = super(indent)
if defined? @children
@children.each do |child|
s += "\n#{child.to_s(indent+1)}"
end
end
s
end
def add(component)
@children = [] if not defined? @children
@children << component
end
end
Et enfin le composant.
class Leaf < Component
def initialize(name="", description="", action="")
@name = name
@description = description
@action = action
end
def to_s(indent=0)
s = super(indent)
s += " [#{@action}]"
end
end
Simple non ?
Voyons maintenant comment tester tout ça.
main_menu = Composite.new("Start","menu principal")
term = Leaf.new("xterm", "terminal", "xterm")
firefox = Leaf.new("Firefox", "Navigateur", "mozilla-firefox")
multimedia_menu = Composite.new("Multimedia", "Les sons, les fims")
multimedia_menu.add(Leaf.new("Sound Juicer", "Pour mettre en Ogg","sound-juicer"))
multimedia_menu.add(Leaf.new("xmms","Pour écouter les Ogg","xmms"))
main_menu.add(term)
main_menu.add(firefox)
main_menu.add(multimedia_menu)
puts main_menu.to_s()
Ce qui nous affiche :
Start : menu principal -xterm : terminal [xterm] -Firefox : Navigateur [mozilla-firefox] -Multimedia : Les sons, les fims --Sound Juicer : Pour mettre en Ogg [sound-juicer] --xmms : Pour écouter les Ogg [xmms]
Il est important de noter que ce motif s’associe très bien avec le motif iterator. Ici, c’est l’appel à la méthode
eachqui fait office d’itération, mais pour des objets plus complexes, nous pourrions très bien faire appel à des itérateurs externes aux composants/composés et itérer ainsi sur tout type d’objet.


