Clients riches en Windows Forms ou Java Swing
![]()
Le développement de clients
légers est à la mode et offre d’énormes facilités de déploiement et d’accès à
une infinité de services par le biais d’un poste client
banalisé : le navigateur Web. Toutefois,
ce modèle ne se prête pas à toutes les applications. Certaines nécessitent en
effet une très grande interactivité ou des techniques de rendu particulières
(graphismes raffinés, 3D…).
Dans ce chapitre, nous présentons les outils disponibles dans les plate-formes .NET (WindowsForms) et J2EE (Java Swing) permettant de réaliser des « clients riches » (ou, selon le terme péjoratif consacré, des « clients lourds »). Nous présenterons de manière incrémentale les composants à disposition, les outils permettant de bâtir aisément des applications graphiques, puis nous vous proposerons une étude de cas comparant les bonnes pratiques de conception d’un client riche sur ces deux plate-formes.
Dès sa naissance, Java a disposé d’une bibliothèque de composants graphiques : AWT (Abstract Windowing Toolkit). Ces composants étaient basés sur des contrôles natifs du système d’exploitation local (Windows, Mac, Unix) rendant les applications Java AWT assez performantes. Malheureusement, cette approche posait quelques difficultés en terme de portabilité et d’extensibilité.
Depuis Java 2 (JDK 1.2.2), Sun incite les développeurs Java à utiliser Swing en remplacement de AWT. Cette nouvelle bibliothèque est beaucoup plus riche que la précédente, et offre une bien meilleure portabilité ; et pour cause : tous les composants graphiques (du simple bouton au tableur) sont réécrits en Java et ne s’appuient que sur des primitives basiques (tracé de lignes, couleurs…) pour réaliser leur affichage à l’écran.
Les composants Swing sont de bonne facture : ils respectent de nombreuses bonnes pratiques de conception Orientée Objet (Design Pattern), leur « look and feel » est complètement adaptable, et la palette de composants disponibles est assez riche pour répondre aux principaux besoins. Jugez plutôt (ces images sont tirées du Java Tutorial de Sun Microsystem) :
|
|
|
|
|
|
|
|
|
|
|
|
Java Swing fait en réalité partie d’un framework plus ambitieux, Java Foundation Classes (JFC), qui inclut le glisser-déposer, copier-coller, l’affichage et les animation 2D (Java 2D), le traitement d’images (Java Advanced Imaging), ainsi que les impressions en qualité PostScript… Nous n’entrerons pas dans ce niveau de détail dans cet article.
Bien entendu, côté graphisme et système de fenêtrage, Windows n’est pas en reste (il mérite bien son nom) : les développeurs sur cette plate-forme ont de bonnes raisons d’être fiers de leurs outils de développement car ils offrent un grand nombre de composants graphiques aisément réutilisables. En C++ par exemple, on peut s’appuyer sur les MFC (Microsoft Foundation Classes) pour bâtir une application graphique ; en Visual Basic, on utilisera plutôt la métaphore des formulaires (Forms).
Bonne nouvelle, .NET uniformise les développements graphiques : tous les langages vont s’appuyer sur la même bibliothèque, WindowsForms, et la même philosophie pour construire leurs IHM. L’effort d’apprentissage de ces composants en VB.NET n’est donc pas perdu si l’on passe à C# ou tout autre langage permettant de générer du MSIL. Voici un aperçu des composants WindowsForms disponibles :
|
|
|
|
|
|
Tout comme la plate-forme Java, .NET complète ces composants graphiques par des mécanismes de glisser-déposer, copier-coller. L’interface de programmation GDI+ permet d’avoir la main sur le tracé de dessins en 2D et de gérer les impressions sur le modèle de Java 2D (à ce sujet, les WindowsForms offrent un mécanisme de pré-visualisation des impressions pratiquement automatique !).
Si l’on souhaite concevoir, afficher et imprimer des rapports (états, statistiques, etc), WindowsForms propose même un composant qui n’est autre que le visualiseur « CrystalReports ». On peut donc, dans une application .NET, visualiser un rapport au format RPT, ou même concevoir la trame d’un rapport, en charger dynamiquement les données depuis un DataSet (donc, depuis XML ou une base de données !), et visualiser puis imprimer les informations dynamiquement. On ne trouve pas d’équivalent de manière native dans la plate-forme J2EE ! Un bémol toutefois : la plate-forme .NET ne permet d’utiliser gratuitement le runtime de CrystalReports que dans une limite de 5 utilisateurs concurrents, après quoi il faut acheter une licence auprès de l’éditeur CrystalDecisions.
La plupart des environnements de programmation sont dotés d’outils d’aide à la conception d’interfaces graphiques. Ces outils sont eux-mêmes graphiques et permettent de concevoir par glisser-déposer nos interfaces, tout en nous donnant une idée du résultat final (tel qu’il se présentera à l’exécution).
Ces outils sont idéaux pour prototyper ou réaliser les IHM statiques. Par contre, leur usage est moins aisé pour la création d’IHM dont les composants ou l’apparence générale évoluent à l’exécution (par exemple, une interface dont certains champs apparaissent ou disparaissent en fonction de la valeur saisie dans une zone éditable).
Voyons donc rapidement ce que nous proposent les éditeurs Java et .NET.
Le monde Java est riche en éditeurs, plus ou moins complets, open source ou commerciaux. La plupart proposent aujourd’hui un environnement graphique compatible Java Swing (et certains proposent toujours AWT pour des raisons de compatibilité ascendante) : c’est le cas de Jbuilder, VisualAge, WebGain (Visual Cafe), Forte4Java, Eclipse, NetBeans, et j’en passe…
Dans cet article (surtout dans l’étude de cas), nous utiliserons NetBeans pour concevoir les interfaces graphiques. Cet éditeur a la bonne idée de protéger le code qui est généré automatiquement en mode graphique et de nous inviter à frapper notre code Java dans des zones réservées. De cette manière, NetBeans est certain de toujours pouvoir garantir la cohérence des environnements textuel (développement) et graphique (conception).

