Les Design Patterns avec ModelMaker de Delphi 7

Design Pattern = Modèle de conception
Dans cet article nous allons passer en revue les principaux patterns livrés avec ModelMaker de Delphi 7 : Mediator, Wrapper, Visitor, Decorator, Observer, Autres patterns

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

La notion de Design Patterns a été cristallisée par le livre Design Patterns, Element of Reusable Object Oriented Software d'Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides (paru chez Addison Wesley en 1995)

Il ne s'agit pas d'un nouveau concept à la mode pour illuminés de la programmation.
Mais d'une réflexion de programmeurs sur leurs pratiques.

« Chaque pattern décrit un problème qui se présente souvent dans notre environnement, et décrit le cœur d'une solution à ce problème sur un mode qui permet d'utiliser cette solution des millions de fois, sans jamais avoir à refaire deux fois la même chose » (Christophe Alexander cité par Gamma, et al.)

A force de développer des classes et de les faire évoluer dans le temps, il apparaît au programmeur que certains dessins de relations entre classes se répètent. L'idée se forme alors, au fil de l'expérience, de regrouper des situations ayant été résolues par des dessins identiques de liaisons entre classes.

C'est de cette réflexion sur l'expérience que sont nés les Design Patterns.

Leur but est donc

  • de faciliter l'identification des problèmes d'architecture logicielle,
  • et de les rapporter au catalogue des solutions connues.

Pour les différencier des « trucs et astuces », on soulignera que les solutions des Design Patterns s'expriment avec un souci d'abstraction qui permet une utilisation concrète plus large de la solution.

II. Mediator

Commençons par un Design Pattern que nous utilisons tous les jours, peut-être sans le savoir.
Prenons par exemple les gestionnaires d'événements d'une forme : le clic sur un bouton déclenche

 
Sélectionnez
procedure Form0.Button1OnClick(Sender : TObject);
begin
  ShowMessage('T''as cliqué');
end;

L'événement OnClick est attaché à l'objet Button1, qui au sens Windows est une fenêtre.
Comment se fait-il donc que la déclaration de l'événement soit dans TForm1 et non dans TButton1 ?

 
Sélectionnez
TForm1 = class(TForm)
  Button1 : TButton;
  procedure Button1OnClick(Sender : TObject);
  ...
end;
et non 

TButton1 = class(TButton)
  procedure Click;
  ...
end;

Button1 ne va pas traiter directement le clic, mais envoyer à la forme parente le message BN_CLICKED.

La première écriture que nous utilisons dans Delphi suppose donc une redirection de l'événement du bouton Button1 vers le gestionnaire d'événement de la forme Form1.Button1OnClick

C'est le pattern Mediator qui est utilisé ici :

Il apporte une économie d'utilisation de Delphi puisque les gestionnaires d'événements remontés au niveau de la forme peuvent plus facilement interagir entre eux.

La TForm est le Mediator, et le bouton, le Mediated.

Ecrire le gestionnaire d'événement au sein de la TForm est devenu tellement naturel que l'on a oublié ce que l'on aurait du écrire sans ce mécanisme : une classe descendant de TButton, dans laquelle on aurait surchargé la méthode Click. Notre code s'en serait trouvé alourdi, d'autant que nous aurions eu à déclarer une classe descendante pour chaque composant de la forme.

Inscrire le gestionnaire dans TForm1 simplifie également l'apprentissage de Delphi : le bouton est posé dans la forme, le gestionnaire d'événement aussi. Cela parait plus logique pour celui qui débute, même si, en réalité, les choses se passent différemment : c'est le gestionnaire de TButton1 que Delphi construit en arrière-plan qui reçoit le clic et appelle à son tour Button1OnClick.

Le pattern Mediator se représente de la manière suivante :

Image non disponible

Nous avons là un pattern, car

  • Nous répondons à un problème récurrent : réduire le nombre de classes à manipuler et écrire plus simplement les interactions entre événements
  • par une solution qui peut se dessiner (design) sous une forme (pattern) assez générale pour en permettre la réutilisation.

