le guide du développeur java 2 - eyrolles.com · le guide du d é veloppeur java 2 le livre ......

27
Le guide du développeur Java 2 Pierre-Yves Saumont Antoine Mirecourt Le guide du développeur Java 2 © Groupe Eyrolles, 2003, ISBN : 2-212 -11275-0

Upload: dohanh

Post on 09-Jul-2018

222 views

Category:

Documents


1 download

TRANSCRIPT

Le guide du développeur

Java 2

P i e r r e - Y v e s S a u m o n t

A n t o i n e M i r e c o u r t

Le guide du développeur

Java 2

© Groupe Eyrolles, 2003, ISBN : 2-212 -11275-0

723

23

Utilisation des design patterns

Une des avancées majeures de la programmation orientée objet a été la recon-naissance des design patterns. L’expression vient du titre d’un livre publié parquatre auteurs, Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides,qui sont généralement désignés collectivement par l’expression GoF, qui signifieGang of Four (le gang des quatre). Le sous-titre du livre est une excellente défini-tion des design patterns : Elements of reusable object-oriented software. Lesdesign patterns sont en effet des techniques permettant d’augmenter la produc-tivité des développeurs grâce à l’adoption d’éléments architecturaux réutilisa-bles. Il est important de comprendre qu’il ne s’agit pas de code réutilisable, maisd’architecture. Les design patterns sont des éléments architecturaux invariants.Il est ainsi apparu aux auteurs que la plupart des problèmes traités par des pro-grammes informatiques pouvaient être décomposés en problèmes élémentairescommuns à de nombreuses applications. La reconnaissance de ces invariantsétait le premier pas. Le second consistait à définir des solutions réutilisablespour traiter ces problèmes. Il faut noter que l’apport de cette technique ne con-cerne pas seulement la productivité lors de l’écriture du code. La création d’unvocabulaire commun à tous les développeurs pour désigner les solutions récur-rentes aux problèmes des logiciels est un élément très important favorisant lacommunication entre les équipes de développement.

Dans ce chapitre

Le pattern Modèle/Vue/ContrôleurStructure de l’application HelloMVCImplémentation de l’application HelloMVCUtilisation de l’application HelloMVCAjout de composants à l’application HelloMVCLe pattern Proxy

Copyright © 2003 G

roupe Eyrolles

724

Le

Gu

ide

du D

ével

oppe

ur

Java

2 Le livre Design Patterns présente vingt-trois patterns. Ce ne sont évidemment pas lesseuls, et d’autres ont été découverts depuis (le livre date de 1994, c’est-à-dire un an avantles premiers balbutiements de Java, et les exemples qu’il présente sont écrits en C++ ouen SmallTalk). Nous avons déjà étudié certains de ces patterns dans les chapitres précé-dents. Dans ce chapitre, nous présenterons deux patterns qui nous paraissent figurerparmi les plus importants pour le développeur Java : Modèle/Vue/Contrôleur (MVC) etProxy.

Le pattern Modèle/Vue/ContrôleurLe pattern Modèle/Vue/Contrôleur est un des plus importants, bien qu’il ne figure pasdans le livre du GoF. Il consiste à définir une application sous la forme de trois compo-sants essentiels :

� le modèle, responsable du traitement de l’information ;

� la vue, responsable de la présentation des résultats ;

� le contrôleur, chargé de faire fonctionner le tout, et en particulier d’obtenir les infor-mations dont le modèle a besoin ;

Prenons l’exemple d’un jeu vidéo très simple représentant un terrain de squash. Le jeu estcomposé de trois murs, à gauche, à droite et en haut, d’une raquette positionnée le longdu côté inférieur et se déplaçant latéralement, et d’une balle. Le jeu consiste à frapper laballe qui rebondit sur les murs, et ce le plus longtemps possible.Dans un tel jeu, on peut distinguer facilement les trois fonctions modèle, vue etcontrôleur :

� le modèle est chargé de calculer la nouvelle position de la balle en fonction de sa posi-tion initiale, de sa vitesse, de sa direction, de la position de la raquette et de celle desmurs. Il déplace la balle, vérifie si celle-ci a heurté un mur ou la raquette, modifie ladirection en conséquence, etc. Il faut noter que le modèle n’a aucun besoin des’encombrer de quelque considération relative à l’affichage. Il utilise le système decoordonnées le plus pratique, avec des unités abstraites ;

� la vue est chargée de traduire l’état du modèle à un moment donné en affichage. Cetravail peut impliquer un changement du système de coordonnées ;

� le contrôleur lit les entrées de l’utilisateur (déplacement de la raquette) et les transmetau modèle afin qu’il puisse calculer la trajectoire de la balle.

L’utilisation d’une telle architecture offre un avantage essentiel. S’il devient nécessaire dechanger de vue, cela peut être fait sans remettre en cause le modèle. Il est ainsi possibled’utiliser une vue inversée (raquette en haut de l’écran) ou déformée. Ce dernier point estfondamental : le jeu fonctionnera ainsi en plein écran quelle que soit la résolution, sansqu’il soit nécessaire de modifier en quoi que ce soit le fonctionnement du modèle. Un teljeu pourra ainsi disposer d’une vue pour ordinateur portable et d’une autre destinée àl’affichage d’un téléphone mobile ! Il suffira de créer une nouvelle vue, et cela sans risquerd’introduire des bogues dans le modèle.L’architecture MVC est utilisée dans divers domaines par Java. C’est le cas, par exemple,pour les composants Swing que nous avons étudiés dans le chapitre 17. Elle est égale-ment très souvent mise en œuvre pour les applications Web composées de servlets et de

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

725

CH

APITRE 23 Utilisation

des designpattern

s

JSP. À titre d’exemple, nous allons créer une application MVC minimale. Il s’agira d’uneversion du classique programme « Hello World », identique dans son résultat à ceux quenous avons créés dans le chapitre 2. Nous la compliquerons toutefois un peu afin qu’ellepuisse afficher un message de bienvenue en trois langues différentes, en fonction dedonnées contenues dans un fichier de paramètres. Nous créerons tout d’abord une vue enmode texte, puis nous ajouterons une vue en mode fenêtré. Enfin, nous verronscomment la modularité d’une telle application permet d’ajouter des vues toutes faites enréférençant simplement un nouveau composant. Nous utiliserons pour cela une vue déjàcompilée et fournie sur le CD-Rom accompagnant ce livre, qui permettra d’envoyer lerésultat de l’application par mail.