Microsoft est fidèle à sa réputation en termes d’outillage : VisualStudio.NET est un environnement tout à fait opérationnel pour concevoir des applications à base de composants WindowsForms. Les principes de cet outil sont très semblables aux autres builders graphiques, jugez plutôt :

Voilà. Nous avons fait le tour, brièvement, des briques de base permettant le développement de clients riches en .NET et Java. Reste à voir, sur un cas concret, comment utiliser ces technologies à bon escient.
Dans cette section, nous allons concevoir et partiellement implémenter une application graphique de complexité moyenne dans les deux frameworks : Windows Forms et Java Swing. Initialement, notre solution sera quelque peu simpliste. Nous découvrirons ses faiblesses de conception au fur et à mesure, ce qui nous amènera à appliquer certains Design Patterns (modèle de conception).
Notre application est un outil graphique permettant de visualiser un portefeuille boursier, de faire des statistiques (pourcentage de sicav, fcp, actions, etc…) et de les présenter sous différentes formes : camemberts, graphiques en barres, tableaux, courbes… Nous ne nous intéresserons pas à la récupération des informations brutes (qui peut être facilement implémentée grâce aux WebServices par exemple). Les utilisateurs de notre appli ont émis les souhaits suivants :
· pouvoir visualiser la même information sous plusieurs angles en même temps (plusieurs rendus des mêmes données), sans qu’il n’y ait jamais d’incohérence entre ces différents angles
· pouvoir lancer une fonction du logiciel par plusieurs moyens (menu déroulant, raccourci clavier, boutons cliquables…) selon l’expertise et le degré de connaissance du logiciel par les utilisateurs
· ne pas craindre d’essayer certaines fonctionnalités avancées ; pour cela, il faut que la plupart des actions de notre logiciel puissent être « défaites » (undo) sans encombre
· enfin, dans la mesure du possible, les utilisateurs aimeraient pouvoir agencer à leur guise les différents rendus visuels à l’intérieur de la fenêtre du logiciel (personnaliser leur application)
La première étape consiste à bâtir des composants graphiques permettant d’afficher camemberts, graphiques en barres, etc… Ces composants sont un besoin tellement récurrent que certaines entreprises ou communautés de développement se sont déjà penchées sur la question (KLGroup, Xanth, Ilog …). Toutefois, pour des besoins spécifiques ou pour ne pas dépendre de bibliothèques de composants externes, nous pouvons décider de ré-implémenter ces quelques classes.
Prenons l’exemple du camembert. Ses instances doivent pouvoir être déposées sur un panneau d’affichage ou dans un environnement à ascenseurs. La classe Camembert doit donc hériter d’un composant générique du framework utilisé, qui permette de re-définir la manière dont le composant se dessine à l’écran.
En Java, nous avons choisi de créer une classe qui hérite de JPanel qui est zone pouvant accueillir des contrôles fils (nous n’épiloguerons pas sur la qualité du code ni sur tout ce qui manque à cette classe pour qu’elle soit réutilisable, paramétrable…). Un JPanel peut être inclus directement dans une fenêtre (JFrame) ou dans un panneau intermédiaire.

