P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ I TOOK THE RED PILL Articles | Connexion
 
~Le motif Médiateur

 Présentation

Certains programmes se révèlent plus adaptés que d'autres pour l'implémentation des motifs de conception objets. La grande majorité des programmes dotés d'une interface graphique emploient ou peuvent employer ces motifs.
 Sommaire


 Introduction

Dans ce même numéro, le développeur découvrira un article concernant la programmation d'interfaces graphiques en Python par l'intermédiaire de la librairie PyQT. Cet article exploite le code source d'un petit éditeur de texte fort simple. En nous basant sur le code source de cette application, nous allons ici découvrir comment implémenter le motif de conception intitulé Médiateur.

Un Médiateur caractérise un objet dont le rôle se veut d'encapsuler les interactions d'un ensemble d'objets. L'intérêt principal de ce motif consiste à introduire une notion d'interaction (ou couplage) faible. Lors de l'écriture d'une application graphique, les programmeurs "orientés objet" ont souvent tendance à favoriser la prolifération de petites classes définissant leurs divers composants. S'il s'agit d'une pratique très appréciable en terme de réutilisation de l'existant, ce procédé implique malheureusement dans les faits une augmentation remarquable de la complexité de l'architecture logicielle. Cette complexité se trouve due à des interactions multiples entre les divers composants qui les rendent tous plus ou moins tributaires les uns des autres. Ainsi après avoir passé beaucoup de temps à développer un composant générique et en théorie réutilisable, de nombreux programmeurs détruisent son indépendance en introduisant le code de gestion de l'interface.

Prenons l'exemple d'un logiciel présentant une fenêtre comprenant une liste de données, un bouton et un champ de saisie. Lorsque l'utilisateur sélectionne un élément de la liste, le bouton et le champ textes s'activent. Enfin, un clic sur le bouton provoque la désactivation du champ et de lui-même, et annule enfin la sélection au sein de la liste. Le premier réflexe de tout programmeur sera de saisir le code relatif à l'interaction entre ces composants dans la classe contenant la fenêtre. Nous obtiendrions ainsi un code source aux relations multiples comme le montre le schéma 1. Ce dernier expose bien le paradoxe : l'application stricte du paradigme objet conduit à une implémentation monolithique.



Schéma 1. Interconnexions multiples, les dépendances sont trop nombreuses.


 Sortez les guitares

On peut éviter ce problème de conception en jouant sur l'encapsulation du comportement collectif dans un objet médiateur distinct. Cet objet sera ainsi responsable des interactions, en les coordonnant, d'un groupe d'objets. Sa nature d'intermédiaire permet aux objets de ne plus faire de références explicites les uns aux autres car ils ne connaissent que le seul médiateur.

L'application de ce motif de conception peut survenir dans deux situations majeures : lorsque le résultat d'interdépendances entre des objets communiquant rend le système particulièrement ardu à comprendre et quand la réutilisation d'un objet devient très difficile, voire impossible, du fait ses dépendances.

En implémentant un motif médiateur dans la situation présentée par le schéma 1, nous aboutirions à des relations décrites par le schéma 2. Ces relations se révèlent univoques et favorisent grandement l'appréhension de l'architecture du logiciel. Un point important doit rester constamment à l'esprit du développeur soucieux d'utiliser le Médiateur pour ses bienfaits : l'implémentation de ce motif échange une complexité entre composants distribués pour une complexité centralisée. Il est avéré qu'un médiateur puisse devenir un composant lourd, monolithique et difficile à maintenir. Toutefois, nous savons toujours où intervenir et le parcours du code sera toujours plus simple qu'auparavant.



Schéma 2. Après implémentation du motif Médiateur, les dépendances sont centralisées.

Les objets faisant appel aux diverses méthodes du médiateur s'appellent les collègues. Chacun de ces collègues s'adresse à son médiateur chaque fois qu'il aurait dû s'adresser à un autre collègue de l'environnement. Cela nécessite donc qu'ils connaissent l'objet médiateur employé. Vous trouverez sur le CD-Rom accompagnant ce magazine un fichier nommé mediateur.py contenant l'intégralité du code source de notre exemple.


 Découverte des collègues

Nous souhaitons ajouter à notre éditeur de texte trois boutons dans la barre d'outils donnant accès aux fonctionnalités du presse-papiers : Copier, Couper et Coller. De plus, nous désirons afficher en permanence le contenu dudit presse-papiers par l'entremise d'une zone de texte, elle aussi positionnée dans la barre d'outils. Enfin, un quatrième bouton offre la possibilité à l'utilisateur d'effacer le contenu du presse-papiers. En étudiant les interactions possibles, nous constatons que les boutons Copier, Couper et Effacer doivent interagir avec la zone de texte de l'éditeur et avec le champ accueillant le contenu du presse-papiers. Nous sommes ici dans un cas tout à fait propice à l'application du motif Médiateur.

Outre ce dernier, notre logiciel implémente aussi le motif Commande que nous avons déjà étudié auparavant. Chacun de nos boutons hérite de la classe EdButton qui encapsule le médiateur. Cette classe hérite d'une classe de widget ainsi que de l'interface EdCommand. Conformément aux spécifications du motif correspondant, notre classe de commande ne propose que la méthode execute(). Le principe sera le suivant : la fenêtre insère les divers boutons dans sa barre d'outils et lie un signal particulier, nommé "action", à un réceptacle invoquant la commande. De la sorte, chaque bouton, dérivé de EdButton, surchargera la méthode execute() afin de communiquer avec le médiateur.

Voici la ligne de code autorisant la création d'un de nos boutons spécialisés (ici le bouton Copier) :

self.textCopy  = CopyButton(toolBar,  self.med)
QObject.connect(self.textCopy,  PYSIGNAL("action"), self.commandEvent)
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
L'argument self.med désigne le médiateur. La seconde ligne lie le signal d'action (un clic sur le bouton) à la méthode chargée d'exécuter la commande :

def commandEvent(self, source):
  source.execute()
       
      
JextCopier dans Jext
Sachez simplement que le signal "action" désigne un signal particulier créé de toutes pièces par notre classe abstraite EdButton. Le listing numéro 1 propose enfin le code source complet du bouton "Copier". Le contenu de la méthode execute() se veut extrêmement concis et pourtant lors de son exécution, une interconnexion s'opère entre le bouton, la zone de texte et le champ du presse-papiers.


 L'implémentation du médiateur

La mise en place du médiateur impose que ce dernier prenne connaissance des composants entre lesquels des interactions seront effectuées. Un tel objet se dote donc naturellement de méthodes d'enregistrement des objets ainsi que le démontre le listing 2. Pour terminer, le programmeur doit simplement ajouter au médiateur les méthodes invoquées par ses collègues. Le listing 3 en propose un exemple, toujours relatif au bouton Copier. La méthode copy() fait référence à deux composants graphiques, réalisant la connexion entre le bouton, l'éditeur et le champ de texte. L'implémentation des méthodes cut(), paste() et clear() se trouve régie par les mêmes principes.

class CopyButton(EdButton, EdCommand):
  def __init__(self, parent, mediator):
    EdButton.__init__(self, parent, mediator, "Copy")

  def execute(self):
    self.mediator.copy()
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
class Mediator:
  def registerClipboard(self, clipboard):
    self.clipboard = clipboard

  def registerTextView(self, textView):
    self.textView = textView
       
      
JextCopier dans Jext
def copy(self):
  if self.textView is not None:
    self.textView.copy()
    if self.clipboard is not None:
      self.clipboard.setText(qApp.clipboard().text())
       
      
JextCopier dans Jext

 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