Structure de l’application HelloMVCLa figure 23.1 montre la structure de l’application complète exprimée sous la forme d’undiagramme de classes UML. Cette figure a été réalisée à l’aide de Poseidon for UML, quifigure sur le CD-Rom accompagnant ce livre.

Figure 23.1Diagramme de classes UML de l’application HelloMvc.

<<interface>>

Controller

~welcome(t:String[])

GenericModel

#name : String

#start : String

#end : String

+process(name:String) : String

HelloMvc

+main(argv:String[])

<<interface>>

Model

~process(name:String) : String

ModelEn

~ModelEn() : ModelEn

ModelEs

~ModelEs() : ModelEs

ModelFactory

~getInstance(languageCode:String) : Model

ModelFr

~ModelFr() : ModelFr

MyController

-name : String

-languageCode : String

-result : String

+welcome(t:String[])

<<interface>>

View

~display(message:String)

ViewDos

+display(s:String)

ViewFactory

~getInstance() : View

ViewMail

-mailServer : String

-address : String

-fromAddress : String

-fromName : String

-mailSubject : String

~ViewMail() : ViewMail

+display(s:String)

ViewWindow

+ViewWindow() : ViewWindow

+display(message:String)

-controller

Model<-GenericModel

GenericModel<-ModelEn

GenericModel<-ModelEsGenericModel<-ModelFr

-model

Controller<-MyController

-view

View<-ViewDos View<-ViewMail View<-ViewWindow

Copyright © 2003 G

roupe Eyrolles

726

Le

Gu

ide

du D

ével

oppe

ur

Java

2 En examinant cette figure, vous constaterez que la classe principale est HelloMvc. C’est ellequi contiendra la méthode main permettant de lancer l’application. Cette classe est asso-ciée à une interface appelée Controller. C’est là le seul lien entre la classe principale et lereste de l’application (figure 23.2).

La classe MyController implémente l’interface Controller. Elle possède deux associationsavec les interfaces Model et View. Les références à ces interfaces sont obtenues par l’inter-médiaire des classes ModelFactory et ViewFactory, qui sont chargées de déterminer lesimplémentations correctes de ces deux interfaces (figure 24.5).Nous avons représenté sur le diagramme trois implémentations possibles de l’interfaceView. En ce qui concerne le modèle, nous avons utilisé une classe abstraite nommée Gene-ricModel. En effet, les trois implémentations ne diffèrent que par les données linguisti-

Figure 23.2La classe principale de l’application ne possède qu’une seule association avec une interface.

Meilleures pratiques

L�association entre la classe HelloMvc et l�interface Controller est un exemple d�un concept trèsimportant en programmation orientée objet : programmer pour des interfaces. Dans ce typed�association, la classe utilisatrice dépend de l�élément utilisé. Cette dépendance est de naturefonctionnelle, et non essentielle, ce qui signifie que la classe utilisatrice n�a pas à connaître lafaçon dont la classe utilisée accomplit son travail, ou remplit son contrat, selon la terminologieconsacrée. Seule la nature du contrat est importante. C�est exactement le concept exprimé par uneinterface. Ici, l�interface Controlleur exprime un contrat consistant à mettre à la disposition de laclasse qui l�utilise une méthode nommée welcome, sans valeur de retour et prenant pour paramètreun tableau de chaînes de caractères.

Meilleures pratiques

Un des principes fondamentaux d�une programmation efficace consiste à réduire au maximum lesdépendances entre les objets. Une application sera d�autant plus fiable et évolutive que le nombrede dépendances rapporté au nombre d�objets sera faible. Dans la pratique, cela peut être exprimépar les faits suivants :

� la majorité des classes ont une ou deux dépendances au maximum ;

� l�ensemble des dépendances forme un graphe sans boucle, ce qui correspond au fait qu�enparcourant les dépendances, quelles que soient la classe de départ et la classe d�arrivée, iln�existe qu�un seul chemin possible.

Vous pouvez constater que c�est bien le cas sur le diagramme de classes de notre application. Nousaurions pu organiser les dépendances d�une autre façon en respectant cette règle, comme sur lafigure 23.3. En revanche, la figure 23.4 montre un exemple qui contient une boucle.

<<interface>>

Controller

~welcome(t:String[])

HelloMvc

+main(argv:String[])

-controller

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

727

CH

APITRE 23 Utilisation

des designpattern

s

ques. En fait, l’implémentation proprement dite de la méthode process est commune auxtrois classes. Comme il n’est pas possible de placer cette implémentation dans une inter-face, nous utilisons une classe abstraite.

Implémentation de l’application HelloMVCMaintenant que nous avons défini la structure de l’application, nous pouvons facilementcoder les différents éléments. Nous allons les décrire brièvement un par un, en commen-çant par la classe principale. Notez que, dans la pratique, il vaut mieux commencer parécrire les éléments les moins dépendants, et en particulier les interfaces. Si vous utilisezun IDE, cela vous évitera au moins les messages d’erreur affichés lorsque vos référencerezdes classes ou des interfaces que vous n’aurez pas encore écrites.

Figure 23.3Ce modèle est correct.

Figure 23.4Ce modèle doit être évité, car il contient des dépendances inutiles.

<<interface>>

Controller

~welcome(t:String[])

HelloMvc

+main(argv:String[]) MyController

-name : String

-languageCode : String

-result : String

+welcome(t:String[])

<<interface>>

View

~display(message:String)

-controllerController<-MyController

-view

<<interface>>

Controller

~welcome(t:String[])

HelloMvc

+main(argv:String[]) MyController

-name : String

-languageCode : String

-result : String

+welcome(t:String[])

<<interface>>

View

~display(message:String)

-controllerController<-MyController

-view

Copyright © 2003 G

roupe Eyrolles

728

Le

Gu

ide

du D

ével

oppe

ur

Java

2

La classe HelloMvcLa classe principale, HelloMvc, est très simple :