En C#, nous créons une classe qui hérite de UserControl, qui elle aussi peut être ajouté à pratiquement n’importe quel niveau d’une interface graphique.

Créons rapidement un petit formulaire permettant de saisir les valeurs de chaque part du camembert. Ceci est trivial si l’on considère que le formulaire ne peut contenir qu’un certain nombre de champs au maximum, et qu’un champ vide correspond à une valeur qui ne doit pas être prise en compte. On pourrait implémenter une interface graphique plus adaptative (qui ajoute automatiquement un champ lorsqu’on a rempli tous ceux qui étaient présents…), mais dans le cadre de cet article, l’objectif est plus de réfléchir aux interactions entre les différents composants que de peaufiner l’IHM.
En Java, nous ajoutons un JPanel intermédiaire à l’ouest du Camembert. Ce JPanel va accueillir nos zones de saisie, et un bouton de mise à jour du rendu graphique. C’est en réaction au click sur le bouton que nous invoquons la méthode setValeurs() sur le Camembert.

En C#, nous ajoutons un GroupBox sur la gauche de notre Form principale, ainsi qu’un Panel permettant de positionner précisément notre Camembert sur la droite. De même que précédemment, c’est le click sur le bouton « Actualiser » qui met à jour l’affichage.

La petite application que nous venons de construire fonctionne assez bien, mais elle n’est pas extensible. Si l’on devait y ajouter d’autres composants de rendu visuel (graphique en barres, etc…), il faudrait modifier le code de gestion de l’événement « click » et piloter autant de composants que de rendus graphiques. Cette conception n’est pas acceptable.
Comme nous l’avons vu précédemment, le bouton « actualiser » (par son événement « click ») ne peut pas porter seul la responsabilité de donner à chaque composant de rendu graphique l’ordre de se redessiner avec les nouvelles valeurs saisies. Pour limiter ce couplage, la pratique consiste à mettre en place un mécanisme d’abonnement et de publication : un composant graphique intéressé par un événement (ici, une modification de saisie) s’abonne auprès du composant visé ; ce dernier notifiera tous les composants enregistrés pour les informer de l’occurrence de l’événement attendu. On appelle également ce Design Pattern l’Observateur. L’avantage de cette technique est que le branchement / débranchement des composants visuel est beaucoup plus flexible : il peut se faire dynamiquement, et le même événement peut déclencher un nombre arbitraire de répercussions.
Dans la bibliothèque Java Swing, nous avons masqué les segments de code permettant à notre bouton précédent de déclencher la méthode « jButton1ActionPerformed ». Mais en réalité, la gestion de tout événement Swing repose entièrement sur l’abonnement / publication : une classe intéressée par un événement (ActionEvent sur un bouton poussoir) doit devenir un « listener » pour ce type d’événement. En pratique, cela revient à dire que notre classe doit implémenter l’interface « listener » correspondante (ActionListener pour récupérer les événements de type ActionEvent). Enfin, selon les composants auxquels on est à l’écoute, il faudra implémenter une ou plusieurs méthodes « callbacks » : par exemple, un bouton ne peut être que cliqué, d’où l’existence d’une méthode unique actionPerformed.
Heureusement, Java Swing implémente ce mécanisme pour chaque composant de la bibliothèque. Contrairement à ce que nous croyions dans la conclusion de la section précédente, nous n’aurons donc jamais de problème de couplage très fort entre un événement utilisateur et le ou les composants notifiés.
Pour référence, voici le code que NetBeans génère automatiquement pour nous lorsque nous double-cliquons dans l’environnement de conception graphique et que nous saisissons du code dans la méthode « callback ». Ce code tire parti de mécanismes Java avancés, mais récurrents dans le domaine de la gestion d’événements : la création d’une classe anonyme imbriquée qui implémente l’interface « listener », suivie de l’instanciation immédiate et unique de cette classe. Soit, en mots moins barbares :