Dans le cas qui nous occupe, le Mediator est la Forme Form1 : elle rassemble les événements des composants.
Le Mediated est le bouton Button1, (ainsi que tous les composants de la forme).
Et l'événement, liaison de médiation, est le Click.

Nota : La fonction Wire sert à suspendre la redirection d'événement de manière contrôlée par le programme.

Ce paragraphe nous a fait toucher du doigt un exemple de pattern.
Nous allons maintenant regarder quelques patterns très importants et voir comment ModelMaker nous aide à les mettre en pratique.

III. Wrapper

En Delphi, le polymorphisme découle de l'héritage : deux classes vont pouvoir être appelées dans la même méthode d'un ancêtre commun. Cela suppose donc qu'elles descendent de cet ancêtre. Par exemple (tiré de la documentation de ModelMaker), vous avez une classe TExemple qui descend de TObject. Vous souhaitez l'ajouter à la palette de composant de Delphi. Or les composants doivent descendre de TComponent. Vous pourriez, évidemment faire descendre TExemple de TComponent plutôt que de TObject… si vous aviez les sources. Mais supposons que vous ne les avez pas. Comment faire ? Il faut que TExemple soit vu comme un TComponent.

La solution qui le permet consiste en l'application du pattern Wrapper.

Par les relations uses que ce pattern met en œuvre, il fournit une manière d'approcher dans Delphi l'héritage multiple : la classe TExemple, une fois enveloppée dans le Wrapper, contiendra les méthodes et propriétés publiques de TExemple et les méthodes et propriétés publiques (et protégées) de TComponent.

Voyons directement comment nous faisons avec ModelMaker :

Nota : Pour l'utilisation de ModelMaker, veuillez vous reporter à l'article ModelMaker de Delphi 7

Nous ouvrons un projet dans ModelMaker, et nous affichons un nouveau diagramme de classes.