Elle contient simplement une méthode main qui commence par initialiser un contrôleur,puis appelle sa méthode welcome en lui passant le tableau des paramètres de la ligne decommande.

Figure 23.5La classe MyController possède deux associations avec les interfaces View et Model.

Design pattern : Factory

Nous utilisons ici le design pattern Factory pour déterminer les implémentations du modèle et de lavue. Ce design pattern a été présenté dans le chapitre 9.

Meilleures pratiques

Il est impératif d�éviter toute duplication du code qui est toujours source de problème demaintenance. Lorsque le code est dupliqué et doit être modifié, il faut modifier toutes les copies. Ilest facile d�en oublier ! L�utilisation d�une classe abstraite permet d�éviter ce problème. Noteztoutefois, que cela ne doit pas empêcher d�appliquer le principe Programmer pour des interfaces.Ici, la classe GenericModel implémente l�interface Model et est étendue par les classes ModelEn,ModelEs et ModelFr. De cette façon, le contrôleur ne dépend que de l�interface Model.

package com.volgadev.devjava.chap22.hellomvc;

public class HelloMvc {

public static void main(String[] argv) { Controller controller = new MyController(); controller.welcome(argv); }}

<<interface>>

Model

~process(name:String) : String

ModelFactory

~getInstance(languageCode:String) : Model

MyController

-name : String

-languageCode : String

-result : String

+welcome(t:String[])

<<interface>>

View

~display(message:String)

ViewFactory

~getInstance() : View

-model

-view

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

729

CH

APITRE 23 Utilisation

des designpattern

s

L’interface ControllerL’interface Controller ne définit qu’une seule méthode, welcome, de type void et qui prendpour paramètre le tableau des arguments de la ligne de commande :

La classe MyControllerLa classe MyController est la plus complexe de l’application. Elle définit trois variables detype String, représentant le nom de l’utilisateur, un code correspondant au langageemployé et la chaîne à afficher comme résultat du traitement :

Meilleures pratiques

Notez l�utilisation de la syntaxe :

Controller controller = new MyController();

dans laquelle nous attribuons à une référence de type Controller un objet de type MyController.Cette façon de faire est conseillée chaque fois qu�il n�est pas possible d�instancier un objet sansfaire référence à sa classe. Il serait préférable d�utiliser le pattern Factory et d�employer uneméthode renvoyant une référence de type Controller, comme nous le ferons pour le modèle etpour la vue. Nous avons procédé ainsi pour vous montrer l�inconvénient qui en résulte. Cetinconvénient est que notre programme est inutilement dépendant de la classe MyController, quiest l�implémentation, au lieu de dépendre uniquement de l�interface Controller. Pour réduire auminimum cette dépendance, nous affectons immédiatement l�objet créé à une référence du typede l�interface. Cette pratique est un coding pattern très courant. Un de ses avantages est que si,pour une raison ou une autre, nous devons utiliser une autre implémentation de l�interfaceController, nous n�aurons qu�une seule modification à effectuer.

package com.volgadev.devjava.chap22.hellomvc;

interface Controller {

void welcome(String[] t);}

package com.volgadev.devjava.chap22.hellomvc;

class MyController implements Controller {

private String name; private String languageCode; private String result; private Model model; private View view;

public void welcome(String[] t) {

int sw = Math.min(t.length, 2); switch (sw) { case 2: if (t[1] != null) { languageCode = t[1].toLowerCase();

Copyright © 2003 G

roupe Eyrolles

730

Le

Gu

ide

du D

ével

oppe

ur

Java

2

Elle définit également les variables model et view, qui référenceront respectivement lemodèle et la vue, et sont donc des types des interfaces correspondantes.La méthode welcome analyse tout d’abord le tableau des arguments reçu. L’instruction :

nous permet de nous assurer que seul les deux premiers arguments seront pris en compte.Les autres arguments éventuels seront ignorés. L’instruction switch qui suit est unexemple de fall through. Dans le cas case 2:, le deuxième argument est non null, il estconverti en minuscule et affecté à la variable languageCode. Le cas case 1: est ensuiteexécuté et la valeur du premier argument est affectée à la variable name.

Une fois les arguments décodés, le contrôleur utilise la classe ModelFactory pour obtenirune instance de modèle correspondant à la valeur de la variable languageCode :

La ligne suivante fait exactement la même chose pour la vue, à la différence qu’aucunparamètre n’est passé à la classe ViewFactory :

Nous verrons plus loin comment cette classe sélectionne l’implémentation adéquate.À l’avant-dernière ligne, le contrôleur utilise le modèle pour effectuer le traitement desdonnées et affecte le résultat à la variable result :

} case 1: this.name = t[0]; break; } this.model = ModelFactory.getInstance(this.languageCode); this.view = ViewFactory.getInstance(); this.result = model.process(name); this.view.display(result); }}

int sw = Math.min(t.length, 2);

NOTE On voit ici, comme au chapitre précédent, un exemple de séparation entre les tâches utili-taires et le « business process ». Le décodage des arguments est un processus qui peutêtre complexe, mais qui n�a rien à voir avec le traitement des données. Il est donc préféra-ble de le séparer complètement de celui-ci. Ici, les arguments sont décodés par le contrô-leur, et les données qui en résultent sont traitées par le modèle. De cette façon, le modèlepourra être réutilisé par un autre contrôleur, qui pourra par exemple lire les argumentsdans une base de données. Nous sommes ici en présence d�un double contrat : le modèles�engage à traiter les données d�une certaine façon, et le contrôleur s�engage à fournir lesdonnées sous une forme utilisable.

this.model = ModelFactory.getInstance(this.languageCode);

this.view = ViewFactory.getInstance();

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

731

CH

APITRE 23 Utilisation

des designpattern

s

Enfin, il appelle la méthode display de la vue en lui passant les données à afficher :

L’interface ModelL’interface Model définit une seule méthode, de type String, prenant pour paramètre lenom de l’utilisateur :

La classe GenericModelLa classe abstraite GenericModel est celle qui contient l’implémentation commune desdifférents modèles :

this.result = model.process(name);f

this.view.display(result);

Meilleures pratiques

