Migration d'objets métier de Java vers JavaScript
Par Marc Autran

Le , par autran, Rédacteur
Lors de précédents billets, j'ai évoqué la migration d'application JEE vers des architectures JavaScript. Les migrations que j'ai expérimentées m'ont permis de porter des applications JEE vers AngularJS pour le front office et Node.js pour le back-office. Comme dans les blogs précédents, je rappelle que ces migrations sont envisageables pour des applications légères.
Dans ce billet, j'aborderai la migration des objets métier. En JEE ces objets métier sont des POJO (Plain Old Java Objects) c'est-à-dire des classes. Aussi ces objets pourront devenir en JavaScript des POJO (Plain Old Javascript Objects) c'est-à-dire encore des classes.
Pour être autorisé à parler de classes en JavaScript, il faut bien entendu implémenter le standard ES6.
Regardons de plus près comment on pourrait migrer un objet Owner qui agrégerait des objets cellules.
Voici une implémentation en Java :
Code java : Sélectionner tout
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
public class Cellule { 
  
	// attributs 
	private String name = ""; 
	private Owner owner = null; 
  
	// constructeur 
	public Cellule(String name){ 
		this.setName(name); 
	} 
  
	// méthodes --> setter et getter 
	public String getName() { 
		return name; 
	} 
  
	public void setName(String name) { 
		this.name = name; 
	} 
  
	public Owner getOwner() { 
		return owner; 
	} 
  
	public void setOwner(Owner owner) { 
		this.owner = owner; 
	} 
} 
  
public class Owner  
{ 
	// attributs 
	private ArrayList<Cellule> cellules; 
  
	// constructeur 
	public Owner(){ 
		this.cellules = new ArrayList<Cellule>(); 
	} 
  
	// méthodes 
	public int getSize(){ 
		return this.cellules.size(); 
	} 
  
	public int addElement(Cellule e){ 
		if(e.getOwner() != this)  
			e.setOwner(this); 
		this.cellules.add(e); 
		return this.cellules.indexOf(e); 
	} 
  
	public Cellule getElementAtIndex(int index){ 
		return this.cellules.get(index); 
	} 
}

Et maintenant l’implémentation en JavaScript :
Code javascript : Sélectionner tout
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
class Cellule {  
    constructor(name){  
		// attributs 
		this.name = name; 
		this.owner = null; 
    }  
} 
  
class Owner{ 
	constructor(){  
		// attributs 
		this.cellules = []; 
    } 
  
	// methodes 
	getSize(){ 
		return this.cellules.length; 
	} 
  
	addElement(cellule){ 
		if(cellule.owner != this)  
			cellule.owner = this; 
		return this.cellules.push(cellule); 
	} 
  
	getElementAtIndex(index){ 
		return this.cellules[index]; 
	} 
}

On remarque que les règles de passage de Java à JavaScript semblent assez simples si on accepte quelques petites entorses aux règles strictes de l'objet et notamment l'encapsulation grâce aux mentions de protection. En effet, en Java les propriétés sont privateset donc seulement accessibles au travers de getter et setter. Tandis qu'en JavaScript, tout est public, mais cela permet de faire l'économie d'écriture des getter et setter.
Cependant, si l'on n'implémente pas l'héritage dans l'organisation des objets métier, on peut sans problème s'abstraire de ces mentions de protection. Et on remarque que l'on n'utilise que rarement l'héritage dans les tiers métier.

Donc, il semble que la migration des objets métier de Java à JavaScript soit assez simple.


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster un commentaire

Avatar de danielhagnoul danielhagnoul - Rédacteur https://www.developpez.com
le 21/03/2016 à 11:33
Accesseur et mutateur (get/set) existent depuis ES5 et sont utilisables dans une classe ES2015. Il est tout à fait possible d'avoir des propriétés "privées" (inaccessible à l'extérieur de la classe) et "protégées" (accessible par get/set). Comme le module ES2015 n'est toujours pas disponible en natif, il faut recourir à une clôture (closure) pour protéger les propriétés et méthodes privées.

Voici un exemple fonctionnel en ES2015 natif (Chrome 49+ ou Firefox 46+) sur un sujet que tu connais bien. Il contient les classes Panier, LignePanier et Article. Attention ce n'est pas "utilisable en l'état", c'est juste l'ébauche d'une solution, mais c'est suffisant pour illustrer le propos.

Références


