WinDev et le Design pattern observateur

Design pattern de type comportement : observateur

Ce tutoriel explique la mise en place du Design Pattern observateur dans l'EDI WinDev.

2 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Définition

Avant de nous lancer tête baissée dans la programmation, nous allons découvrir plusieurs définitions référencées lors de la mise en place de cet article :

  • Livre : Design patterns - Tête la première

Le pattern observateur définit une relation de type un-à-plusieurs, de façon que, lorsqu'un objet change d'état, tous ceux qui en dépendent en soient notifiés et soient mis à jour automatiquement.

  • Livre Design patterns en Java

Le Pattern Observer a pour objectif de construire une dépendance entre un sujet et des observateurs de sorte que chaque modification du sujet soit notifiée aux observateurs afin qu'ils puissent mettre à jour leur état.

  • Wikipédia

Le patron de conception observateur/observable est utilisé en programmation pour envoyer un signal à des modules qui jouent le rôle d'observateur. En cas de notification, les observateurs effectuent alors l'action adéquate en fonction des informations qui parviennent depuis les modules qu'ils observent (les « observables »).

Comme nous venons de voir avec ces définitions, nous avons deux entités :

  • sujet ou observable ;
  • observateurs.

Le sujet envoie une notification à ses observateurs, ceux-ci se mettent à jour dès réception de celle-ci. Vous remarquerez que dans mon explication j'ai utilisé le mot « ses », sous-entendant l'utilisation de la composition. Ce Design pattern peut être utilisé dans les cas suivants :

•    la modification d'un objet entraîne la modification d'autres objets déterminés dynamiquement ;

•    un objet veut prévenir d'autres objets sans connaître leur type. On parlera de couplage faible.

II. UML

Après avoir acquis un ensemble de définitions, nous allons nous intéresser à la conception de ce Design pattern. Au même titre que les définitions varient dans la manière d'exprimer l'idée, les diagrammes UML diffèrent. Même si au niveau des définitions, l'idée reste la même, les différences de diagramme UML entraînent des choix de programmation différents. Dans ce paragraphe nous allons voir trois diagrammes différents et mon avis sur ceux-ci.

II-A. Héritage/Compositions

Image non disponible
Fig. : schéma UML

Source : http://www.cs.mcgill.ca/~hv/classes/CS400/01.hchen/doc/observer/observer.html

Ce diagramme UML utilise deux notions :

  • héritage : les classes ConcreteSubject et ConcreteObserver héritent des classes Subject et Observer ;
  • composition : la classe Subject possède un membre regroupant toutes les références des Observer. La classe ConcreteObserve possède un membre référençant le sujet.

Pourquoi ne pas utiliser cette conception ?

  • L'héritage : utiliser l'héritage lie fortement nos objets alors que dans le premier paragraphe, nous avons parlé de couplage faible.
  • La double composition : si l'observateur possède la référence de ces observables, pourquoi l'observateur a-t-il une référence de l'observable ? Nous pourrions remplacer cette référence par un passage de paramètre.

II-B. Implémentation/Compositions

Image non disponible
Fig. : schéma UML

Source : http://design-patterns.fr/observateur

Ce diagramme UML utilise deux notions :

  • implémentation : les classes ConcreteSubject et ConcreteObserver implémentent les interfaces Subject et Observer ;
  • composition : la classe Subject possède un membre regroupant toutes les références des Observer. La classe ConcreteObserve possède un membre référençant le sujet

Pourquoi ne pas utiliser cette description ?

La double composition : si l'observateur possède la référence de ces observables, pourquoi l'observateur a-t-il une référence de l'observable. Nous pourrions remplacer cette référence par un passage de paramètre.

II-C. Implémentation/Composition

Image non disponible
Fig. : schéma UML

Source : http://blog.soat.fr/2013/04/le-design-pattern-observer-limplementation-proposee-par-microsoft-en-net-4-0/

Ce diagramme UML utilise deux notions :

  • implémentation : les classes ConcreteSubject et ConcreteObserver implémentent les interfaces Subject et ObserverAuteur inconnu2015-11-03T11:49:13.35Police (dans les autres cas vous avez mis en texte non proportionnel). ;
  • composition : la classe Subject possède un membre regroupant toutes les références des Observer.