Dans le framework WindowsForms de .NET, comment gère-t-on la récupération des événements ? Eh bien la bonne nouvelle, c’est que ce mécanisme est ici complètement intégré aux langages de programmation C# et VB.NET : il s’agit de la notion de delegate. Celle-ci est complètement équivalente à celle du listener Java, mais dispose d’une syntaxe plus concise et libère le développeur des tâches de création de classes qui implémentent des interfaces… (le compilateur C# ou VB.NET prend cela à sa charge).
En deux mots, un delegate est une sorte de pointeur de méthodes ; ce pointeur est robuste, car il ne peut pointer que sur des méthodes respectant une signature fixée à la déclaration. De plus, un delegate peut être multicast, c’est-à-dire qu’une simple invocation du delegate peut automatiquement déclencher un nombre arbitraire de méthodes, à condition que chacune respecte la signature du delegate.
Bref, ce concept se prête parfaitement à la notification d’événements graphiques, et constitue logiquement le fondement de la gestion d’événements dans .NET, en particulier dans la bibliothèque des WindowsForms. Pour référence, voici le code permettant d’enregistrer la méthode button1_click de la classe courante dans le delegate du bouton « actualiser » :
![]()
Cette bonne pratique de conception se trouve donc implémentée à la fois dans Swing et dans WindowsForms.
Pour aller encore plus loin dans le découplage, il est possible de traiter les événements graphiques dans des tâches (Threads) distincts de celui responsable du rafraîchissement de notre interface graphique. En effet, si les actions déclenchées par un événement utilisateur sont lourdes (telles qu’un téléchargement de fichier, un calcul complexe, un traitement de données massif), il est indispensable de découpler le fil d’exécution de notre IHM de ceux des actions. Sans cela, l’interface graphique va se figer complètement, en attente de la fin d’exécution des actions.
Ce mécanisme est à manier avec prudence (attention aux accès concurrents à des données communes par les différents Threads, aux étreintes fatales…), mais il est simple à implémenter, que ce soit en C# ou en Java.
En Java, il suffit de créer une classe héritant de la classe prédéfinie Thread et qui redéfinisse la méthode run(). A la réception de l’événement, il suffit d’instancier notre classe, et d’invoquer la méthode asynchrone start() sur cette instance. A nouveau, cela se comprend mieux en jetant un œil au code Java :

Les puristes diront que cette implémentation crée un objet (et un Thread) pour chaque événement graphique géré. Effectivement : il serait bien plus efficace d’implémenter un pool de Threads réutilisables, d’affecter dynamiquement un Thread à l’invocation de la méthode « callback » lors de l’occurrence d’un événement, puis de replacer ce Thread dans le pool à l’issue du traitement. Ce pool serait bien entendu un singleton. Son code n’est pas très complexe, mais dépasse l’objet central de cet article ; passionnés du multi-threading, à vos claviers.
En C# (ou en VB.NET), les choses sont encore plus simples. En effet, .NET comporte un système permettant de choisir, lors de l’invocation d’une méthode, si l’on souhaite faire un appel synchrone (en attente du retour de la méthode) ou asynchrone. Nous vous proposons une illustration simpliste de ce mécanisme :