On trouve différentes variantes du pattern MVC. La principale controverse consiste à savoir qui faitquoi, et en particulier qui passe les données à la vue. Il serait évidemment inacceptable que lemodèle appelle la vue, ce qui le rendrait dépendant de celle-ci. En revanche, il serait tout à faitpossible que la vue interroge le modèle pour obtenir les données à afficher. Il suffirait d�ajouter aumodèle une méthode getData() retournant une chaîne de caractères. Cette façon de procéderprésente l�inconvénient de créer une dépendance entre la vue et le modèle. Que le modèledépende de la vue est tout à fait inacceptable ; en revanche, que la vue dépende du modèle estpossible. Comme il est impossible de supprimer la dépendance entre le contrôleur et la vue (dansle cas contraire, la vue devrait être créée et appelée par le modèle), nous nous trouvons alors dansun cas de dépendance circulaire, ce que nous cherchons à éviter. Nous privilégions doncl�utilisation de la vue uniquement par le contrôleur, mais sachez que vous trouverez desimplémentations différentes.

package com.volgadev.devjava.chap22.hellomvc;

interface Model {

String process(String name);}

package com.volgadev.devjava.chap22.hellomvc;

abstract class GenericModel implements Model {

protected String name; protected String start; protected String end;

public String process(String name) { if (name != null && !name.equals("")) { this.name = name; }

Copyright © 2003 G

roupe Eyrolles

732

Le

Gu

ide

du D

ével

oppe

ur

Java

2

Elle définit trois variables protected, qui ne seront donc accessibles que pour les classesdérivées, et la méthode process, qui traite les données en concaténant les variables start,name et end.

Les classes ModelFr, ModelEn et ModelEsCes trois classes correspondent aux implémentations du modèle pour le français, l’anglaiset l’espagnol. Elles ne comportent en fait que des données :

La classe ModelFactoryLa classe ModelFactory est plus complexe que les composants que nous avons décritsjusqu’ici, car elle a pour rôle de déterminer l’implémentation correcte du modèle. Deplus, nous souhaitons qu’il n’y ait aucune dépendance entre cette classe et les différentesimplémentations de modèles. Nous utiliserons donc la réflexion pour les instancier :

return this.start + this.name + this.end; }}

package com.volgadev.devjava.chap22.hellomvc;

class ModelFr extends GenericModel {

ModelFr() { this.name = "l'ami"; this.start = "Salut, "; this.end = " !"; }}

package com.volgadev.devjava.chap22.hellomvc;

class ModelEn extends GenericModel {

ModelEn() { this.name = "my friend"; this.start = "Hello, "; this.end = "!"; }}

package com.volgadev.devjava.chap22.hellomvc;

class ModelEs extends GenericModel {

ModelEs() { this.name = "amigo"; this.start = "°Buenos dias, "; this.end = "!"; }}

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

733

CH

APITRE 23 Utilisation

des designpattern

s

Cette classe ne contient qu’une seule méthode static nommée getInstance, prenantcomme paramètre un code de langage (languageCode) et retournant une instance del’interface Model. Afin de choisir la classe à instancier, la méthode commence par lire lefichier Model.prop au moyen des instructions suivantes :

package com.volgadev.devjava.chap22.hellomvc;

class ModelFactory {

static Model getInstance(String languageCode) { java.util.Properties props = new java.util.Properties(); try { java.io.FileInputStream fis = new java.io.FileInputStream("Model.prop"); props.load(fis); } catch (java.io.FileNotFoundException e) { System.out.println("File not found"); } catch (java.io.IOException e) { System.out.println("IO error"); } String defaultModelClassName = props.getProperty("defaultModelClassName"); String modelClassName = null; if (languageCode != null && !languageCode.equals("")) { modelClassName = props.getProperty(languageCode); } if (modelClassName == null || modelClassName.equals("")) { modelClassName = defaultModelClassName; } try { return (Model) Class.forName(modelClassName).newInstance(); } catch (InstantiationException e) { System.out.println("InstantiationException"); return new ModelFr(); } catch (ClassNotFoundException e) { System.out.println("ClassNotFoundException"); return new ModelFr(); } catch (IllegalAccessException e) { System.out.println("IllegalAccessException"); return new ModelFr(); } }}

java.util.Properties props = new java.util.Properties(); try { java.io.FileInputStream fis = new java.io.FileInputStream("Model.prop"); props.load(fis); } catch (java.io.FileNotFoundException e) {

Copyright © 2003 G

roupe Eyrolles

734

Le

Gu

ide

du D

ével

oppe

ur

Java

2

Le fichier chargé est un fichier de propriétés, c’est-à-dire qu’il contient une liste de pairesclé/valeur sous la forme clé=valeur :

Une fois le fichier chargé, la propriété defaultModelClassName, correspondant au modèlepar défaut, est lue et est affectée à la variable correspondante :

Puis la méthode déclare la variable modelClassName et tente de lui affecter la valeur corres-pondant au paramètre languageCode :

Si le paramètre est nul ou égal à une chaîne vide, la variable modelClassName prend lavaleur de la variable defaultModelClassName :

Enfin, la méthode tente d’instancier le modèle :

System.out.println("File not found"); } catch (java.io.IOException e) { System.out.println("IO error"); }

en=com.volgadev.devjava.chap22.hellomvc.ModelEnes=com.volgadev.devjava.chap22.hellomvc.ModelEsfr=com.volgadev.devjava.chap22.hellomvc.ModelFrdefaultModelClassName=com.volgadev.devjava.chap22.hellomvc.ModelEn

String defaultModelClassName = props.getProperty("defaultModelClassName");

String modelClassName = null; if (languageCode != null && !languageCode.equals("")) { modelClassName = props.getProperty(languageCode); }

if (modelClassName == null || modelClassName.equals("")) { modelClassName = defaultModelClassName; }

Model model = null; try { return (Model) Class.forName(modelClassName).newInstance(); } catch (Exception e) { model = new ModelFr(); } return model;

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

735

CH

APITRE 23 Utilisation

des designpattern

s

Notez qu’ici, nous attrapons la classe la plus générale Exception. Cette pratique, généra-lement déconseillée, est ici sans danger puisque dans tous les cas, nous effectuons lemême traitement, consistant à instancier la classe ModelFr.

