Développement d’application web. L’approche composant et l’approche modulaire (1/2)

Je l’avais expliqué plus tôt sur ce blog, j’ai l’habitude de réaliser des applications « riches » (RIA) en Flex depuis pas mal de temps. Avec l’expérience, on apprend à concevoir son application pour que celle-ci ne soit pas monolithique.

Pour cela, je distingue deux approches différentes et complémentaires:

  • l’approche « composant »
  • l’approche « modulaire »

Je met de côté l’aspect MVC dont on entend beaucoup parler et qui, avec le recul, ne me plait pas plus que cela car souvent mal adapté à mes cas d’utilisation.

Ce sont ces deux aspects que j’ai utilisé dans toutes mes applications Flex réalisées au sein de Business Geografic et je peux vous assurer que cela a permis un développement rapide et facile à maintenir (pas trop dur en tout cas, ce n’est pas magique non plus).

L’approche « composant »

L’approche composant consiste à séparer son application en petites « parties » ou « blocs ». La réalisation d’une application consiste au final à assembler ces blocs (pensez aux Lego) et à les faire communiquer. Dans la pratique, un composant est souvent aussi appelé « vue » car c’est avant tout un composant graphique auquel on va fournir de la donnée.

Un composant peut lui-même être constitué d’autres composants, c’est aussi ce que l’on appelle le Design Pattern « Composite ». Il faut dans la mesure du possible que chaque composant soit le moins couplé possible avec les autres composants. Cela vous évite les effets de bord et vous aidera dans le découpage de votre application.

Attention tout de même à ne pas appliquer cette règle de manière trop abrutie, en créant des composants trop génériques. Vous aurez parfois besoin de créer des composants fortement liés avec d’autres, c’est tout à fait normal. Vos composants « métier » peuvent avoir des liaisons fortes avec d’autres composants. Ce sont ces composants métier qui vont constituer votre librairie « haut-niveau ».

Rendre un composant complètement générique n’est pas simple. C’est un peu plus simple en JavaScript grâce à la nature dynamique du langage mais ça l’est moins en ActionScript et dans les autres langages compilés car chaque dépendance va aussi être compilée avec l’application, c’est à ce moment-là que l’on utilise notamment les Interface. Par expérience, vouloir rendre un composant trop générique, avant de savoir que l’on va l’utiliser à d’autres endroits de son code a souvent résulté en un code trop difficile à lire / comprendre que ce qu’il aurait du être. Cela s’apparente souvent à de l’optimisation prématurée.

Pour permettre à un composant de communiquer avec l’extérieur (et vice-versa), vous avez plusieurs possibilités:

  • Accès à des propriétés publiques du composant (son id, sa largeur, son code client lié, …)
  • Une mécanique évènementielle permettant au composant de propager des évènements pour informer un ou plusieurs autres composants d’un changement de son état

Faire communiquer deux composants par leurs propriétés publiques

La première option est la plus simple car n’importe qui peut aller lire une propriété publique d’un composant. C’est encore plus simple en JavaScript car tout est public. Mais vous vous rendrez compte que lier le composant A à la propriété « client » (par exemple) d’un composant B revient à coupler ces 2 composants.

En ActionScript, ce que l’on fait souvent (enfin j’espère que vous le faites!) est que la propriété « client » ou la méthode « getClient() » est définie au sein d’un Interface. Pour rappel, une Interface définit le contrat de service du composant (comme chez Darty). Une Interface ne contient pas de code, elle définit seulement un contrat. C’est-à-dire que chaque composant qui implémente cette interface va devoir respecter ce contrat. Dans notre cas, le composant B va implémenter l’interface en question et exposer la propriété « client » ou la méthode « getClient() » pour revenir à notre exemple.

Ensuite, le composant A va être couplé à l’Interface et plus directement à B. Si on doit remplacer B par un composant C ou un composant B modifié, pas de problème, il suffit que C implémente correctement l’Interface.

Voilà la recette pour faire du générique :). En ActionScript, c’est aussi la recette pour faire de l’injection de dépendance mais ce n’est pas ce qui nous intéresse ici.

Faire communiquer deux composants par des évènements

La mécanique évènementielle est plus difficile à mettre en place mais c’est la plus puissante et la plus malléable (et ma préférée). Je vais expliquer comment cela se passe en ActionScript 3 (Flex) mais ce concept n’est pas spécifique à de l’AS3.

Prenons un exemple simple. Vous avez un composant « ClientPicker » avec une liste de choix contenant une liste déroulante de clients et un bouton « OK » permettant à l’utilisateur de valider son choix. Ce composant est au passage un Composite de deux composants: la ComboBox et le Button.
Bien entendu, votre application sera composée d’autres composants qui auront besoin de savoir que l’utilisateur a changé de client. Dans ce cas-là, on ne peut pas se fier à des propriétés de composants, on va laisser « ClientPicker » informer ceux qui en ont envie que le client a changé. Pour cela, « ClientPicker » va créer un évènement et le propager (dispatch en anglais).

