Programmation Orientée Aspect

Introduction et Définition

L'AOP est une technique de conception et de programmation qui vient en complément de l'approche 
Orientée Objet ou procédurale. Elle permet de factoriser (et donc de rendre plus cohérentes) certaines 
fonctionnalités, dont l'implémentation aurait nécessairement été répartie sur plusieurs classes et 
méthodes dans le monde objet ou sur plusieurs bibliothèques et fonctions dans le monde procédural.

L'AOP n'est pas une technique autonome de conception ou de programmation: sans code procédural 
ou objet, la notion d'Aspect perd tout son sens. Mais inversement, on pourrait dire que les 
programmations Orientée Objet ou procédurale ne sont pas complètes puisque inaptes à mettre en 
facteur ou à bien séparer certaines responsabilités des éléments logiciels.

Glossaire

• Code cible (ou socle ou encore code de base) : Ensemble de classes qui constituent une 
application ou une bibliothèque. Ces classes n'ont pas connaissance des aspects (il n'y a donc aucune dépendance), elles ne se "doutent" pas qu'elles vont constituer la cible d'un tissage.
• Aspect : Il s'agit du code que l'on souhaite tisser. Selon les tisseurs, un aspect peut être une simple 
classe (cf. AspectDNG) ou constituer un élément d'un langage spécifique (cf. AspectJ).
• Zone de greffe : Ce sont les "endroits" dans le code cible dans lesquels aura lieu le tissage. Par 
exemple: "au début du corps de la méthode hello()" est une zone de greffe.
• Tissage (ou greffe ou encore injection de code) : Processus qui consiste à ajouter (tisser, greffer,
injecter) le code des Aspects sur les zones de greffe du code
• Insertion (ou introduction ou injection) : Tissage spécial consistant à rajouter de nouvelles
méthodes ou de nouveaux attributs à une classe cible, ou encore à rendre disponibles de nouveaux
types (interfaces, classes, enums...) dans un projet cible (i.e. dans un module .NET ou une archive
Java).

• Langage de tissage : Langage qui permet d'exprimer sur quelles zones de greffe doivent être
tissés les aspects. Ce langage peut être intégré à celui des aspects (cf. AspectJ) ou être séparé (cf. AspectDNG qui utilise XPath comme langage de tissage).

Découverte du monde des Aspects

• Tisseur : Programme exécutable ou framework dynamique qui procède au tissage. Le tisseur peut être invoqué:
• statiquement avant la compilation du code cible (le tisseur travaille sur le code source),
• statiquement pendant la compilation du code cible (le tisseur intègre un compilateur du langage de programmation),
• statiquement après la compilation du code cible (le tisseur travaille sur le code précompilé: bytecode, CIL),
• dynamiquement avant exécution du code (au moment de la compilation Just-In-Time),
• dynamiquement pendant l'exécution du code (par la technique d'interception d'invocation de méthode, utilisée intensivement par les frameworks d'inversion de contrôle).

Puisque les notions de classe, de méthode, d'attribut... peuvent apparaître à la fois dans le contexte
du code cible et des aspects, nous lèverons l'ambiguïté en parlant par exemple de méthode cible ou d'attribut d'Aspect.

Pour reformuler ce que nous avons fait dans la section précédente de découverte du principe 
des Aspects, disons que nous avons tissé la méthode d'aspect AspectTrace.MethodeATisser() au 
début de toutes les méthodes de la classe cible Produit.

Tisseurs d'Aspects

Un tisseur d'Aspect (ou Aspect Weaver en anglais) est donc l'outil responsable de la greffe des 
Aspects sur le code cible. Il existe aujourd'hui de nombreux projets de développement de tisseurs qui 
varient par leur approche, par le(s) langage(s) qu'ils visent ou la plate-forme sur laquelle ils s'exécutent.
Le tisseur est-il mono- ou multi-langage cible?