L’interface ViewL’interface View est extrêmement simple et ne définit qu’une seule méthode affichant lesdonnées reçues en paramètres :

La classe ViewDosLa classe ViewDos est la vue la plus simple. Elle affiche simplement le résultat sur laconsole :

Meilleures pratiques

Nous avons dit que nous voulions éviter les dépendances entre la classe ModelFactory et lesdifférents modèles. Il semble que ce ne soit pas le cas ici, puisque nous référençons explicitementla classe ModelFr. En fait, il faut éviter tout sectarisme et ne pas perdre de vue la raison de ce choix.Si nous tenons à éviter les dépendances, c�est afin qu�en utilisation normale, il soit possibled�ajouter un modèle « à chaud », sans recompiler ni modifier de quelque façon que ce soitl�application existante. Pour ajouter, par exemple, un modèle en allemand, il suffit de compilerséparément la classe suivante :

package com.volgadev.devjava.chap22.hellomvc;

class ModelGr extends GenericModel {

ModelGr() { this.name = "mein Freund"; this.start = "Hallo, "; this.end = "!"; }}

et d�ajouter au fichier Model.prop la ligne :

gr=com.volgadev.devjava.chap22.hellomvc.ModelGr

Nous voyons donc que la dépendance créée avec la classe ModelFr est d�un autre ordre et nouspermet simplement d�avoir une chance de plus d�obtenir un modèle par défaut si tout le resteéchoue.

package com.volgadev.devjava.chap22.hellomvc;

interface View {

void display(String message);}

package com.volgadev.devjava.chap22.hellomvc;

class ViewDos implements View {

Copyright © 2003 G

roupe Eyrolles

736

Le

Gu

ide

du D

ével

oppe

ur

Java

2

La classe ViewWindowLa classe ViewWindow est un peu plus complexe. Elle affiche le résultat dans une fenêtre.Elle ne présente toutefois rien de nouveau par rapport à ce que nous avons appris dans lechapitre 17 :

La classe ViewFactoryLa classe ViewFactory ressemble beaucoup à la classe ModelFactory. La seule différence estque la méthode getInstance ne reçoit pas de paramètre mais lit dans un fichier le nom dela classe à instancier :

public void display(String s) { System.out.println(s); }}

package com.volgadev.devjava.chap22.hellomvc;

class ViewWindow implements View {

private javax.swing.JFrame window; private javax.swing.JLabel label;

public ViewWindow() { this.window = new javax.swing.JFrame("Vue fenêtrée"); this.window.addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent e) { System.exit(0); } }); this.label = new javax.swing.JLabel(); this.window.getContentPane().add(label); this.window.setBounds(100, 100, 300, 200); }

public void display(String message) { this.window.setVisible(true); this.label.setText(message); }}

package com.volgadev.devjava.chap22.hellomvc;

class ViewFactory {

static View getInstance() { java.util.Properties props = new java.util.Properties(); try { java.io.FileInputStream fis = new java.io.FileInputStream("View.prop"); props.load(fis); }

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

737

CH

APITRE 23 Utilisation

des designpattern

s

Le fichier View.prop contient la ligne suivante :

ce qui entraîne l’instanciation de la vue ViewWindow.

Utilisation de l’application HelloMVCAvant d’être utilisée, l’application peut être configurée en modifiant le nom de la vuedans le fichier View.prop. Elle peut ensuite être lancée avec une des commandessuivantes :

La figure 23.6 montre l’affichage obtenu sans paramètre. La figure 23.7 montre l’affi-chage avec les paramètres Paul et es.

catch (java.io.FileNotFoundException e) { System.out.println("File not found"); } catch (java.io.IOException e) { System.out.println("IO error"); } String viewClassName = props.getProperty("viewClassName"); View view = null; try { return (View) Class.forName(viewClassName).newInstance(); } catch (Exception e) { view = new ViewDos(); } return view; }}

viewClassName=com.volgadev.devjava.chap22.hellomvc.ViewWindow

java com.volgadev.devjava.chap22.hellomvc.HelloMvcjava com.volgadev.devjava.chap22.hellomvc.HelloMvc nomjava com.volgadev.devjava.chap22.hellomvc.HelloMvc nom code_langage

Figure 23.6Affichage obtenu sans paramètre.

Copyright © 2003 G

roupe Eyrolles

738

Le

Gu

ide

du D

ével

oppe

ur

Java

2

Ajout de composants à l’application HelloMVCGrâce à l’architecture que nous avons adoptée, il est très facile d’ajouter des composantsà l’application pour en modifier le comportement. Comme il n’est pas nécessaire pourcela de modifier ni même de recompiler l’application, il n’y a aucun risque d’introduiredes bogues ! Nous avons vu précédemment comment ajouter un modèle. L’ajout d’unevue n’est pas plus compliqué. Il suffit de compiler la nouvelle classe et d’indiquer sonnom dans le fichier View.prop. À titre d’exemple, nous avons placé sur le CD-Rom laversion compilée d’une vue nommée ViewMail. Cette « vue » envoie le résultat fourni parle modèle sous forme de mail. Elle utilise le fichier de paramétrage viewmail.propsuivant :

Vous devez remplacer les éléments ci-dessus en italique par les données correspondant àvotre serveur de mail et placer ce fichier dans un emplacement accessible au programme,avec les autres fichiers .prop. Placez ensuite le fichier viewmail.jar ainsi que les fichiersmail.jar et activation.jar dans votre classpath et lancez l’application. Si tout se passebien, vous devriez obtenir sur la console l’affichage suivant :

Figure 23.7Affichage obtenu avec les paramètres Paul et es.

mailServer=adresse_serveur_de_mailaddress=adresse_destinatairefromAddress=adresse_expéditeurfromName=Nom_expéditeurmailSubject=Test du pattern MVC

DEBUG: setDebug: JavaMail version 1.3DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Sun Microsystems, Inc]DEBUG SMTP: useEhlo true, useAuth false

DEBUG: SMTPTransport trying to connect to host "adresse_serveur_de_mail", port 25

