samedi 19 mars 2011

Javascript: où définir ses méthodes ?

Il y a deux écoles: ceux qui préfèrent définir les méthodes d'un objet directement dans le constructeur de l'objet et ceux qui utilise le prototype du constructeur. Les premiers choisissent la possibilité de définir des propriétés privées - comme dans un langage objet orienté classe, les seconds ne veulent pas renoncer aux possibilités d'héritage et de monkey-patching offerte par le prototype.

Dans cet article, je vais décrire une technique qui permet de bénéficier à la fois du prototypage et du controle de visibilité des méthodes. Mais commençons par l'examen des deux façons classiques de définir des méthodes.

Les deux écoles

Les méthodes privilégiées et privées

Lorsque les méthodes sont définies dans le constructeur on parle de méthodes privilégiées et privées selon que la méthode est accessible de l'extérieur ou pas.



Cette méthode permet donc de définir des niveaux de visibilité. Cependant, vous perdez la possibilité de redéfinir une méthode après coup sur toute les instances créées. C'est une possibilité que peu de langage offre, et certains choisissent de s'en passer - même si à l'origine javascript le permet via les prototypes.

Un autre inconvénient de cette façon de procéder est que vos méthodes sont "redéfinies" à chaque fois que vous créer une instance. Elles ne sont pas redéfinies dans le sens ou elles changent, mais l'interprétateur javascript lui les récréer belle et bien à chaque appel. Accepteriez vous que java recompile votre classe chaque fois que vous créé une instance ?

Le prototype

Il existe un mécanisme de lookup particulier au langage à prototype qui permet de définir les méthodes sur un autre objet que le "receveur" direct. Dans javascript il s'agit de la propriété prototype du constructeur. L'avantage est que vos méthodes ne sont définis qu'une fois (à moins que vous ne les changiez vous même). De plus, vous pouvez changer la méthode plus tard, et cette modification se reportera sur tout les objets déjà créés.

Certains pensent que la définition de méthodes par prototype ne permet pas de faire des méthodes privées. C'est faux, comme le montre l'exemple suivant :



La véritable difficulté n'est pas de construire des méthodes privée, mais d'avoir l'équivalent d'une propriété privée. Ne croyez pas que l'on puisse utiliser la technique précédente pour définir une propriété privée. Une telle variable serait partagée entre toutes les instances. (C'est d'ailleurs pourquoi nous appelons la méthode changeName via call)

La propriété inacessible


Vous ne connaitrez pas mon nom...

Une convention fréquente est de faire précéder les propriétés privées par deux underscore. Il s'agit d'une simple convention que les développeurs choisissent (ou pas) de respecter.



Le controlleur d'accès

Il y a une bonne idée dans l'approche précédente. Le défaut n'est pas que l'on puisse accéder à la propriété '__name', le problème est que l'on puisse accéder à la valeur de cette propriété. Après tout, dans un langage à classe, lorsque vous appelez une méthode privée, le compilateur ne vous dit pas que cette méthode n'existe pas, il vous dit qu'elle existe mais qu'elle est privée.

Ce qu'il nous faudrait c'est un mécanisme équivalent. Essayons de coder un objet utilitaire qui permettent de controller l'accès à des variables privées.



Comment ça marche ?

Le constructor précédant définit deux méthodes. La première (privatize) prend un objet cible et définit sur cet objet une méthode. Cette méthode retourne un conteneur si elle est appelée avec l'objet qui l'a défini en paramètre ou jette une exception. Il s'agit d'un contrôle d'accès. Notons que nous sommes obligés de donner un nom unique au contrôleur d'accès pour éviter les problèmes d'héritage.

La deuxième méthode (access) enferme la logique d'accès, et se contente de renvoyer le conteneur des propriétés privées. Autrement dit, qui peut appeler cette méthode, peut accéder aux propriétés privées !

Le fonctionnement de cet objet peut paraître compliqué, voici un exemple pratique



Le privatizer en action

Nous possédons désormais tous les éléments nécessaires pour utiliser le prototypage avec le controle de visibilité des méthodes et propriétés.

Un exemple In real Life

Dans cet exemple - afin de restez concis, nous n'introduisons pas de problématique d'héritage, mais vous pourriez très bien le faire.

Personne n'est parfait

Il existe tout de même un défaut à cette façon de faire: toute les méthodes qui utilisent une propriété ou une méthode privée ne pourront pas être détachées et appelées sur un autre objet.



Notez cependant que ce défaut existe également avec la méthode des fonctions "privilégiées" que nous avons décrit au début de cet article. De même, nous pourrions modifier l'implémentation du Privatizer afin de mieux gérer le problème: en retournant l'objet lui-même en cas d'accès illégal par exemple...

Le mot de la fin

Nous l'avons vu, il est possible de bénéficier du contrôle d'accès et du prototypage. L'approche présentée ici ne peut être considéré comme complète - mais elle ouvre des perspectives diverses du contrôle de visibilité des propriétés d'objet.

Nous pourrions enrichir notre objet Privatizer afin d'inclure les notions d'objet amis (comme en C++) ou de visibilité protected. Ce qu'il est important de comprendre c'est que le coeur du langage possède toutes les propriété pour définir des comportement propres au langage à classe, tout en conservant la souplesse des langages à prototype.

Aucun commentaire:

Enregistrer un commentaire