L’avantage de ce mécanisme de .NET réside dans le fait que la simple invocation de BeginInvoke déclenche automatiquement le delegate correspondant dans un Thread séparé. De plus, .NET implémente automatiquement un pool de Threads, donc les quelques lignes de code précédentes sont tout à fait robustes quant à la monté en charge et à l’occupation mémoire (pas de création / garbage collection intempestives grâce au pool).
Le découplage des composants que nous avons abordé dans les deux sections précédentes est primordial pour le concepteur / développeur de l’application, mais n’offre pas de fonctionnalité supplémentaire aux utilisateurs. Or, dans la liste des besoins identifiés auprès de ces utilisateurs, nous avons mentionné celui d’afficher simultanément plusieurs composants graphiques se rapportant aux mêmes données sous-jacentes.
Or, WindowsForms et Java Swing offrent tous deux un mécanisme « multi-document » automatique permettant de faire d’une pierre deux coups : cela nous permet à la fois
· d’afficher plusieurs panneaux à l’intérieur de la même fenêtre principale,
· et de donner à l’utilisateur toute latitude pour le placement relatif de ces différents panneaux.
En C#, nous utilisons donc la notion d’application MDI (Multiple Document Interface). Il suffit pour cela d’ajouter à notre projet une Form contenant notre composant graphique Camembert. Pour être capable d’accueillir plusieurs fenêtres internes, la fenêtre principale de notre application doit voir son attribut IsMdiContainer positionné à true. Il suffira ensuite de dire à chaque Form fille quel est son parent (l’attribut MdiParent doit donc pointer sur la Form principale) pour obtenir le résultat suivant :

En Java Swing, le composant JInternalFrame correspond à une « Form MdiChild » du framework WindowsForms.

Bien entendu, pour que notre application fonctionne, il a fallu modifier le comportement de la méthode « callback » qui réagit à la pression sur le bouton « Actualiser ». La section suivante s’attache à l’analyse et à l’éviction de cette dépendance entre composants.
Notre formulaire de saisie possède une dépendance forte vis-à-vis des composants de rendu visuel. Pour limiter ce couplage, il est bon dans une interface graphique complexe d’avoir recours au Design Pattern Médiateur. Il s’agit d’un composant (invisible) à part entière, chargé de faire le lien entre des sollicitations (ici : mise à jour des composants visuels avec un tableau de données) et les composants visés. Ce médiateur est une sorte de routeur applicatif.
Le problème réside dans le fait que la complexité de notre médiateur croît avec le nombre de composants de l’interface graphique. De plus, son code est dépendant des composants qu’il pilote ; ainsi la moindre modification de l’interface de ces composants implique une répercussion dans le code du médiateur.
Pour résoudre ce problème, nous proposons d’avoir recours au Pattern Commande, qui correspond à l’exécution d’une action particulière dans notre logiciel. Chaque formulaire de saisie (une fois la validation des champs réalisée) déclenchera l’exécution d’une commande particulière ; celle-ci se chargera d’exécuter les accès aux données, les traitements, etc… Une fois les commandes exécutées, le médiateur notifiera les composants graphiques (souvenez-vous, grâce au mécanisme d’abonnement / publication) des évènements étant survenus.
Vous pourriez contester la raison d’être de notre médiateur si nous employons la technique des commandes. C’est une question intéressante ; le principal argument en faveur de l’utilisation conjointe de ces deux Design Patterns est que le médiateur représente un passage obligé pour les composants (type formulaire ou déclenchement) souhaitant exécuter une commande. Quelques propriétés intéressantes en découlent :
· Le médiateur permet d’alléger la complexité des composants type formulaire. En effet, le déclenchement d’une commande peut se faire de manière synchrone ou asynchrone, il faut peut-être gérer un pool de Threads… tâches qui ne devraient pas être répétées à plusieurs endroits du code de notre application.
· Le médiateur connaît la liste des commandes en cours d’exécution. Certaines d’entre elles sont parfois redondantes, ou exclusives ; le médiateur peut donc prendre des mesures pour assurer la cohérence, la réactivité ou la performance globale de notre application. Par exemple, dans notre petite application d’affichage de données dans un ensemble de camemberts, que faire lorsque l’utilisateur est suffisamment rapide (imaginez que le rendu soit très long, en 3 dimensions, etc…) pour modifier les données et demander à Actualiser avant que la commande d’actualisation précédente ne soit terminée (il reste 2 composant à rafraîchir). Une optimisation intéressante consiste à ne pas mener la commande précédente à terme (on ne rafraîchit pas les 2 composants restants), et à débuter la suivante (idéalement, en commençant par les composants laissés pour compte).
· Le médiateur est également un excellent outil pour implémenter (généralement à l’aide de classes dédiées) la validation des champs d’un formulaire. En effet, le formulaire peut procéder à la validation de surface de ses propres champs, mais n’a pas intérêt à implémenter directement un accès aux données (en base de données par exemple). Déporter la validation fine des données dans une classe pilotée par le médiateur sépare donc bien les responsabilités.