DEBUG SMTP RCVD: 220 adresse_serveur_de_mail ESMTP Sendmail 8.9.3/8.9.3; Wed, 26 Feb 2003 10:12:09 -0800

DEBUG: SMTPTransport connected to host "adresse_serveur_de_mail", port: 25

DEBUG SMTP SENT: EHLO Nom de votre machineDEBUG SMTP RCVD: 250-adresse_serveur_de_mail Hello votre adresse, pleased to meet you250-EXPN250-VERB

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

739

CH

APITRE 23 Utilisation

des designpattern

s

Au bout de quelques secondes, vous devriez recevoir un mail. Si pour une raison ou une autre, vous souhaitez modifier la classe ViewMail, le codecorrespondant est donné sur le listing 23.1. Vous le trouverez également sur le CD-Rom.

250-8BITMIME250-SIZE 5000000250-DSN250-ONEX250-ETRN250-XUSR250 HELP

DEBUG SMTP Found extension "EXPN", arg ""DEBUG SMTP Found extension "VERB", arg ""DEBUG SMTP Found extension "8BITMIME", arg ""DEBUG SMTP Found extension "SIZE", arg "5000000"DEBUG SMTP Found extension "DSN", arg ""DEBUG SMTP Found extension "ONEX", arg ""DEBUG SMTP Found extension "ETRN", arg ""DEBUG SMTP Found extension "XUSR", arg ""DEBUG SMTP Found extension "HELP", arg ""DEBUG SMTP: use8bit falseDEBUG SMTP SENT: MAIL FROM:<adresse_expéditeur>DEBUG SMTP RCVD: 250 <adresse_expéditeur>... Sender ok

DEBUG SMTP SENT: RCPT TO:<adresse_destinataire>DEBUG SMTP RCVD: 250 <adresse_destinataire>... Recipient ok

Verified Addresses adresse_destinataireDEBUG SMTP SENT: DATADEBUG SMTP RCVD: 354 Please start mail input.

DEBUG SMTP SENT: .DEBUG SMTP RCVD: 250 Mail queued for delivery.

DEBUG SMTP SENT: QUIT

Listing 23.1 – La classe ViewMail.

package com.volgadev.devjava.chap22.hellomvc;

import javax.mail.Session;import javax.mail.Transport;import javax.mail.MessagingException;import javax.mail.internet.MimeMessage;import javax.mail.internet.InternetAddress;import java.io.UnsupportedEncodingException;import java.io.IOException;import java.io.FileNotFoundException;import java.io.FileInputStream;import java.util.Properties;

Copyright © 2003 G

roupe Eyrolles

740

Le

Gu

ide

du D

ével

oppe

ur

Java

2

class ViewMail implements View {

private Properties props; private String mailServer; private String address; private String fromAddress; private String fromName; private String mailSubject;

ViewMail() { props = new Properties(); try { FileInputStream fileinputstream = new FileInputStream("ViewMail.prop"); props.load(fileinputstream); } catch (FileNotFoundException filenotfoundexception) { System.out.println("File not found"); } catch (IOException ioexception) { System.out.println("IO error"); } mailServer = props.getProperty("mailServer"); address = props.getProperty("address"); fromAddress = props.getProperty("fromAddress"); fromName = props.getProperty("fromName"); mailSubject = props.getProperty("mailSubject"); }

public void display(String s) { props.put("mail.smtp.host", mailServer); props.put("mail.smtp.host", mailServer); Session session = Session.getDefaultInstance(props, null); session.setDebug(true); MimeMessage mimemessage = new MimeMessage(session); try { InternetAddress internetaddress = new InternetAddress(fromAddress, fromName); mimemessage.setFrom(internetaddress); mimemessage.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(address)); mimemessage.setSubject(mailSubject); mimemessage.setText(s); Transport.send(mimemessage); } catch (UnsupportedEncodingException unsupportedencodingexception) { } catch (MessagingException messagingexception) { } }}

Listing 23.1 – La classe ViewMail. (suite)

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

741

CH

APITRE 23 Utilisation

des designpattern

s

Le pattern ProxyLe pattern Proxy est également un pattern très répandu. Il consiste à placer entre un objetA et un objet B dont dépend A un troisième objet, qui reproduit l’interface de B, maispermet d’en contrôler l’usage. Une des utilisations les plus courantes des proxies consisteà fournir le même résultat que l’objet proxifié1, mais à un coût réduit. Par exemple,lorsque vous connectez votre navigateur à un site Web situé aux Etats-Unis, un proxypeut être intercalé entre ce site et vous. Si la page demandée l’est pour la première fois, leproxy la demande au serveur, puis la met en mémoire et en transmet une copie à votrenavigateur. Lors de l’accès suivant, le proxy peut réutiliser la même page, faisant ainsil’économie d’une transmission depuis le serveur. Dans ce cas, l’intérêt du proxy est ungain de temps pour l’utilisateur, et une économie de bande passante.Bien entendu, le fonctionnement d’un proxy est plus complexe que cela et implique devérifier l’âge des données stockées et de définir une procédure pour déterminer lemoment où les données doivent être rafraîchies.Dans notre exemple, la raison justifiant l’utilisation d’un proxy est différente. Nous utili-serons une connexion au serveur américain du National Institute of Standards and Tech-nology, à l’adresse time.nist.gov, qui donne en permanence l’heure sur le port 13. Toute-fois, ce serveur présente une limitation : il n’est pas possible de demander l’heure de façonextrêmement rapprochée. Nous pouvons alors créer un proxy pour ce service. Ce compo-sant demandera l’heure au serveur une fois par minute et comptera le temps entre chaqueaccès. L’information fournie par le proxy sera de moins en moins précise, la plus grandeimprécision étant atteinte au bout de 59 secondes.

1. Nous utilisons sans remord ce néologisme correspondant à l’anglais proxified, en respectant la transformation du y en i.

Figure 23.8Structure des deux applications, avec et sans proxy.

TestTimeTellerWindow

-label : JLabel = new JLabel(timeTeller.getTime())

-timeTeller : TimeTeller = TimeTellerAgent.getInstance()

+main(args:String[])

TestTimeTellerWindowProxy

+main(args:String[])