Cette conception rectifie les défauts des deux exemples précédents. C'est celle-ci que nous allons utiliser dans la suite de la publication. Ci-dessous les rôles des différents objets du diagramme :

  • Subject : est l'interface de l'objet à observer. Elle fournit les méthodes (ajouter, modifier, supprimer, notifier) à redéfinir dans les classes implémentant cette interface ;
  • ConcreteSubject : est l'implémentation de l'interface à observer. Lorsqu'une valeur est modifiée, la méthode notifyObserver()est appelée ;
  • Observer : est l'interface de l'observateur. Il déclare la/les méthode(s) de l'interface Observer ;
  • ConcreteObserverOne et ConcreteObserverTwo : implémentent l'interface Observer. La partie cliente indique à l'objet Subject les objets Observer qu'il (objet Subject) avertira.

Les observateurs peuvent récupérer le ou les états de l'observable via des fonctions, accesseurs de celui-ci ; cette solution est nommée « TIRER ». Effectivement les observateurs vont tirer les informations de l'observable. Mais une seconde variante existe, celle-ci est nommée « POUSSER ». À l'inverse de la première, c'est l'observable qui va fournir les informations aux observateurs lors de la modification de l'état. Nous allons maintenant voir à travers un cas concret la mise en place de ce Design Pattern avec ses deux variantes.

III. Mise en application

III-A. Description du problème

Nous devons développer une fenêtre avec les descriptions suivantes :

•    une table avec les adresses des clients ;

•    un ensemble de champs (numéro, type de voie, nom de voie, code postal, ville) pour visualiser cette adresse ;

•    un champ carte de type Google Maps.

Exemple d'interface graphique :

Image non disponible
Fig. 4 : exemple d'interface graphique

Pour développer ce problème, nous allons lister les classes en partant de schéma UML :

  • Subject : représente l'interface adresse observable : classe abstraite ac_adresseObservable.

Image non disponible

WinDev en version 20 et antérieures ne permettent pas de déclarer des interfaces. Pour simuler la notion d'interface, nous allons utiliser des classes abstraites avec des méthodes vides.

  • Observer : représente les interfaces des adresses Observer : classe abstraite  ac_adresseObservateur.
  • ConcreteSubject : représente l'entité observable : classe pc_AdresseClient.
  • ConcreteObserverOne : représente l'entité observer groupe de champs « adresse » : classe pc_AdresseGrpChamp.
  • ConcreteObserverTwo : représente l'entité observer champ Google Maps : classe pc_AdresseGoogleMaps.

Après avoir listé les sujets tâches à effectuer, nous allons mettre en application selon les deux versions du Design pattern.

III-B. Observateur : version « POUSSER »

Dans cette version, c'est l'observable qui met à disposition son/ses état(s) aux observateurs.

III-B-1. Classe ac_adresseObservable

Code de la classe ac_AdresseObservable
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
Déclaration de ac_clientObservable
ac_AdresseObservable est une Classe, abstraite
      
FIN
 
PROCEDURE Constructeur()
 
 
PROCEDURE Destructeur()
 
 
// Résumé : Ajoute un observateur
// Syntaxe :
//EnregistreObservateur (<po_clientObservateur> est ac_AdresseObservateur dynamique)
//
// Paramètres :
//    po_clientObservateur (ac_AdresseObservateur dynamique) : objet de type observateur à ajouter à la liste
// Valeur de retour :
//    Aucune
//
// Exemple :
// lo_adrresseObservable:EnregistreObservateur(lo_clientObservateur)
//
PROCEDURE EnregistreObservateur(po_clientObservateur est un ac_AdresseObservateur dynamique)
 
 
// Résumé : Supprime un observateur à la liste
// Syntaxe :
//SupprimeObservateur (<po_clientObservateur> est ac_AdresseObservateur dynamique)
//
// Paramètres :
//    po_clientObservateur (ac_AdresseObservateur dynamique) : objet de type observateur à supprimer à la liste
// Valeur de retour :
//    Aucune
//
// Exemple :
// Indiquez ici un exemple d'utilisation.
// lo_adrresseObservable:SupprimeObservateur(lo_clientObservateur)
PROCEDURE SupprimeObservateur(po_clientObservateur est un ac_AdresseObservateur dynamique)
 
 
// Résumé : Notifie aux observateurs du changement d'état
// Syntaxe :
// notifierObservateurs ()
//
// Paramètres :
//    Aucun
// Valeur de retour :
//    Aucune
//
// Exemple :
// lo_adrresseObservable:notifierObservateurs()
//
PROCEDURE notifierObservateurs()
 

III-B-2. ac_AdresseObservateur