En ActionScript, on va créer une nouvelle classe « ClientPickerEvent » qui va hériter de la classe de base « Event ». Cette classe « ClientPickerEvent » va contenir une propriété « client » qui va transporter le client sélectionné. Chaque évènement a un type. Ce type est une simple chaîne de caractère qui est souvent stockée comme constante static dans la classe ClientPickerEvent. Dans notre exemple, le type de l’évènement sera « onClientChange ».

Dans l’ordre:

  1. Le composant ClientPicker est instancié et ajouté dans l’interface de votre application
  2. ClientPicker écoute l’évènement « click » de bouton OK
  3. L’utilisateur change le client dans la liste et appuie sur OK
  4. ClientPicker crée un évènement de type ClientPickerEvent, lui donne le type « onClientChange » et assigne la propriété « client » de cet évènement avec le client sélectionné
  5. ClientPicker propage l’évènement de type ClientPickerEvent
  6. et c’est tout.

Notre composant ne va pas agir directement sur une autre composant car en fait, il ne connait personne.

Prenons maintenant le cas du composant « ClientInformations » qui a besoin de savoir que le client a été modifié. Pour cela, il lui suffit d’écouter l’évènement de type « onClientChange » propagé par ClientPicker. Il peut le faire directement mais le plus souvent on va avoir un objet intermédiaire qui va connecter les deux composants. Cet objet intermédiaire est un Mediator (parfois appelé Controller ou Manager) dont le rôle sera de gérer la communication entre les composants. Le Mediator connait le composant ClientPicker et le composant ClientInformations mais les composants entre eux ne se connaissent pas.

Dans l’ordre:

  1. Le composant ClientInformations est instancié et ajouté à l’application
  2. ClientInformations écoute un évènement de type « onClientChange », directement ou par l’intermédiaire d’un Mediator.
  3. A un moment donné, il va recevoir cet évènement et récupérer une instance de ClientPickerEvent.
  4. Depuis l’évènement, il va récupérer le contenu de la propriété « client », contenant le client sélectionné
  5. ClientInformations se met à jour par rapport à cette information qu’il vient de récupérer

Voilà, vous savez tout sur la communication entre composants par des évènements :). Veuillez noter que c’est en grande partie comme cela que cela se passe dans Flex et dans Flash Player en général mais ce concept n’est pas propre à Flash.

Voici les grands avantages à utiliser ce genre de mécanique:

  • Le composant ClientPicker peut évoluer et être remplacé sans problème, son contrat à lui est qu’il propage des évènements « onClientChange ». Par exemple, si vous voulez que la mise à jour de votre interface se fasse directement lorsque l’utilisateur fait son choix dans la ComboBox, il suffit que ClientPicker écoute le « change » de la ComboBox et envoie l’évènement « onClientChange » qui va bien.
  • Plusieurs composants peuvent s’abonner à l’évènement « onClientChange » sans problème. C’est plus difficile lorsque l’on travaille avec les propriétés publiques d’un composant (voir plus haut)
  • Il est possible de n’avoir aucun couplage direct entre les composants, en passant par un Mediator qui connait les deux composants.
  • C’est une des meilleures manières de créer une API. En effet, il est très facile de construire au dessus d’une mécanique évènementielle sans connaître le fonctionnement interne de celle-ci.

C’est tout pour l’approche que j’appelle « composant ». Dans la plupart des cas, une bonne implémentation de cette approche vous permettra de créer votre application sans trop de problème. Pour des applications plus larges (les fameuses RIA), on a parfois besoin d’avoir en plus une approche « modulaire ». C’est ce que l’on va voir dans le billet suivant!

5 réflexions au sujet de « Développement d’application web. L’approche composant et l’approche modulaire (1/2) »

  1. vincentLg

    Très intéressant, c’est claire qu’il y a bcp de bénéfices à utiliser la mécanique évènementielle, mais sans « une bonne implémentation de cette approche », ça peut vraiment foutre le bordel dans les applications ;) Je garde un très mauvais souvenir d’une app Flex blindée d’écouteurs et de dispatcheurs d’event, impossible à debugger. En faite c’est tellement « pas couplé » que ça peut rendre le code impossible à lire :(

    Répondre
  2. fadi

    Je tiens à vous féliciter pour la qualité de vos articles.
    J’ai appris pas mal de choses grâce à votre blog Flex.
    concernant l’html5, j’ai trouvé un framework qui a l’air pas mal ^pour les développements mobiles http://www.sencha.com/
    est ce que vous avez eu l’occasion de le tester?
    sinon, je vais le tester bientôt et je vous donne mon retour sur expérience si celà vous intéresse.

    Répondre
  3. admin Auteur de l’article

    @vincent Effectivement, il ne faut pas en abuser car cela devient plus difficile à debugger que du code procédural. Bien garder les évènements pour les points d’extension / d’entrée

    @fadi Merci, j’ai vu ce que faisais sencha mais je ne suis pas fan fan. Pas vraiment envie de m’engager avec un éditeur après ce qu’il nous est tous arrivé avec Adobe :)

    Fabien

    Répondre
  4. Ping : Développement d’application web. L’approche composant et l’approche modulaire (2/2) | HTML5 Tutorial

  5. Ping : Modifier le comportement d’une application modulaire grâce à des évènements | HTML5 Tutorial

Répondre à vincentLg Annuler la réponse.

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *