mardi 8 décembre 2009

Le mécanisme de réflexion – partie 1

En naviguant sur les forums de développement, il est assez fréquent de trouver une question relative à la réflexion, ou bien un intervenant qui propose d’utiliser la réflexion  comme solution à un problème. Les exemples ci-dessous sont donnés dans l’environnement .NET, mais la mécanique est très proche en Java.

Quelles sont les possibilités offertes par ce mécanisme ?

Le mécanisme de réflexion offre un très large spectre de possibilités : chargement dynamique d’assemblages, exploration dynamique des types d’un assemblage, création d’instances de classes, déclenchement de méthodes, compilation de code à la volée, etc.

Quels sont les problèmes liés à son utilisation ?

Le mécanisme de réflexion présente certes des avantages, mais il a aussi des inconvénients qu’il est important de maîtriser :

  • Les performances : le déclenchement d’une méthode par l’intermédiaire de la réflexion (instruction Invoke) est beaucoup plus lent que son appel direct. Il est cependant possible d’optimiser ce chargement en utilisant des Interfaces.
  • La conception : La réflexion offrant un moyen d’accéder à chaque membre de chaque classe de chaque assemblage, elle peut occulter la nécessité d’une conception rigoureuse et l’utilisation de Design Patterns. Par exemple, la réflexion permet de connaître le type d’une classe dérivée depuis du code situé dans sa classe mère (c’est un peu tordu, mais c’est possible). Ce genre d’utilisation viole le principe LSP (Liskov Substitution Principle) et suggère un design médiocre qui sera tôt ou tard difficile à maintenir.
  • La clarté du code : la lisibilité du code source est très nettement dégradée. Ce critère n’est pas seulement subjectif et peut à terme être source de bugs et de régressions lors de la maintenance du logiciel.
  • L’obfuscation : elle empêche le chargement dynamique des classes puisque leur nom a été modifié.

Quand doit-on utiliser la réflexion ?

Compte-tenu de ses inconvénients, l’utilisation de la réflexion devrait être réservée au seul usage suivant : la manipulation par un assemblage A d’autres assemblages qui n’étaient pas connus au moment de la compilation de A. Dans la pratique, on trouvera 3 grandes familles d’applications :
  • L’exploration d’assemblages à l’exécution : les outils ildasm.exe, fxcop.exe ou encore .NET Reflector utilisent massivement la réflexion pour explorer les assemblages. Visual Studio l’utilise également dans son explorateur d’objets ou lors de la complétion de code.
  • Le chargement dynamique d’assemblages (late binding) : un assemblage A peut utiliser la réflexion pour charger explicitement un autre assemblage B si B n’était pas connu lors de la compilation de A. Cela permet par exemple de construire des mécanismes de plugins.
  • Compilation de code à la volée : Dans certains cas, on souhaitera compiler et exécuter dynamiquement du code. Cela peut être utile pour évaluer des expressions mathématiques qui sont fournies à l’exécution. Le temps perdu  lors de la construction et la première compilation de l’assemblage est compensé par la vitesse des exécutions suivantes.

Un prochain article présentera une utilisation du chargement dynamique d’assemblages sans dégradation notable des performances.

Aucun commentaire:

Enregistrer un commentaire