Cette question est particulièrement importante dans le monde .NET où le multi-langage est
chose courante (C#, VB.NET, Managed C++, Eiffel Envison, J#...). Dès lors, choisir un tisseur
mono-langage (par exemple: C#) est très contraignant car cela obligerait tous les développeurs
de composants métier comme de frameworks techniques à faire le choix du même langage de
programmation. Vous me direz, peu d'entreprises ont choisi plusieurs langages .NET... A supposer
que cela soit vrai, n'oublions pas toutes les bibliothèques de classes ou de composants réutilisables qui
peuvent être développées par des prestataires de service ou des éditeurs de logiciel: ceux-ci risquent
de ne pas avoir fait le même choix de langage. Dans le monde Java, bien sûr, ce critère n'existe pas.

Le tissage est-il statique ou dynamique?

On dit qu'un tisseur est statique lorsqu'il greffe réellement du code avant ou après la compilation.
Avant la compilation, nous avons affaire à un tisseur qui modifie directement le code des classes
cibles; après la compilation, le tisseur doit faire son travail sur le bytecode Java ou le CIL .NET.
Les tisseurs dynamiques quant à eux peuvent s'exécuter au chargement des classes en mémoire lors
de la compilation Just-In-Time ou à l'exécution en interceptant les invocations de méthodes.
Les avis des experts sont partagés sur ce sujet. D'aucuns préféreront la robustesse et la performance
des tisseurs statiques, d'autres la souplesse des tisseurs dynamiques. En réalité, tout dépend de votre
besoin: si vous souhaitez pouvoir tisser un aspect à la volée, en cours d'exécution de vos applications,
seul un tisseur dynamique pourra vous satisfaire. Si vos applications sont déjà un peu juste en termes
de performances, ou si vous avez besoin de "tisser très fin" (avant ou après l'accès aux attributs, qu'il
existe ou non des accesseurs pour ces derniers), alors il vous faudra vous orienter vers les tisseurs
statiques. Nous reviendrons plus en détail sur ce point après avoir pratiqué quelques tissages concrets.


Quelle est la puissance d'expression du langage de tissage?
Le langage de tissage est un bon point de départ pour découvrir les fonctionnalités implémentées
par un tisseur.

Un tisseur "sérieux" doit au minimum être capable de greffer du code:

• au début du corps des méthodes,
• avant l'invocation d'une méthode,
• avant qu'une méthode ne renvoie la main à son appelant,
• autour d'une méthode (éventuellement en escamotant la méthode originale),
• avant ou après l'accès à un attribut.


Outre les zones sur lesquelles une greffe peut avoir lieu, il est important que le langage de tissage
soit suffisamment puissant pour éviter les redondances. Par exemple, il faudrait pouvoir exprimer que
l'on souhaite tisser du code "sur tous les débuts de méthodes de toutes les classes d'un package"
ou "autour de tous les getters et setters d'une classe et de ses filles".
En effet, le langage de tissage fait le lien ou la jointure entre le code de base et celui des Aspects
et doit donc permettre le plus de tissages possibles. Voire même certains dont nous ne connaissons
pas encore l'usage.


Dans quel langage doivent être développés les Aspects?
Pour le développeur d'un tisseur, il est tentant de définir un nouveau langage doté de nouveaux
mots-clé plus adaptés aux concepts de l'AOP que ne le sont les classes, attributs, méthodes et autres outils déjà disponibles dans les langages objet. De plus, cela permettra certainement aux utilisateurs
du tisseur de ne pas faire l'amalgame entre classes de base et Aspects.

Mais la décision de créer un langage propriétaire, spécifique à un tisseur, est à double tranchant.

En contre partie, les utilisateurs devront encore apprendre un nouveau langage et disposer d'outils complémentaires pour développer et maintenir leurs Aspects. Plus grave encore, leurs Aspects seront complètement dépendants du tisseur en question. Il sera dès lors difficile voire coûteux (en adaptations à apporter au code des Aspects) de réutiliser ces Aspects sur d'autres projets qui feraient le choix d'un autre tisseur.

Dans la première partie de ce livre, nous avons passé en revue les principes de base de la conception
Objet, nous avons insisté sur les risques d'un trop fort couplage et sur l'importance de respecter
l'ouvert-fermé et l'inversion des dépendances. Si nous voulons être cohérents, il nous faut donc
condamner les tisseurs d'Aspects qui ne respectent pas ces principes et qui rendent leurs utilisateurs
captifs.
Plus généralement, existe-t-il une adhérence quelconque entre les Aspects et le tisseur utilisé?
Sans revenir sur le langage de développement des Aspects, il faut se demander si nos Aspects
dépendent d'une interface de programmation spécifique au tisseur utilisé. Typiquement, certains
tisseurs proposent de manipuler des objets techniques afin de pouvoir prendre une décision contextuelle dans le corps de l'aspect (un aspect peut ainsi se dire: "si j'ai été tissé dans telle méthode, alors je me comporte de telle manière, sinon autrement...").

S'ils offrent cette possibilité, c'est généralement à travers une API propriétaire. Mais si l'on souhaite
rendre nos aspects portables sur tous les tisseurs du marché, il faudrait soit ne pas utiliser ce type
de mécanisme, soit reposer sur une API commune à tous les tisseurs d'Aspects; or un consortium
oeuvre dans ce sens et semble recueillir les suffrages des principaux groupes de développements de
tisseurs du monde Java: l'AOP Alliance (aopalliance.sourceforge.net). L'avenir nous dira si son API
sera supportée par tous les tisseurs Java du marché; il faudra également qu'une API standard soit
supportée (celle-ci de l'AOP Alliance ou une API équivalente) par les tisseurs du monde .NET.

Sur quelle machine virtuelle fonctionne le tisseur?
Cette question prend tout son sens lorsque le tisseur fonctionne au chargement des classes car
cela nécessite d'être très proche de la machine virtuelle utilisée. Les tisseurs de ce type offrent en
général plusieurs implémentations de leur couche basse, de manière à être portables sur plusieurs environnements. Il faut donc vérifier que votre machine virtuelle cible en fasse partie.

Combien de temps prend le tissage?

Le processus de tissage peut intervenir à plusieurs moment, comme nous l'avons vu dans la définition d'un tisseur. Dans tous les cas, il vaudrait mieux que le temps d'exécution de la phase de tissage ne soit pas trop long.

Un tisseur statique, par exemple, sera intégré dans la phase de compilation (certains tisseurs
comme AspectJ prennent même la place de cette phase de compilation) et sera donc déclenché assez
souvent. Il ne faudrait pas que la productivité des développeurs soit limitée par un tissage trop long.
Difficile de chiffrer précisément le temps que devrait prendre un tissage (puisqu'il dépend du code de
base, du nombre d'aspects à tisser, du nombre de zones de greffe visées et bien entendu de la puissance
du matériel sur lequel s'effectue le tissage) mais un ordre de grandeur de quelques secondes reste
acceptable pour des projets de taille moyenne. En fait, il faudrait que le temps de compilation et de
tissage (notons-le TCT pour Temps de Compilation et de Tissage) soit du même ordre de grandeur
que le temps de compilation du même code dans lequel les instructions ou les classes tissées auraient
été développées à la main (notons-le TC pour Temps de Compilation). Si TCT = TC, le tisseur est
très efficace. Si TCT = 5 * TC, le tisseur est un peu lent. Et si TCT < 10 * TC, le tisseur est très lent,
voire trop lent selon de la taille de vos projets.

Un tisseur qui ne s'exécutera qu'au chargement des classes (généralement à la compilation à
la volée des classes en Java ou en .NET) doit lui aussi être le plus rapide possible, sinon le temps
de chargement de nos applications va s'allonger. Ceci peut s'avérer gênant, surtout pour les classes
chargées à la demande, en cours d'exécution du programme: des contraintes fortes sur le temps de
réponse du système pourraient ne plus être respectées. Il n'est pas rare de voir le temps de chargement
des classes à tisser s'allonger d'un facteur 5.

Enfin, les tisseurs dynamiques ont un temps d'exécution qui est probablement encore plus critique
que les précédents puisqu'ils vont être sollicités lors d'un certain nombre d'invocations de méthodes

(le nombre varie avec la stratégie de tissage). Selon leur implémentation, il se peut que le surcoût
induit à l'exécution ne soit pas le même lors de la première invocation d'une méthode tissée que lors
des invocations ultérieures. Mais quoi qu'il en soit, il faut que vous mesuriez le surcoût "moyen"
d'invocation des méthodes si vous utilisez de tels tisseurs et que vous voyiez si les performances
globales restent compatibles avec vos contraintes d'exécution. Si vos aspects portent sur peu de
méthodes cibles, ou si les performances globales avant tissage de vos applications sont bien en-deçà
de leurs maxima imposés, alors un tisseur dynamique peut parfaitement faire l'affaire. Dans le cas
contraire, évaluez les tisseurs statiques (qu'ils fonctionnent au cours de la phase de développement ou
au chargement des classes): le code de l'application tissée sera généralement bien plus performant, ce
qui est normal, mais vérifiez également que vos autres critères d'évaluation restent respectés (couplage

faible, dynamisme suffisant...).

Quelle est le niveau d'intégration du tisseur dans un environnement de développement?

Certains tisseurs sont dotés de plug-in adaptés aux principaux environnements de développement.
L'intégration permet non seulement de simplifier le démarrage de projets contenant des Aspects, mais
dans certains cas elle va jusqu'à nous montrer en temps réel sur quelles zones sont greffés les Aspects
du projet! 

Commentaires

Posts les plus consultés de ce blog

Sécurité des Applications

Principes de la Programmation Orientée Objet

Principe de Responsabilité Unique