Ce mécanisme permet également, sans répercussion sur notre code, d’ajouter de nouveaux moyens de déclencher les commandes tels que les boîtes à outils (boutons qui déclenchent directement une action), les menus déroulant ou contextuels, les raccourcis clavier… Le médiateur garantit l’unicité de l’accès aux commandes, et les commandes elles-mêmes garantissent que tous ces moyens de déclenchement graphiques sont sémantiquement équivalents au sens de notre application.
Pour finir ce tour d’horizon du développement de clients riches utilisant les Design Pattern, mentionnons le fait que la plupart des logiciels proposent à leurs utilisateurs (autant que faire se peut) un mécanisme permettant d’annuler les dernières actions entreprises (dans l’ordre chronologique inverse). Comment implémenter ce mécanisme ? A qui incombe cette responsabilité ? Aux « Commandes », bien sûr : il suffit, pour chaque commande, d’implémenter une méthode supplémentaire « Undo » réciproque à la méthode d’exécution typique de la commande. Il existe plusieurs techniques permettant de remettre le système dans un état antérieur ; les principales sont de :
· prendre un cliché de tout ou partie des objets à un instant donné (par clonage, sérialisation ou tout autre mécanisme de stockage simple). Si l’on demande à défaire la commande correspondante, il suffira de dé-sérialiser le graphe, et de remplacer les objets en mémoire par ces objets « restaurés ».
· mémoriser les différences entre l’état précédant l’exécution de la commande et l’état suivant. Pour annuler la commande, il faudra alors imaginer le mécanisme permettant de faire machine arrière. Cette technique est plus optimale en termes de stockage, d’efficacité, mais peut s’avérer plus complexe (cela dépend des commandes en question).
Il suffit donc que le médiateur maintienne une pile de toutes les commandes exécutées (dans l’ordre d’invocation). Pour remonter l’historique (défaire les commandes les unes après les autres), il suffit de dépiler la dernière commande, et d’exécuter sa méthode réciproque.
En général, l’utilisateur souhaite également pouvoir « re-faire » les choses qu’il a défaites. Rien de plus simple : une deuxième pile, également maintenue par le médiateur, mémorise la liste des commandes défaites. Pour les refaire, on les ré-exécute à nouveau dans le sens chronologique.
Par cette petite étude de cas, nous avons illustré que les deux frameworks, WindowsForms et Java Swing sont très proches. Leurs similitudes vont des composants de base aux techniques avancées (fenêtres internes et applications multi-documents, programmation asynchrone…).
Nous avons également observé que les Design Patterns employés (par les bibliothèques de composants ou dans leur utilisation) sont les mêmes ; ils sont parfois partie prenante des frameworks (pool de Threads automatique .NET), ou laissés libres à l’implémentation.
Dernière étape dans notre démarche : une fois le client riche développé, testé et validé par nos utilisateurs, il s’agit de le déployer sur tous leurs postes. Cette étape a longtemps été la bête noire des sociétés utilisant des architectures client-serveur, mais ces problématiques ont trouvé, depuis quelques années, des réponses efficaces. Voyons ce que proposent .NET et J2EE en termes de déploiement semi-automatique de clients « lourds ».
Il est possible dans la plate-forme Java, de créer des scripts d’installation automatique d’un logiciel (on peut mettre ces scripts à disposition des utilisateurs sur un intranet, sur internet, ou encore sur un CD-ROM). Mais le plus intéressant, dans l’hypothèse où les utilisateurs disposent d’une connexion réseau, est d’utiliser Java Web Start.
JavaWebStart est un petit logiciel qu’il faut installer côté client ; il mesure entre 700 Ko et 5 Mo selon que le poste utilisateur dispose ou non d’un runtime Java. Ce logiciel se comporte comme un plug-in et permet le déploiement et le lancement automatique de logiciels par un simple clic sur un lien hypertexte dans un navigateur Web.
Plus précisément, lorsqu’un utilisateur suit un lien hypertexte qui l’amène à un document JNLP (Java Network Launch Protocol), le navigateur associe le type MIME de ce fichier avec le JavaWebStart, qui se lance et interprète le contenu de ce fichier JNLP. Il va y trouver le nom, l’icône de l’application, un lien vers son ou ses fichiers JAR (archives), et enfin la version Java minimale nécessaire à la bonne exécution de cette application.
Lors du premier lancement (clic) de l’application, le JavaWebStart la télécharge et l’enregistre dans un répertoire du disque dur côté client ; puis il la lance. Lors des lancements ultérieurs, le JavaWebStart se borne à vérifier que la version de l’application n’a pas changé depuis sa dernière exécution ; il ne la re-téléchargera que si les versions côté client et serveur ne concordent pas.
L’avantage de cette technique est que la configuration des applications se fait côté serveur. Rendre une nouvelle application disponible à un ensemble de clients consiste donc à :
· placer un fichier JNLP sur le serveur, qui détaille les informations nécessaires au téléchargement et au déclenchement de l’application
· s’assurer bien sûr que tous les clients disposent bien du JavaWebStart sur leur poste.
Pour illustrer cette architecture, nous avons extrait cette image du site de Sun Microsystem :