Code la classe ac_AdresseObservateur
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
ac_AdresseObservateur est une Classe
 
FIN
 
PROCEDURE Constructeur()
 
 
PROCEDURE Destructeur()
 
 
// Résumé : Actualiser l'observateur avec les informations de l'observable
// Syntaxe :
//Exe_Actualiser (<ps_adresse1> est chaîne, <ps_adresse2> est chaîne, <ps_adresse3> est chaîne, <ps_codePostal> est chaîne, <ps_ville> est chaîne)
//
// Paramètres :
//    ps_adresse1 (chaîne ANSI) : zone 1 adresse
//    ps_adresse2 (chaîne ANSI) : zone 2 adresse
//    ps_adresse3 (chaîne ANSI) : zone 3 adresse
//    ps_codePostal (chaîne ANSI) : code postal
//    ps_ville (chaîne ANSI) : ville
// Valeur de retour :
//    Aucune
//
// Exemple :
// :lst_observateurs[1]:Exe_Actualiser("1 rue du stade","","","57000","Metz").
//
PROCEDURE Exe_Actualiser(ps_adresse1 est une chaîne, ps_adresse2 est une chaîne, ps_adresse3 est une chaîne, ps_codePostal est une chaîne, ps_ville est une chaîne)

III-B-3. pc_AdresseClient

Code de la classe pc_AdresseClient
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
pc_AdresseClient est une Classe
      hérite de ac_AdresseObservable
      
      PRIVÉE
             mlst_Observateur est un tableau de 0 ac_AdresseObservateur dynamique
             ms_adresse1 est une chaîne
             ms_adresse2 est une chaîne
             ms_adresse3 est une chaîne
             ms_codePostal est une chaîne
             ms_ville est une chaîne
FIN
 
PROCEDURE PUBLIQUE adresse1() : chaîne
 
RENVOYER ms_adresse1
PROCEDURE PUBLIQUE adresse1(Valeur est une chaîne)
 
ms_adresse1=Valeur
PROCEDURE PUBLIQUE adresse2() : chaîne
 
RENVOYER ms_adresse2
PROCEDURE PUBLIQUE adresse2(Valeur est une chaîne)
 
ms_adresse2=Valeur
PROCEDURE PUBLIQUE adresse3() : chaîne
 
RENVOYER ms_adresse3
PROCEDURE PUBLIQUE adresse3(Valeur est une chaîne)
 
ms_adresse3=Valeur
PROCEDURE PUBLIQUE codePostal() : chaîne
 
RENVOYER ms_codePostal
PROCEDURE PUBLIQUE codePostal(Valeur est une chaîne)
 
ms_codePostal=Valeur
PROCEDURE PUBLIQUE ville() : chaîne
 
RENVOYER ms_ville
PROCEDURE PUBLIQUE ville(Valeur est une chaîne)
 
ms_ville=Valeur
PROCEDURE Constructeur()
 
:mlst_Observateur=allouer un tableau de 0 ac_AdresseObservateur dynamique
PROCEDURE Destructeur()
 
 
 
PROCEDURE EnregistreObservateur(po_clientObservateur est un ac_AdresseObservateur dynamique)
 
Ajoute(:mlst_Observateur,po_clientObservateur)
PROCEDURE SupprimeObservateur(po_clientObservateur est un ac_AdresseObservateur dynamique)
 
POUR li_i=1 _A_ :mlst_Observateur..Occurrence
      SI :mlst_Observateur[li_i] = po_clientObservateur ALORS
            TableauSupprime(:mlst_Observateur,li_i)
             SORTIR
      FIN
FIN
 
 
// Redéfinition de la méthode ac_ClientObservable.notifierObservateurs
PROCEDURE notifierObservateurs()
 
lo_ObservateurTmp est un ac_AdresseObservateur dynamique
 
POUR li_i=1 _A_ :mlst_Observateur..Occurrence
      lo_ObservateurTmp=:mlst_Observateur[li_i]
lo_ObservateurTmp:Exe_Actualiser(:ms_adresse1,:ms_adresse2,:ms_adresse3,:ms_codePostal,:ms_ville)
     
FIN
PROCEDURE Maj_Adresse(ps_adresse1 est une chaîne, ps_adresse2 est une chaîne, ps_adresse3 est une chaîne, ps_codePostal est une chaîne, ps_ville est une chaîne)
 
:ms_adresse1=ps_adresse1
:ms_adresse2=ps_adresse2
:ms_adresse3=ps_adresse3
:ms_codePostal=ps_codePostal
:ms_ville=ps_ville
 