~TestTimeTellerWindowProxy() : TestTimeTellerWindowProxy

<<interface>>

TimeTeller

~getTime() : String

TimeTellerAgent

-timeTeller : TimeTeller = new TimeTellerAgent()

~getInstance() : TimeTeller

-TimeTellerAgent() : TimeTellerAgent

+getTime() : String

TimeTellerProxy

-ONE_SECOND : int = 1000

-seconds : int

-counter : int

-time : String

-timeTeller : TimeTeller = new TimeTellerProxy()

~getInstance() : TimeTeller

-TimeTellerProxy() : TimeTellerProxy

-setSeconds()

+getTime() : String

TimeTeller<-TimeTellerAgentTimeTeller<-TimeTellerProxy

Copyright © 2003 G

roupe Eyrolles

742

Le

Gu

ide

du D

ével

oppe

ur

Java

2 La figure 23.8 montre le diagramme de classes UML de l’application. Sur ce diagramme,deux versions de l’application sont représentées simultanément. En effet, les classes Test-TimeTellerWindow et TestTimeTellerWindowProxy représentent respectivement la classeprincipale de l’application appelant directement le serveur et celle utilisant le proxy. Dansles deux cas, la référence à l’objet fournissant le service est du type de l’interface Time-Teller. Dans le cas d’une utilisation sans proxy, la référence est initialisée de la manièresuivante :

alors que dans l’application avec proxy, l’initialisation suivante est effectuée :

Dans le premier cas, TimeTellerAgent.getInstance() renvoie un objet de type TimeTelle-rAgent, implémentant l’interface TimeTeller et se connectant directement au serveur àchaque appel de la méthode getTime(). Dans le second cas, la méthode TimeTeller-Proxy.getInstance() renvoie un objet de type TimeTellerProxy, implémentant égalementl’interface TimeTeller, mais dont l’implémentation de la méthode getTime() renvoie lavaleur du temps sans consulter le serveur.

Le listing 23.2 montre le code de la classe TimeTellerAgent, chargé de se connecter auserveur. Le listing 23.3 montre le code de la classe TimeTellerProxy. Notez que ces deuxclasses implémentent l’interface TimeTeller, qui déclare uniquement la méthode getTime :

private static final TimeTeller timeTeller = TimeTellerAgent.getInstance();

final TimeTeller timeTeller = TimeTellerProxy.getInstance();

Meilleures pratiques : double proxy

Chaque fois que cela est possible, il est souhaitable que le proxy implémente la même interfaceque l�objet « proxifié ». Si cet objet est une classe Java, cela ne pose généralement pas deproblème. En revanche, lorsque le proxy est appliqué, comme ici, à un service, cela n�est paspossible. Il est alors conseillé d�utiliser un double proxy. Le premier n�a d�autre objectif que derendre le service disponible tel quel sous la forme d�une classe Java. Le fait de définir cette classevous obligera à définir également de façon rigoureuse l�interface correspondant au service. Lesecond proxy sera appliqué au premier pour modifier la façon dont le service est implémenté. Cettefaçon de faire permet en outre d�effectuer la mise au point et le débogage en deux étapes, d�aborden utilisant seulement le premier proxy, puis en ajoutant le second.

Listing 23.2 – La classe TimeTellerAgent.

package com.volgadev.devjava.chap22.timeteller;

public interface TimeTeller {

String getTime();}package com.volgadev.devjava.chap22.timeteller;

import java.io.BufferedReader;

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

743

CH

APITRE 23 Utilisation

des designpattern

s

import java.io.InputStream;import java.io.InputStreamReader;import java.io.IOException;import java.net.Socket;import java.net.MalformedURLException;

class TimeTellerAgent implements TimeTeller {

private static TimeTeller timeTeller = new TimeTellerAgent();

static TimeTeller getInstance() { return timeTeller; }

private TimeTellerAgent() { }

public String getTime() { BufferedReader reader = null; String time = "00-00-00 00:00:00"; try { System.out.println("TimeTeller appelle le serveur"); Socket socket = new Socket("time.nist.gov", 13); InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); reader = new BufferedReader(isr); reader.readLine(); time = reader.readLine(); time = time.substring(6, 23); System.out.println("L'heure du serveur est: " + time + " (dans TimeTellerAgent)"); } catch (MalformedURLException e) { System.err.println("Format incorrect: " + e); } catch (IOException e) { System.err.println("I/O Exception: " + e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { } } } return time; }}

Listing 23.2 – La classe TimeTellerAgent. (suite)

Copyright © 2003 G

roupe Eyrolles

744

Le

Gu

ide

du D

ével

oppe

ur

Java

2 Listing 23.3 – La classe TimeTellerProxy.

package com.volgadev.devjava.chap22.timeteller;

import javax.swing.*;import java.awt.event.ActionListener;import java.awt.event.ActionEvent;

public class TimeTellerProxy implements TimeTeller {

private final static int ONE_SECOND = 1000; private Timer timer; private int seconds; private int counter; private String time; private static TimeTeller timeTeller = new TimeTellerProxy(); private TimeTeller timeTellerDirect;

static TimeTeller getInstance() { return timeTeller; }

private TimeTellerProxy() { timeTellerDirect = TimeTellerAgent.getInstance(); // Création d'un timer. timer = new Timer(ONE_SECOND, new ActionListener() { public void actionPerformed(ActionEvent evt) { setSeconds(); } });

setSeconds(); timer.start(); }

private void setSeconds() { if (counter == 0) { System.out.println("Synchronisation..."); String tempTime = timeTellerDirect.getTime(); System.out.println("L'heure du serveur est: " + tempTime + " (dans TimeTellerProxy)"); time = tempTime.substring(0, tempTime.length() - 2); seconds = Integer.parseInt(tempTime.substring(tempTime.length() - 2)); counter = (90 - seconds) % 60; if (counter == 0) { counter = 60; } } else { counter--; seconds = ++seconds % 60; } System.out.println(counter); }

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

745

CH

APITRE 23 Utilisation

des designpattern

s

La classe principale de l’application est la classe TestTimeTellerWindow, dont le code estdonné sur le listing 23.4. Notez que la version donnée ici n’utilise que le premier proxy,accédant au serveur à chaque utilisation.

Pour tester cette application, compilez l’ensemble des classes et exécutez la classe TestTi-meTellerWindow. Vous devez obtenir l’affichage de la figure 23.9.Parallèlement, l’application affiche un certain nombre de messages sur la console,permettant de suivre le déroulement des opérations :

public String getTime() { String temp = "0" + seconds; int end = temp.length(); int start = end - 2; return (time + temp.substring(start, end)); }}

Listing 23.4 – La classe principale de l’application.

package com.volgadev.devjava.chap22.timeteller;

import javax.swing.*;import java.awt.event.ActionListener;import java.awt.event.ActionEvent;import java.awt.*;

public class TestTimeTellerWindow {

private static final TimeTeller timeTeller = TimeTellerAgent.getInstance(); private static final JLabel label = new JLabel(timeTeller.getTime());

public static void main(String args[]) { System.out.println(timeTeller.getTime());

JFrame frame = new JFrame("Time Teller"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Actualiser"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { label.setText(timeTeller.getTime()); } }); Container content = frame.getContentPane(); content.add(label, BorderLayout.CENTER); content.add(button, BorderLayout.SOUTH); frame.pack(); frame.show(); }}

Listing 23.3 – La classe TimeTellerProxy. (suite)

Copyright © 2003 G

roupe Eyrolles

746

Le

Gu

ide

du D

ével

oppe

ur

Java

2

Pour actualiser l’affichage, cliquez sur le bout Actualiser. Le programme accède denouveau au serveur, puis affiche la nouvelle heure dans la fenêtre, et les messages suivantssur la console :

Rien ne semble jusqu’ici nécessiter une autre approche. Pourtant, si vous cliquez trèsrapidement sur le bouton Actualiser, vous risquez d’obtenir le résultat suivant :

Figure 23.9L’affichage de l’heure lue sur le serveur.

TimeTeller appelle le serveurL'heure du serveur est: 03-02-27 12:26:47 (dans TimeTellerAgent)03-02-27 12:26:47

TimeTeller appelle le serveurL'heure du serveur est: 03-02-27 12:30:46 (dans TimeTellerAgent)

TimeTeller appelle le serveurjava.lang.NullPointerException at com.volgadev.devjava.chap22.timeteller.TimeTellerAgent.getTime(TimeTellerAgent.java:39) at com.volgadev.devjava.chap22.timeteller.TestTimeTellerWindow$1.actionPerformed(TestTimeTellerWindow.java:28) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1764) at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1817) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:419) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:257) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:245) at java.awt.Component.processMouseEvent(Component.java:5093) at java.awt.Component.processEvent(Component.java:4890) at java.awt.Container.processEvent(Container.java:1566) at java.awt.Component.dispatchEventImpl(Component.java:3598) at java.awt.Container.dispatchEventImpl(Container.java:1623) at java.awt.Component.dispatchEvent(Component.java:3439) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3450) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3165) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3095) at java.awt.Container.dispatchEventImpl(Container.java:1609) at java.awt.Window.dispatchEventImpl(Window.java:1585) at java.awt.Component.dispatchEvent(Component.java:3439) at java.awt.EventQueue.dispatchEvent(EventQueue.java:450) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:197) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)

Copy

right

© 2

003

Gro

upe

Eyr

olle

s

747

CH

APITRE 23 Utilisation

des designpattern

s

L’exception NullPointerException est provoquée par le fait que le serveur refuse derépondre à des sollicitations trop rapprochées. Pour éviter ce problème, il suffit de mettreen œuvre le second proxy en effectuant la modification suivante :

Relancez l’application. L’affichage obtenu dans la fenêtre est identique. En revanche, lesmessages affichés sur la console sont différents :

Avec le second proxy, le serveur n’est contacté qu’une fois par minute (nous avons choisipour simplifier d’appeler le serveur trente secondes après la minute pleine pour éviter lesproblèmes dus au changement du chiffre des minutes). Entre deux connexions, le proxycompte les secondes à l’aide d’un timer. Ici, le programme a démarré vingt-cinq secondesaprès une minute pleine, et une synchronisation a donc eu lieu cinq secondes plus tard.Vous pouvez maintenant cliquer aussi rapidement que vous le souhaitez sur le boutonActualiser sans provoquer d’erreur.

at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:144) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:136) at java.awt.EventDispatchThread.run(EventDispatchThread.java:99)

private static final TimeTeller timeTeller = TimeTellerProxy.getInstance();

Synchronisation...TimeTeller appelle le serveurL'heure du serveur est: 03-02-27 12:38:25 (dans TimeTellerAgent)L'heure du serveur est: 03-02-27 12:38:25 (dans TimeTellerProxy)503-02-27 12:38:2543210Synchronisation...TimeTeller appelle le serveurL'heure du serveur est: 03-02-27 12:38:31 (dans TimeTellerAgent)L'heure du serveur est: 03-02-27 12:38:31 (dans TimeTellerProxy)595857565554

NOTE Ce programme ne sert que d�exemple de l�utilisation d�un double proxy. En ce quiconcerne les informations qu�il fournit, sa fiabilité est assez moyenne. Il souffre deplusieurs défauts. Le principal est que la synchronisation est une opération synchrone parrapport au décompte interne du temps, ce qui introduit une forte imprécision au momentdu passage de la demi-minute. En raison du cumul de l�imprécision, il se produit un retarddans la synchronisation. Celle-ci a d�abord lieu à 30 secondes, puis 31, 32, etc.

Copyright © 2003 G

roupe Eyrolles

748

Le

Gu

ide

du D

ével

oppe

ur

Java

2 RésuméCeci termine ce chapitre dans lequel nous avons présenté les design patterns MVC etProxy. Il existe bien d’autres design patterns qu’il est utile de connaître, et nous vousrenvoyons aux nombreux livres publiés sur ce sujet. Il ne faut toutefois pas croire qu’ils’agit d’un domaine figé. De nouveaux patterns sont régulièrement proposés. Certainsdisparaissent rapidement, d’autres sont adoptés par la communauté des développeurs.Vous pouvez d’ailleurs proposer les vôtres, si vous en découvrez.