P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ NO I WILL NOT FIX YOUR COMPUTER Articles | Connexion
 
~Le motif Poids Mouche

 Présentation

L'utilisation des modèles de conception réutilisables permet dans bien des cas d'éclaircir l'architecture d'un programme ou d'en améliorer les performances. Malheureusement, ces notions se révèlent la plupart du temps exclusives.
 Sommaire


 Introduction

Le Design Pattern Poids Mouche (ou "flyweight" en anglais) est le dernier motif structural que nous allons étudier. Celui-ci désigne une technique de partage des ressources permettant la mise en oeuvre d'un grand nombre de petits objets. On parle dans ce cas d'objets de fine granularité. En programmation objet, la tentation pour le développeur de faire proliférer les classes et leurs instances est grande. Si conceptuellement une implémentation très élémentaire possède de nombreux avantages, la réalité s'avère bien moins heureuse car les performances peuvent s'en ressentir fortement.

Prenons l'exemple d'un simple éditeur de documents tel qu'un logiciel WYSIWYG pour HTML. Ce type d'éditeur requière une importante modularité dans ses fonctions de formatage. Par exemple, l'utilisateur pourra centrer aussi bien une image qu'un paragraphe de texte. En faisant appel à l'approche objet, le programme utilisera des objets pour représenter les divers éléments d'une page. Pourtant, aucun utilitaire de ce genre ne crée un objet pour chaque caractère bien qu'une telle implémentation pousserait la souplesse de l'application à son paroxysme. Les coûts engendrés par tous ces objets seraient pour le moins prohibitif.

Le modèle Poids Mouche décrit la manière de partager les objets de sorte que les performances de l'outil n'en souffrent pas. Un Poids Mouche désigne un objet partagé et utilisé dans plusieurs contextes simultanément. Le concept sous-jacent s'avère l'emploi de données intrinsèques et de données extrinsèques. Les premières permettent d'opérer la distinction entre deux instances du modèle tandis que les secondes dépendent du contexte d'utilisation. Seules les données intrinsèques sont partagées. Dans notre exemple d'éditeur HTML, l'implémentation du motif flyweight se matérialiserait par la génération de 26 objets caractère. L'état intrinsèque de ces objets serait un code ASCII et les états extrinsèques fourniraient les coordonnées d'affichage, la couleur et la mise en forme. Ainsi, nous obtiendrions bien moins d'objets que de caractères réels dans le document.


 Implémentation

Ce motif de conception ne doit pas se voir appliqué tout le temps car son efficacité dépend beaucoup des conditions d'utilisation. Dans la pratique, son emploi se justifie lorsque l'application utilise un grand nombre d'objets similaires, lorsqu'une telle population introduit un impact négatif sur les temps de traitement, lorsque la plupart des attributs de ces objets peuvent être considérés comme extrinsèques et enfin lorsque l'application ne repose pas sur leur identité (deux lettres "a" mais de couleurs différentes seront considérées identiques).

La mise en oeuvre de notre motif implique la présence de trois classes : le poids mouche, une fabrique de poids mouche et le client. La seconde classe, qui correspond à une utilisation du motif de conception du même nom, a pour rôle la création et la gestion des instances de la première. La troisième classe encapsule une référence aux poids mouche et leur fournit les états extrinsèques (souvent par le calcul).

Le code source du visualiseur de fichiers décrit dans l'article concernant PyQT dans ce même numéro nous servira de base. Ce programme affiche la liste des fichiers d'un disque dur et autorise leur parcours. Normalement, tous les fichiers présentés dans l'arbre possèdent la même icône. Nous souhaiterions modifier le logiciel afin que certains d'entre eux se voient attribué une icône correspondant à leur format. Concrètement, chaque feuille de l'arbre correspond à une instance de la classe File. Celle-ci possède une méthode de dessin que nous pouvons aisément modifier pour satisfaire nos exigences. Néanmoins, il serait idiot que 1000 fichiers MP3 engendrent la création de 1000 instances de File dont la méthode d'affichage sera identique. Il convient d'étudier quels sont les états intrinsèques et extrinsèques afin de procéder à une implémentation de notre Design Pattern.

Deux fichiers de même extension posséderont deux attributs communs : leur couleur et leur icône. Les données externes se révèlent bien plus nombreuses puisque nous avons le contexte graphique, le contexte de couleurs (couleur de sélection, de fond...), la largeur, la hauteur, le nom de l'élément et pour terminer s'il est sélectionné ou non. Le listing 1 présente le constructeur de la classe FileItem, notre Poids Mouche.

#Listing 1
class FileItem:
  def __init__(self, extension):
    if os.path.exists("images/file-dot-" + extension + ".png"):
      mime = extension
    else:
      mime = "plain"
    self.thumbnail = QImage("images/file-dot-" + \
  mime + ".png").smoothScale(16, 16)
    self.color = None
    if extension in self.colors:
      self.color = self.colors[extension]
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Ce constructeur initialise les attributs self.thumbnail, l'icône, et self.color, la couleur de l'élément. Ces derniers trouvent leur utilité dans la seule méthode de la classe, intitulée paint() et reproduite par le listing 2.

#Listing 2
def paint(self, p, cg, width, height, text, selected):
  p.eraseRect(0, 0, width, height)

  if selected:
    p.fillRect(0, 0, width, height, QBrush(cg.color(QColorGroup.Highlight), \
  QBrush.SolidPattern))
    p.setPen(cg.color(QColorGroup.HighlightedText))
  elif self.color:
    p.setPen(self.color)
  else:
    p.setPen(cg.color(QColorGroup.Text))

  p.drawImage(QPoint(2, 0), self.thumbnail)
  p.drawText(22, 13, text)
       
      
JextCopier dans Jext
L'ensemble des paramètres de celle-ci désigne les états extrinsèques de l'objet. Puisque notre outil ne peut offrir qu'un nombre limité d'icônes, nous pouvons créer un nombre fini d'instances de FileItem, une par extension connue. La fabrique de Poids Mouche va donc parcourir tous les icônes distribués avec l'application et, pour chacun d'eux, générer une instance de FileItem. Le code source complet de la fabrique se trouve dans le listing 3.

#Listing 3
class FileItemFactory:
  def __init__(self):
    self.files = { }
    self.generic = FileItem("")
    for file in os.listdir("images/"):
      extension = os.path.splitext(file)[0][9:]
      self.files[extension] = FileItem(extension)

  def getPainter(self, extension):
    if extension in self.files:
      return self.files[extension]
    else:
      return self.generic
       
      
JextCopier dans Jext
L'interface de programmation contient la méthode getPainter() qui retourne le Poids Mouche correspondant à l'extension de fichier donnée en paramètre. Notez que si le nombre d'extensions connues tend à proliférer, il serait sage d'opter pour la technique d'instanciation paresseuse. La création de la liste ne se ferait pas intégralement dans le constructeur, mais à la demande, élément par élément, dans la méthode getPainter(). Pour terminer, le programmeur doit gérer le poids mouche dans la classe File :

painter = factory.getPainter(self.extension)
painter.paint(p, cg, width, self.height(), self.text(0), self.isSelected())
       
      
JextCopier dans Jext
Avec une telle implémentation, la présence de 1000 fichiers MP3 n'entraînera plus la présence de 1000 méthodes similaires, mais d'une seule, partagée par tous les éléments. En mesurant les besoins de mémoire, vous constaterez très souvent que l'implémentation de ce motif de conception implique une large économie. N'hésitez donc pas à mettre en pratique celui-ci car son implémentation s'avère simple.


 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



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