:notifierObservateurs()

III-B-4. pc_AdresseGoogleMaps

Code de la classe pc_AdresseGoogleMaps
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
pc_AdresseGoogleMaps est une Classe
      hérite de ac_AdresseObservateur
FIN
 
PROCEDURE Constructeur(po_observable est un ac_AdresseObservable dynamique)
 
po_observable:EnregistreObservateur(objet)
 
PROCEDURE Destructeur()
 
 
// Redéfinition de la méthode ac_ClientObservateur.Exe_Actualiser
PROCEDURE Exe_Actualiser(ps_adresse1 est une chaîne, ps_adresse2 est une chaîne, ps_adresse3 est une chaîne, ps_codePostal est une chaîne, ps_ville est une chaîne)
 
lo_XML est un xmlDocument
ls_latitude, ls_longitude est une chaîne
 
SI HTTPRequête("https://maps.googleapis.com/maps/api/geocode/xml?address="+ps_adresse1+"+"+ps_adresse2+"+"+ps_adresse3+",+"+ps_codePostal+"+"+ps_ville+"&key=AIzaSyD5DNLIFxKdrF-PJngyDx7LDIoXNwwhsto") ALORS
lo_XML=XMLOuvre(HTTPDonneRésultat(httpRésultat),depuisChaîne)
ls_latitude=lo_XML.GeocodeResponse.RESULT.geometry.location.lat
ls_longitude=lo_XML.GeocodeResponse.RESULT.geometry.location.lng
FIN
 
gsCodeHtmlPartie1  est une chaîne
gsCodeHtmlPartie1 =[
      <!DOCTYPE html>
      <html>
      <head>
      <title>Asynchronous Loading</title>
      <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
      <meta charset="utf-8">
      <style>
      html, body, #map-canvas {
      height: 100%;
      margin: 0px;
      padding: 0px
      }
      </style>
      <script>
      
      var geocoder;
      var map;
      
      function initialize() {
      var myLatlng = new google.maps.LatLng(%1,%2);
      var mapOptions = {
      zoom: 14,
      center: myLatlng
      }
      var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
      
      var marker = new google.maps.Marker({
      position: myLatlng,
      map: map,
      title: 'Hello World!'
      });
      }
      
      
      function loadScript() {
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp' +
      '&signed_in=true&callback=initialize';
      document.body.appendChild(script);
      }
      
      window.onload = loadScript;
      
      </script>
      
      </head>
      <body>
      
      <div id="map-canvas"></div>
      
      </body>      
</html>
]
 
 
{MaFenêtre..Nom+".HTML1",indChamp} = ChaîneConstruit(gsCodeHtmlPartie1,ls_latitude,ls_longitude)

III-B-5. pc_AdresseGrpChamp

Code de la classe pc_AdresseGrpoChamp
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
pc_AdresseGRpChamp est une Classe
    hérite de ac_AdresseObservateur
FIN
 
PROCEDURE Constructeur(po_Observable est ac_AdresseObservable dynamique)
 
po_Observable:EnregistreObservateur(objet)
 
PROCEDURE Destructeur()
 
 
// Redéfinition de la méthode ac_ClientObservateur.Exe_Actualiser
PROCEDURE Exe_Actualiser(ps_adresse1 est une chaîne, ps_adresse2 est une chaîne, ps_adresse3 est une chaîne, ps_codePostal est une chaîne, ps_ville est une chaîne)
 
{MaFenêtre..Nom+".sai_Adresse1",indChamp}=ps_adresse1
{MaFenêtre..Nom+".sai_Adresse2",indChamp}=ps_adresse2
{MaFenêtre..Nom+".sai_Adresse3",indChamp}=ps_adresse3
{MaFenêtre..Nom+".sai_CodePostal",indChamp}=ps_codePostal
{MaFenêtre..Nom+".sai_Ville",indChamp}=ps_ville

III-B-6. Test

Ci-dessous le code pour tester la version « pousser ». Déclaration des variables dans la partie globale de la fenêtre :

Code du test
Sélectionnez
1.
2.
3.
go_adresseClient est pc_AdresseClient()
go_GrpClient est un pc_AdresseGRpChamp(go_adresseClient)
go_GoogleMapsClient est pc_AdresseGoogleMaps(go_adresseClient)

Code la partie « sélection d'une ligne » de la table :

 
Sélectionnez
1.
go_adresseClient:Maj_Adresse(Tab_LstAdresse.Col_Adresse1[Tab_LstAdresse],Tab_LstAdresse.Col_Adresse2[Tab_LstAdresse],Tab_LstAdresse.Col_Adresse3[Tab_LstAdresse],Tab_LstAdresse.Col_CodePostal[Tab_LstAdresse],Tab_LstAdresse.Col_Ville[Tab_LstAdresse])

Ci-dessous le code d'initialisation de la table

Code d'initialisation de la table
Sélectionnez
1.
2.
3.
4.
TableAjouteLigne(Tab_LstAdresse,"1 rue du stade","","","57000","Metz")
TableAjouteLigne(Tab_LstAdresse,"1 rue du l'eglise","","","57000","Metz")
TableAjouteLigne(Tab_LstAdresse,"1 rue du serpenoise","","","57000","Metz")
TableAjouteLigne(Tab_LstAdresse,"1 rue du dupon des loges","","","57000","Metz")

Lançons la fenêtre, et lors du clic sur une ligne du tableau nous constatons que les champs adresse et la carte se mettent à jour.

III-C. Observateur : version « TIRER »

Dans cette version, ce sont les observateurs qui récupèrent le ou les état(s) de l'observable. Dans ce paragraphe, nous n'allons pas revoir l'ensemble du code des classes, mais uniquement les parties qui varient par rapport à la version « POUSSER ».

III-C-1. ac_AdresseObservateur

Modification de la signature de la fonction Exe_Actualiser.

Nouvelle définition de la fonction Exe_actualiser
Sélectionnez
1.
PROCEDURE VIRTUELLE Exe_Actualiser(po_observale est un ac_AdresseObservable dynamique)

III-C-2. pc_AdresseClient

Modification de la fonction notifierObservateurs pour faire appel à la nouvelle fonction Exe_Actualiser.

Fonction notifierObservateurs
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
PROCEDURE notifierObservateurs()
 
lo_ObservateurTmp est un ac_AdresseObservateur dynamique
 
POUR li_i=1 _A_ :mlst_Observateur..Occurrence
      lo_ObservateurTmp=:mlst_Observateur[li_i]
      lo_ObservateurTmp:Exe_Actualiser(objet)
FIN

III-C-3. pc_AdresseGoogleMaps

Modification de la fonction Exe_Actualiser.

Code de la fonction Exe_Actualiser
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
PROCEDURE VIRTUELLE Exe_Actualiser(po_adresseObservable est un ac_AdresseObservable dynamique)
 
//----->Définition des variables
ls_latitude, ls_longitude est une chaîne
lo_observateurTemps est un pc_AdresseClient dynamique
lo_XML est un xmlDocument
 
 
SELON po_adresseObservable..Classe
      
      CAS "pc_AdresseClient"
             lo_observateurTemps<-po_adresseObservable           
      
      AUTRE CAS
             
FIN
 
SI HTTPRequête("https://maps.googleapis.com/maps/api/geocode/xml?address="+lo_observateurTemps:adresse1+"+"+lo_observateurTemps:adresse2+"+"+lo_observateurTemps:adresse3+",+"+lo_observateurTemps:codePostal+"+"+lo_observateurTemps:ville+"&key=AIzaSyD5DNLIFxKdrF-PJngyDx7LDIoXNwwhsto") ALORS
lo_XML=XMLOuvre(HTTPDonneRésultat(httpRésultat),depuisChaîne)
ls_latitude=lo_XML.GeocodeResponse.result.geometry.location.lat
ls_longitude=lo_XML.GeocodeResponse.result.geometry.location.lng
FIN
 
 
gsCodeHtmlPartie1  est une chaîne
 
 
gsCodeHtmlPartie1 =[
      <!DOCTYPE html>
      <html>
      <head>
      <title>Asynchronous Loading</title>
      <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
      <meta charset="utf-8">
      <style>
      html, body, #map-canvas {
      height: 100%;
      margin: 0px;
      padding: 0px
      }
      </style>
      <script>
      
      var geocoder;
      var map;
      
      function initialize() {
      var myLatlng = new google.maps.LatLng(%1,%2);
      var mapOptions = {
      zoom: 14,
      center: myLatlng
      }
      var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
      
      var marker = new google.maps.Marker({
      position: myLatlng,
      map: map,
      title: 'Hello World!'
      });
      }
      
      
      function loadScript() {
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp' +
      '&signed_in=true&callback=initialize';
      document.body.appendChild(script);
      }
      
      window.onload = loadScript;
      
      </script>
      
      </head>
      <body>
      
      <div id="map-canvas"></div>
      
      </body>
      
      </html>
]
 
{MaFenêtre..Nom+".HTML1",indChamp} = ChaîneConstruit(gsCodeHtmlPartie1,ls_latitude,ls_longitude)

III-C-4. pc_AdresseGrpChamps

Modification de la fonction Exe_Actualiser.

Fonction Exe_Actualiser
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
PROCEDURE VIRTUELLE Exe_Actualiser(po_adresseObservable est un ac_AdresseObservable dynamique)
 
lo_AdresseObservableTemp est un pc_AdresseClient dynamique
 
SELON po_adresseObservable..Classe
 
      CAS "pc_AdresseClient"
             lo_AdresseObservableTemp<-po_adresseObservable           
             
      AUTRE CAS
             
FIN
 
{MaFenêtre..Nom+".sai_Adresse1",indChamp}=lo_AdresseObservableTemp:adresse1
{MaFenêtre..Nom+".sai_Adresse2",indChamp}=lo_AdresseObservableTemp:adresse2
{MaFenêtre..Nom+".sai_Adresse3",indChamp}=lo_AdresseObservableTemp:adresse3
{MaFenêtre..Nom+".sai_CodePostal",indChamp}=lo_AdresseObservableTemp:codePostal
{MaFenêtre..Nom+".sai_Ville",indChamp}=lo_AdresseObservableTemp:ville

III-C-5. Test

Pour les tests, nous utilisons le même jeu d'essai que celui du paragraphe III.B.6Test. Et nous constatons que les champs se mettent à jour lors de la sélection d'une adresse dans la table.

III-D. Différence

Nous venons de mettre en application les deux versions du Design pattern observateur. Notons les différences et simulons une évolution de cahier des charges. Comme évoqué ci-dessus, la différence entre ces deux versions est la façon dont les observateurs obtiennent la modification de l'état de l'observateur. Dans la version « POUSSER » la classe observable met à disposition ses états en paramètres alors que dans la version « TIRER » l'observateur va chercher les informations via un pointeur. Que se passe-t-il si nous modifions notre observable ? Dans notre cas rajoutons le membre pays.

Pour la version « POUSSER », nous devons modifier la fonction Exe_Actualiser de l'interface ac_AdresseObservable en ajoutant un paramètre. La signature de la fonction sera :

Entête de la fonction Exe_actualiser
Sélectionnez
1.
PROCEDURE VIRTUELLE Exe_Actualiser(ps_adresse1 est une chaîne, ps_adresse2 est une chaîne, ps_adresse3 est une chaîne, ps_codePostal est une chaîne, ps_ville est une chaîne, ps_pays est une chaîne)

Et modifier l'appel à cette fonction dans la procédure notifierObservateur de la classe pc_AdresseClient.

Nouveau code de la fonction notifierObservateur
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
PROCEDURE notifierObservateurs()
 
lo_ObservateurTmp est un ac_AdresseObservateur dynamique
 
POUR li_i=1 _A_ :mlst_Observateur..Occurrence
      lo_ObservateurTmp=:mlst_Observateur[li_i]
lo_ObservateurTmp:Exe_Actualiser(:ms_adresse1,:ms_adresse2,:ms_adresse3,:ms_codePostal,:ms_ville,:ms_pays)
      
FIN

Imaginons que cette fonction soit utilisée dans différents endroits de notre programme : nous devons répercuter cette modification pour chaque appel. Nous avons une forte dépendance et de plus, nous savons que chaque modification entraîne des risques de régression. Pour la version « TIRER », l'observable reçoit en paramètre un pointeur, dans le cas de l'ajout du nouveau membre, nous n'effectuons pas de modification sur l'entête de la procédure Exe_Actualiser et donc aucun impact sur les appels. La seule modification qui est faite est dans le corps de la fonction de Exe_actualiser pour prendre en charge ce nouveau membre via sa propriété.

Dans cette dernière version, nous avons lié faiblement l'observable à ses observateurs. En effet, si l'observateur dispose d'un pointeur vers l'objet observable et que la classe observable évolue en ajoutant un autre état. L'observateur souhaitant se tenir informé de cet état aura juste à appeler l'accesseur correspondant.

IV. Remerciements

Merci à tous ceux qui m'ont aidé à la mise en œuvre de cette publication :

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 v. Formet. 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.