Encapsulation de données en JavaScript : getters et setters

Encapsulation de données en JavaScript : getters et setters

Lors de la construction d’applications JavaScript plus importantes, on a vite besoin de la diviser en modules liés par des contrats clairs. Lorsqu’il s’agit de maintenance à long terme, il faut prévoir un moyen de conserver la compatibilité lors des changements d’interface. Pour ce faire, les langages orientés objet s’appuient sur l’encapsulation ou l’information cachant les détails d’implémentation aux utilisateurs d’un morceau de code, afin qu’il puisse changer sans impacter les clients.

Dans ce post, nous parlerons de l’encapsulation des données en JavaScript. Plus précisément, nous parlerons de l’encapsulation des propriétés derrière des getters et des setters.

Quelques concepts

JavaScript a été conçu en ayant comme base le principe de typage du canard:

“Quand je vois un oiseau qui marche comme un canard et nage comme un canard et fait coin-coin comme un canard, j’appelle cet oiseau un canard.”

En d’autres termes, à son cœur, JavaScript est un langage orienté objet, mais différemment des autres langages orientés objet tels que C# et Java, en JavaScript les classes sont des citoyens de seconde classe et les interfaces n’existent pas. Les objets n’ont pas de types, et donc, le code doit être écrit pour s’interfacer avec des objets qui ont certaines propriétés et méthodes. Les contrats entre modules sont basés sur un ensemble de méthodes et de propriétés supposées être présentes dans les objets échangés.

L’encapsulation est fondamentale dans la programmation orientée objet. Cela signifie qu’un objet donné doit être capable de cacher les détails de son fonctionnement interne, de sorte que les autres objets qui interagissent avec lui ne dépendent pas des détails de son implémentation, mais d’une interface de haut niveau convenue. Cela rend la vie des développeurs plus simple, à la fois du côté du fournisseur et du côté des utilisateurs, car les deux savent que les détails de la mise en œuvre peuvent changer sans casser les objets clients.

Pourquoi exposer les données sur une interface est une mauvaise idée

En JavaScript, un objet est simplement un dictionnaire de noms de propriétés mappées sur des valeurs, lorsque la valeur d’une propriété est une fonction, nous l’appelons une méthode. L’interface entre les méthodes est l’ensemble des propriétés que le code client s’attend à trouver sur un objet.

Si un objet expose uniquement des méthodes, il est facile de faire évoluer l’interface avec le temps. Une méthode peut vérifier ses paramètres et réagir en conséquence. Il est également possible de créer de nouvelles méthodes fournissant de nouvelles fonctionnalités. Et de soutenir les anciennes méthodes qui adaptent l’ancien comportement au nouveau. Avec les propriétés, il n’est pas possible de faire cela.

Dans le cas des propriétés, garder une interface stable n’est pas facile. Car, par défaut, le code client peut faire référence à des propriétés inexistantes d’un objet. L’écriture vers une propriété inexistante la créera. La lire retournera undefined. Le code client ne sera pas capable de détecter rapidement s’il utilise des propriétés dépréciées. Pire, l’erreur peut se propager à d’autres parties du code, rendant la détection du problème plus difficile.

L’API JavaScript standard possède des méthodes qui peuvent être utiles pour éviter de tels problèmes : il est possible de geler ou de sceller un objet, de sorte que les noms de propriétés non supportés ne puissent pas être utilisés.

Les Getters/Setters pour sauver la journée

Cacher les détails de l’implémentation des méthodes est facile.

Les Getters/Setters ont été officiellement introduits dans le langage dans ECMAScript 5.1 (ECMA-262). Ils sont actuellement pris en charge dans tous les principaux navigateurs de bureau et mobiles.

L’idée de base est qu’il ajoute une syntaxe pour définir des propriétés d’accès en tant que méthodes, au lieu de simples propriétés de données. Un getter est défini par le mot clé get suivi d’une fonction nommée après la propriété, ne prenant aucun argument et renvoyant la valeur de la propriété. Un setter est défini par le mot-clé set suivi d’une fonction nommée après la propriété prenant la nouvelle valeur de la propriété comme paramètre.

L’exemple suivant illustre un getter et un setter utilisés pour définir une propriété accesseur appelée prop:

var obj = { v: 0, get prop() { return this.v; }, set prop(newValue) { this.v = newValue; }};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Sortie:

042

On peut également créer une propriété en lecture seule en définissant un getter sans setter :

var obj = { get prop() { return -1; },};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Sortie:

-1-1

Une propriété peut alors être remplacée par un couple de méthodes qui, au cas où l’implémentation d’une classe change, peuvent y réagir et adapter le comportement de la classe. Dans le pire des cas, elle peut lever une exception pour indiquer à l’utilisateur que la propriété est dépréciée et ne doit plus être utilisée.

Lecture complémentaire

J’espère que vous avez appris quelque chose de nouveau sur la conception d’applications évolutives complexes en JavaScript dans ce post. Dans cette section, je vous donnerai quelques liens vers des liens contenant plus d’informations.

Articles d’introduction

Cette section apporte quelques courts articles présentant les concepts et fournissant une vue d’ensemble des API connexes :

  1. Getters et setters de propriété.
  2. JavaScript Getters and Setters

Articles plus avancés

Ces articles parlent de sujets connexes plus avancés comme les problèmes apportés par les getters et setters, et comment les résoudre.

  1. Pourquoi les getters/setters sont une mauvaise idée en JavaScript
  2. L’encapsulation des données en JavaScript

Documentation de référence

Enfin, pour vraiment maîtriser le sujet, les liens vers la documentation mozilla des APIs concernées :

  1. get et set permettent de définir des getters et setters. Au-delà de ce que nous avons expliqué ici. Il existe d’autres fonctionnalités intéressantes comme la prise en charge des noms de propriétés générés dynamiquement, et l’utilisation des getters et setters pour simuler la mise en place/le retrait de valeurs dans un tableau
  2. seal et freeze permettent de contrôler quelles propriétés d’un objet sont modifiables ou non et comment. Il peut être utilisé pour éviter que les clients utilisent des parties dépréciées de l’API d’un objet.
  3. defineProperty permet de définir des getters et setters et en même temps d’avoir plus de contrôle sur la façon dont ces propriétés sont vues par les clients et comment elles sont modifiables.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.