Code html : Sélectionner tout
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<!DOCTYPE html> 
<html lang="fr" dir="ltr"> 
<head> 
  <meta http-equiv="cache-control" content="public, max-age=60"> 
  <meta charset="utf-8"> 
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <meta name="author" content="Daniel Hagnoul"> 
  <title>Test</title> 
  <style> 
  
  
  </style> 
  <script> 
    'use strict'; 
     
    document.addEventListener( 'DOMContentLoaded', function( ev ){ 
       
      const 
        kGetType = function( Obj ){ 
          return Object.prototype.toString.call( Obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase(); 
        }, 
        Article = ( function(){ 
          const 
            kCode = Symbol( 'Code' ), 
            kCodeType = 'number', 
            kSetCode = function( obj, value ){ 
              if ( kGetType( value ) === kCodeType ){ 
                obj[ kCode ] = value; 
              } else { 
                throw `Type des paramètres incompatibles. 
                        code : ${ kCodeType } attendu`; 
              } 
              return obj; 
            }, 
            kNom = Symbol( 'Nom' ), 
            kNomType = 'string', 
            kSetNom = function( obj, value ){ 
              if ( kGetType( value ) === kNomType ){ 
                obj[ kNom ] = value; 
              } else { 
                throw `Type des paramètres incompatibles. 
                        nom : ${ kNomType } attendu`; 
              } 
              return obj; 
            }, 
            kPrix = Symbol( 'Prix' ), 
            kPrixType = 'number', 
            kSetPrix = function( obj, value ){ 
                if ( kGetType( value ) === kPrixType ){ 
                  obj[ kPrix ] = value; 
                } else { 
                  throw `Type des paramètres incompatibles. 
                          prix : ${ kPrixType } attendu`; 
                } 
                return obj; 
            }; 
             
          return class cArticle { 
            constructor( code, nom, prix ){ 
              kSetCode( this, code ); 
              kSetNom( this, nom ); 
              kSetPrix( this, prix ); 
            } 
            get code(){ 
              return this[ kCode ]; 
            } 
            get nom(){ 
              return this[ kNom ]; 
            } 
            get prix(){ 
              return this[ kPrix ]; 
            } 
            set prix( value ){ 
              kSetPrix( this, value ); 
            } 
          } 
        })(), 
        LignePanier = ( function(){ 
          const 
            kArticle = new WeakMap(), 
            kArticleType = 'object', 
            kSetArticle = function( obj, value ){ 
              if ( kGetType( value ) === kArticleType ){ 
                kArticle.set( obj, value ); 
              } else { 
                throw `Type des paramètres incompatibles. 
                        article : ${ kArticleType } attendu`; 
              } 
              return obj; 
            }, 
            kNombre = Symbol( 'Nombre' ), 
            kNombreType = 'number', 
            kSetNombre = function( obj, value ){ 
              if ( kGetType( value ) === kNombreType ){ 
                obj[ kNombre ] = value; 
                kSetPrix( obj ); 
              } else { 
                throw `Type des paramètres incompatibles. 
                        nom : ${ kNombreType } attendu`; 
              } 
              return obj; 
            }, 
            kPrix = Symbol( 'Prix' ), 
            kPrixType = 'number', 
            kSetPrix = function( obj ){ 
              let value = kArticle.get( obj ).prix * obj[ kNombre ]; 
               
              if ( kGetType( value ) === kPrixType ){ 
                obj[ kPrix ] = value; 
              } else { 
                throw `Type des paramètres incompatibles. 
                        prix : ${ kPrixType } attendu`; 
              } 
              return obj; 
            }; 
             
          return class cLignePanier { 
            constructor( article, nombre ){ 
              kSetArticle( this, article ); 
              kSetNombre( this, nombre ); 
            } 
            get nombre(){ 
              return this[ kNombre ]; 
            } 
            set nombre( value ){ 
              kSetNombre( this, value ); 
            } 
            get prix(){ 
              return this[ kPrix ]; 
            } 
          } 
        })(), 
        Panier = ( function(){ 
          const 
            kLignes = new WeakMap(), 
            kLignesType = 'object', 
            kSetLignes = function( obj, value ){ 
              if ( kGetType( value ) === kLignesType ){ 
                kLignes.get( obj ).push( value ); 
                kSetPrix( obj ); 
              } else { 
                throw `Type des paramètres incompatibles. 
                        lignes : ${ kLignesType } attendu`; 
              } 
              return obj; 
            }, 
            kSpliceLignes = function( obj, value ){ 
              if ( kGetType( value ) === kLignesType ){ 
                let index = kLignes.get( obj ).findIndex( function( item, i ){ 
                  if ( item === value ) { 
                    return true; 
                  } 
                }); 
                 
                kLignes.get( obj ).splice( index, 1 ); 
                kSetPrix( obj ); 
              } else { 
                throw `Type des paramètres incompatibles. 
                        lignes : ${ kLignesType } attendu`; 
              } 
              return obj; 
            }, 
            kPrix = Symbol( 'Prix' ), 
            kPrixType = 'number', 
            kSetPrix = function( obj ){ 
              let value = kLignes.get( obj ).reduce( function( value, item, i ){ 
                return value + item.prix; 
              }, 0 ); 
               
              if ( kGetType( value ) === kPrixType ){ 
                obj[ kPrix ] = value; 
              } else { 
                throw `Type des paramètres incompatibles. 
                        prix : ${ kPrixType } attendu`; 
              } 
              return obj; 
            }; 
             
          return class cPanier { 
            constructor( ligne ){ 
              kLignes.set( this, [] ); 
              kSetLignes( this, ligne ); 
            } 
            get prix(){ 
              return this[ kPrix ]; 
            } 
            addLigne( ligne ){ 
              kSetLignes( this, ligne ); 
            } 
            removeLigne( ligne ){ 
              kSpliceLignes( this, ligne ); 
            } 
          } 
        })(); 
         
      let 
        articleMontreRolex = new Article( 235689, 'Montre Rolex', 2357 ), 
        articleFerrariE = new Article( 784512, 'Ferrari E', 896234 ), 
        articlePainLuxe = new Article( 134679, 'Pain de luxe', 17 ), 
        ligneRolex = new LignePanier( articleMontreRolex, 2 ), 
        ligneFerrari = new LignePanier( articleFerrariE, 1 ), 
        lignePain = new LignePanier( articlePainLuxe, 3 ), 
        panierDVJH = new Panier( ligneRolex ); 
       
      console.log( articleMontreRolex ); 
      console.log( articleFerrariE ); 
      console.log( articlePainLuxe ); 
       
      console.log( ligneRolex ); 
      console.log( ligneFerrari ); 
      console.log( lignePain ); 
       
      panierDVJH.addLigne( ligneFerrari ); 
      panierDVJH.addLigne( lignePain ); 
       
      panierDVJH.removeLigne( ligneFerrari ); 
       
      console.log( panierDVJH ); 
         
    }); 
  </script> 
</head> 
<body> 
  
  
</body> 
</html>
Avatar de autran autran - Rédacteur https://www.developpez.com
le 21/03/2016 à 18:04
Excellent Daniel

Mais du coup le mapping Java &lt;--> JavaScript est beaucoup moins net.
Or dans mon projet, je dois prendre des racourcis pédagigiques pour raccrocher les Javaistes au JS.

Ta connaissance académique du langage nous serait bien utile sur le projet de tuto que l'on mène actuellement. Alors si tu veux faire partie de la team, n’hésite pas.
Avatar de renaudpawlak renaudpawlak - Membre à l'essai https://www.developpez.com
le 22/03/2016 à 20:31
Hello,

Pour info, JSweet (http://www.jsweet.org) permet d'automatiser ce genre de transformations.

Actuellement il ne supporte pas les collections Java (il faut utiliser des tableaux), mais c'est en cours de développement...

/Renaud
Avatar de autran autran - Rédacteur https://www.developpez.com
le 24/03/2016 à 17:44
Tu penses qu'aimer le français implique de détester l'anglais.
Nous ne sommes pas dans une logique d'opposition.

Bien au contraire, dans le monde du développement, nommer des variables en utilisant la langue internationale permet d'ouvrir ton code au monde.
Sinon on pensera que tu ne souhaites réserver ton code qu'à ceux que tu en estimes digne, c'est à dire les français

D'ailleurs, je ne pense pas une seule seconde que les SSII françaises de l'open source puissent pousser leurs développeurs vers une francisation de leur code source.
Avatar de Mouke Mouke - Membre averti https://www.developpez.com
le 29/03/2016 à 12:48
Citation Envoyé par devwebsympa Voir le message
Bonjour, code en Anglais, je ne regarde pas. En Francophonie, on utilise des noms de variables Françaises. Je ne suis pas Anglais.
Logique ridicule, réductrice et néfaste.
Responsable bénévole de la rubrique JavaScript : Xavier Lecomte -