De même qu’en Java, il est possible de créer des scripts d’installation automatique dans la plate-forme .NET (fichiers MSI, Microsoft Windows Installer). C’est la méthode de déploiement recommandée par le Framwork .NET.
On peut aussi mettre à disposition les assemblies d’une application : si le framework .NET est installé sur tous les postes, il suffit aux utilisateurs de copier les assemblies dans le répertoire de leur choix et de lancer l’exécutable principal de l’application ! (le pré-requis est le même en Java : le JRE doit être installé, ce qui peut être rendu automatique bien entendu)
Enfin, la nouveauté vient du fait que l’utilisateur peut également lancer une application .NET en indiquant simplement dans un navigateur Web l’adresse URL de son fichier exécutable. Les assemblies sont alors automatiquement téléchargées, et l’application lancée. Ce mécanisme s’approche donc de celui de Java Web Start. Attention toutefois aux droits des applications ainsi téléchargées : le classloader les isole automatiquement dans un espace de nommage correspondant au nom de domaine du site de téléchargement, et les applications héritent des paramètres de sécurité correspondant à celles de la zone Internet visitée (Internet, Intranet local, Sites de confiance…).
Moralité, le mieux est de se conformer aux recommandations de l’éditeur et
d’utiliser les scripts d’installation MSI ou de télécharger soi-même les
assemblies (copie de fichier ou FTP par exemple).
Les frameworks WindowsForms et JavaSwing sont assez proches. Ils permettent tous deux de réaliser de superbes interfaces graphiques côté client, et d’avoir une réactivité bien meilleure qu’un client léger (HTML, JavaScript). La prise en main de ces bibliothèques de composants est grandement facilitée par les outils d’assemblage et de conception graphiques, qui génèrent du code (Java, C#, VB.NET) automatiquement ; les outils employés dans cet article génèrent un code de qualité, respectant les bonnes pratiques de conception orientée objet prônées par .NET et J2EE.
Mais l’outillage ne fait pas tout : la discipline traditionnelle qui consiste à limiter le couplage de nos composants logiciels, à maximiser la réutilisation, et à affecter aux bons éléments les bonnes responsabilités est toujours de mise. La conception de clients riches est donc une activité avide de Design Patterns ; ceux-ci sont les mêmes quelle que soit la plate-forme utilisée.
Enfin, le déploiement de clients dits « lourds » est une tâche aujourd’hui bien maîtrisée, qui n’implique plus les coûts exorbitants des premières années du monde client-serveur.
Les architectures « client léger » restent aujourd’hui très en vogue. Toutefois, dans certaines situations, elles ne conviennent pas : c’est typiquement le cas pour les applications à forts besoins en réactivité. Au vu de tous les outils que nous avons présentés, n’hésitons plus : développons des clients sexy !
Auteur : Thomas GIL
Copyright : DotNetGuru Ó 2002
NetBeans (http://www.netbeans.org/)
Java Swing (http://java.sun.com/products/jfc/)
Java Web Start (http://java.sun.com/products/javawebstart/architecture.html)
Microsoft WinForms (http://msdn.microsoft.com/vstudio/techinfo/articles/clients/winforms.asp)
CrystalReports (http://www.crystaldecisions.com/)
Design Patterns (http://hillside.net/patterns/, http://www.cs.wustl.edu/~schmidt/patterns.html
)