(F5 et clic sur l'icône Add Diagram Class, la première des 12 icônes de diagramme à gauche).

Image non disponible

Nous ajoutons 2 classes en cliquant sur l'icône Add Class to Model

Image non disponible

La première classe est nommée TExemple et mise en Placeholder (case à cocher au milieu à droite du panneau Class Symbol) car nous n'avons pas son code.

Avant de placer la deuxième classe qui devra descendre de TComponent, nous ajoutons TComponent en tant que Placeholder, (en procédant comme pour TExemple).

Puis la deuxième classe est ajoutée. Nous la nommons TWrapperExemple : nous indiquons dans le panneau Class Symbol qu'elle descend de TComponent. Cette classe n'est pas en Placeholder : nous allons créer son code.

A cette étape, notre diagramme doit comporter 3 rectangles. Pour faire apparaître les relations, nous faisons Ctrl+A pour sélectionner les 3 classes. Nous cliquons droit et choisissons Wizards dans le menu contextuel, puis Visualize Class Relations.
Le diagramme doit avoir alors l'allure suivante :

Image non disponible

Imaginons que la documentation de TExemple (dont nous n'avons pas le code) nous indique cependant qu'elle contient la propriété Valeur et la méthode Calculer. Nous allons pouvoir ajouter ces éléments à notre modèle : nous cliquons sur TExemple dans le diagramme. Elle doit avoir le focus dans la hiérarchie des classes en haut à droite :

Image non disponible

Nous ajoutons la propriété Valeur en cliquant sur Image non disponible Write Access est mis à Field et nous cliquons sur Ok.

Nous ajoutons la méthode Calculer en cliquant sur Image non disponible Nous cliquons sur Ok.

Nota : À ce niveau, il peut être sage de sauvegarder le modèle en cliquant sur l'icône disquette.

Maintenant, nous allons ajouter à TWrapperExemple une propriété TExemple : en effet, c'est par une relation de ce type, appelée uses que TWrapperExemple pourra atteindre les méthodes et propriétés de TExemple.

Dans le diagramme de classe à droite, nous cliquons sur Image non disponible, puis nous effectuons un glisser-déplacer du rectangle WrapperExemple vers le rectangle TExemple. La propriété est automatiquement créée.

ModelMaker nous a servi jusqu'à présent d'outil de dessin. Mais il va nous rendre grand service car nous allons respecter une des règles de la programmation objet : un objet ne doit pas appeler les sous-méthodes d'un autre objet. Cette règle, appelée règle de Demeter signifie qu'un utilisateur de notre WrapperExemple qui veut provoquer un calcul ne devra pas écrire WrapperExemple1.Exemple.Calculer mais WrapperExemple1.Calculer. Pourquoi ? Eh bien parce que si l'on fait remonter à plus de 1 niveau les propriétés et méthodes, on se trouve rapidement devant une structure complexe : toute modification dans une méthode (par exemple changer le nom de Calculer) devra se propager à tous les objets utilisant un TWrapperExemple. Ceci va à contre-courant de la règle d'encapsulation de la programmation objet. En revanche, si nous créons dans TWrapperExemple une méthode Calculer qui fera appel à Exemple.Calculer nous contenons les effets de propagation d'un changement de nom à la seule classe TWrapperExemple. Les objets qui utilisent TWrapperExemple pourront continuer d'appeler Calculer sans se soucier des modifications internes.

C'est par le respect de cette discipline d'écriture que l'on obtient des programmes faciles à lire et à maintenir. Elle est à mettre en œuvre dès le début du projet, justement là où, tout étant simple, on serait tenté de ne pas l'appliquer.

ModelMaker va nous aider à construire automatiquement toutes les méthodes d'encapsulation :

Cliquez sur l'icône Patterns Image non disponible situé tout en haut.
Sélectionnez dans la hiérarchie de gauche la classe TWrapperExemple.
et en bas, le champ Exemple. Choisissez dans l'onglet Structural la pattern Wrapper (le plus à gauche).
Un panneau s'ouvre. Passez tout à droite et cliquez sur Ok : toutes les méthodes sont créées.

Nota : Sur cet exemple simplifié à l'extrême pour la clarté de l'exposé, vous pouvez penser que vous auriez été plus vite à la main. Mais sur des exemples réels le gain est très important : vous pouvez réaliser en un clic le travail qui vous aurait pris des heures.

Votre diagramme de classe devrait vous apparaître comme ceci :

Image non disponible

Pour voir les propriétés dans le diagramme, cliquez droit dedans. Dans le menu contextuel, choisissez Diagram properties. Décochez Project member type filter et cochez les 4 cases sous Custom member type filter et Ok.

Voyons maintenant comment ModelMaker gère la relation de pattern que nous avons mise en place.
Cliquez en bas à droite sur la méthode Calculer de TWrapperExemple. Puis regardez l'implémentation (F6). Essayez d'en modifier le code Exemple.Calculer;. Vous ne pouvez pas le faire. En effet, ModelMaker considère que le « propriétaire » de cette méthode est le pattern que vous avez mis en place. Maintenant, allez sur la méthode Calculer de TExemple et changez son nom, par exemple en CalculerTick. Ce changement nom est répercuté à tous les niveaux, y compris dans le nom de la méthode Calculer de TWrapperExemple qui devient CalculerTick.

Mais nous avions dit plus haut qu'il ne devait pas y avoir de propagation !

Ici, non seulement il y a propagation, mais de plus, si vous allez à nouveau dans la liste des Patterns et que vous supprimez par le menu contextuel (clic droit) le pattern que vous venez de mettre en place toutes les méthodes de « liaisons » vont être supprimées : le couplage est donc total.

Réfléchissons 2 minutes : lors de l'établissement d'un modèle, il y a une première phase rapide, dans laquelle on pose les noms des classes, des méthodes et des propriétés, et dans laquelle on ajoute des liens de type Pattern. Il peut y avoir des fautes de frappe, des noms à changer, des tests fait sur plusieurs architectures possibles : quelle facilité de pouvoir corriger, modifier ou supprimer un élément et de voir le modèle entier conserver sa cohérence.

Une fois cette phase terminée, et pour disposer de l'indépendance souhaitable, nous procéderons au découplage du pattern. Pour ce faire, vous cliquez droit sur le pattern dans la liste des patterns (Icône Image non disponible) et vous choisissez Release ownership. Si vous revenez maintenant sur TExemple pour changer le nom de Calculer, il n'y a plus de propagation. Vous devrez même aller changer à la main Exemple.Calculer;, mais dans la seule classe TWrapperExemple.
(Faites le découplage au bon moment, car il est irréversible).

Vous pouvez télécharger le modèle ici

En résumé, le pattern Wrapper se présente ainsi :

Image non disponible

Il permet qu'un objet soit vu comme le descendant d'une autre hiérarchie : ici TWrapped sera vu dans l'application au travers de TWrapper avec l'ascendance de TWrapper.

Après avoir traité complètement un pattern dans ModelMaker, nous allons passer en revue d'autres patterns proposés par ModelMaker.

IV. Visitor

Ce pattern permet d'ajouter des méthodes à une hiérarchie de classes mais en les plaçant dans une autre hiérarchie.

Prenons l'exemple d'une gestion de prêts bancaires :

Image non disponible

Ces prêts peuvent être calculés selon plusieurs méthodes financières. Au lieu de mettre ces méthodes dans la hiérarchie des prêts, nous les mettons dans une hiérarchie propre :

Image non disponible

Ceci présente plusieurs avantages :
La gestion du prêt est rassemblée dans son objet propre, tandis que les outils de calculs le sont dans un autre. La maintenance du code est plus aisée et peut même être confiée à des personnes différentes.

De plus, si on ajoute une nouvelle méthode de calcul de prêt, par exemple les prêts à taux variables, celle-ci peut être facilement ajoutée à sa propre hiérarchie TCalculateur sans avoir à modifier la classe des prêts : il suffit d'y mettre une procédure générique qui parcourt les enfants de TCalculateur pour avoir les résultats du coût d'un prêt selon les différents méthodes de calcul.

Image non disponible

Le schéma général du pattern Visitor se présente ainsi :

Image non disponible

V. Decorator

Ce pattern va permettre d'ajouter des propriétés et des méthodes à une classe en les plaçant « à côté » plutôt qu'à l'« intérieur » ou par héritage. Cela permet une articulation plus facilement modulable et localisable des parties de la classe. Le pattern Decorator enveloppe le tout (le décore) pour que cela apparaisse au reste de l'application comme une classe unique :

Image non disponible

VI. Observer

Ce pattern implémente un système de sémaphore qui prévient les objets qui sont reliés à un objet maître d'un changement d'état de ce dernier :

Image non disponible

VII. Autres patterns

Lock

Ce pattern permet de mettre en lecture seule une partie des propriétés d'une classe.
Il est utilisé dans Delphi pour mettre un TStrings en lecture seule.

Image non disponible

Singleton

Ce pattern est utile quand une classe ne doit avoir qu'une seule instance : par exemple si cette classe est celle d'un objet qui doit être unique.

Image non disponible

Reference count

Ce pattern implémente un compteur du nombre d'instances d'un objet. Lorsque l'objet n'est plus référencé, il s'autodétruit.

Image non disponible

VIII. Conclusion

Nous avons passé en revue les principaux patterns proposés par ModelMaker.

Le fait que ModelMaker institue le pattern comme propriétaire du code du pattern (pour modifier ces parties du code ModelMaker vous oblige à intervenir au niveau du pattern : vous ne pouvez le faire directement) permet de bien isoler ce code : c'est une grande aide que d'isoler ainsi le code du pattern de celui du reste de la classe.

Quand vous verrez qu'en outre vous pouvez modifier les patterns proposés et même ajouter les vôtres, vous serez à même de mesurer la puissance que procure une bonne utilisation de ModelMaker livré avec Delphi Entreprise et Delphi Architect.

Pour compléter cet article, consultez :

Interbase
Ma première base InterBase
Delphi 6 et InterBase 6
dbExpress avec Delphi 6
Delphi 6 Relationnel
Le bon PLAN d'InterBase
MDA (Model Driven Architecture)
MDA
Programmez avec les diagrammes de Delphi 7
Les objets métiers avec ModelMaker de Delphi 7
Les Design Patterns avec ModelMaker de Delphi 7
Autres articles
Les transactions
Mise à jour de Delphi 6

  

Copyright © 2002 Henry Cesbron Lavau. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.