Foutse Khomh © Guéhéneuc, 2009; Arnaoudova, 2010; Khomh, 2010 Département de génie informatique et de génie logiciel École Polytechnique de Montréal LOG4430 : Architecture logicielle et conception avancée Applications de base de la conception orientée aspect : surveillance, traçage et profilage
2/82 Applications de base de la conception orientée aspect 1. Contexte 2. Introduction aux aspects 3. Conception par aspects 4. Avantages et inconvénients
3/82 1. Contexte Pensez à la source de tous les maux en génie logiciel ?
4/82 Following slides courtesy of Gregor Kiczales, original available at [
5/82 Vous pensez intuitivement a des objets ? –Points, lignes… –Surfaces de dessin (Drawing) –GUI Widgets –… Display 2 Point getX() getY() setX(int) setY(int) moveBy(int, int) Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) Shape moveBy(int, int) * objects are intuitive
6/82 En 1969, la plupart des programmeurs auraient utilises la difficile conception et implantation suivante ! collection of procedures to operate on and manage table entries objects are not intuitive objects are intuitive
7/82 La programmation par objets –Inventée en 1961 –A peu près au même moment que la programmation structurée –Devient « par objets » en 1967 –Rend le code de simulation plus proche du modèle original OOP intuitive not intuitive
8/82 Aparté
9/82 Aparté Ole-Johan Dahl –12 octobre 1931 – 29 juin 2002 –Norvégien –Père de Simula et de la PPO –Récipiendaire ACM A.M. Turing Award IEEE John von Neumann Medal –Développe lidée de la PPO dans les années 1950 au Centre de calculs norvégien (Norsk Regnesentral) –
10/82 Aparté Kristen Nygaard –27 aout 1926 – 19 aout 2002 –Norvégien –Père de Simula et de la PPO –Récipiendaire ACM A.M. Turing Award IEEE John von Neumann Medal –Développe lidée de la PPO dans les années 1950 au Centre de calculs norvégien (Norsk Regnesentral) –
11/82 Quest-ce la programmation par objets ? –Une façon de penser Objets, classes, hiérarchies –Des mécanismes de soutient de cette pensée Classes, interfaces, encapsulation, polymorphisme –Une façon de Rendre le code plus proche de sa conception Améliorer la modularité de la conception et du code –Avec de nombreuses implantations Styles, bibliothèques, extension ad-hoc… OOP intuitive
12/82 Bonne modularité de la conception mais faible modularité de limplantation class Point extends Shape { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; display.update(this); } void setY(int y) { this.y = y; display.update(this); } 1 Display 2 Point getX() getY() setX(int) setY(int) moveBy(int, int) Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) Shape moveBy(int, int) * MVCObserver Pattern OOP
13/82 Pendant ce temps la… –Début des années 80 (peut-être même plus tôt) –Dautres travaillaient Structure « entrecoupantes » Mécanismes –Réflexion comportementale –MOP –Programmation orientée sujets –Le terme « programmation par aspects » apparaît en MVCObserver Pattern AOP OOP
14/82 aspect ObserverPattern { private Display Shape.display; pointcut change(): call(void figures.Point.setX(int)) || call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Shape.moveBy(int, int)); after(Shape s) returning: change() && target(s) { s.display.refresh(); } } ObserverPattern 1 Display 2 Point getX() getY() setX(int) setY(int) moveBy(int, int) Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) Shape moveBy(int, int) * OOP AOP
15/82 aspect ObserverPattern { private Display Shape.display; pointcut change(): call(void figures.Point.setX(int)) || call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Shape.moveBy(int, int)); after(Shape s) returning: change() && target(s) { s.display.refresh(); } } ObserverPattern 1 Display 2 Point getX() getY() setX(int) setY(int) moveBy(int, int) Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) Shape moveBy(int, int) * OOP AOP
16/82 Maintenant, est-ce que vous pourriez appeler une classe « ObserverPattern » ? OOP AOP
17/82 Quest-ce la programmation par aspects ? –Une façon de penser Aspects, structures entrecoupantes –Des mécanismes de soutient de cette pensée Points de jointure, points de coupe, « advice » –Une façon de Rendre le code encore plus proche de sa conception Améliorer la modularité de la conception et du code –Avec de nombreuses implantations Styles, bibliothèques, extension ad-hoc… OOP AOP
18/82 1. Contexte Autres aspects ? –Patron de conception –Sureté des fils dexécution Swing –Application de politiques Authentification, synchronisation… –Gestion des transactions –Débogage –Logging –…
19/82 1. Contexte IBM rapporte –Implémentation de politiques 15 à 30% damélioration de la qualité Gains en productivités significatifs –Popularisation de logiciels complexes De nouvelles opportunités daffaires
20/82 2.Introduction aux aspects Un premier exemple public class ClasseDuModel { … attributs pour la logique de la classe … attributs pour lauthentification, … vérification des contrats et profilage public void uneMethode { // authentification // vérifier les pré conditions // enregistrer lentrée dans lopération … accomplir les opérations // authentification // / vérifier les post conditions // enregistrer la sortie de lopération } // autres méthodes } Dans cet exemple, authentification, vérification des contrats et profilage sont croisés avec la méthode qui accomplie des fonctions reliées à la logique de la classe
21/82 A noter… Deux problèmes sont à noter ici: 1. Limplémentation dauthentification, vérification des contrats et profilage nest pas localisée –Le code source est dispersé probablement dans beaucoup dautres méthodes, classes, paquetages 2. Limplémentation de uneMethode() fait beaucoup plus que ce quelle devrait –Elle contient du code source concernant plusieurs préoccupations
22/82 Symptômes de lintrication (entrecroisement) Lentrecroisement de concepts est relié à deux symptômes dans le développement logiciel: –Dispersion de code source (code scattering): implémentation de concepts qui ne sont pas bien modulaires et qui sont dispersés dans le système. –Croisement de code source (code tangling): un module peut contenir des parties de code concernant plusieurs concepts. Dispersion et croisement sont deux aspects différents du même problème.
23/82 Doù vient lintrication? Il nexiste pas nécessairement une correspondance un-à-un du domaine du problème au domaine de la solution Lespace des requis est multidimensionnel alors que lespace de limplémentation est unidimensionnel (en programmation orientée objets chaque classe doit avoir une seule responsabilité) R1R1 R2R2 R3R3 R4R4 C1C1 C2C2 C3C3 dispersioncroisement Implementation Requis C4C4
24/82 Entrecroisement des aspects technique - nous aimerions que le diagramme se transforme… Composant
25/82 …en celui-ci! Composant Aspects Nous avons besoin dun nouveau module pour pouvoir résoudre le problème dentrecroisement.
26/82 Principes de la POA AOP can be understood as the desire to make quantified statements about the behavior of programs, and to have these quantifications hold over programs written by oblivious programmers. » Robert E. Filman and Daniel P. Friedman Deux concepts –Quantification –Obliviousness
27/82 Principes de la POA: 1. « Quantification » Dans programme P, quand condition C est évaluée à vrai, exécute action A. C1C2C3 quand exécution atteint ce point… …ou ce point… …exécute ce code!
28/82 Les points dans C2 et C3 ne sont pas choisis au hasard; ils doivent être bien définis Un point dinsertion ou de jonction (joinpoint) est un point dans le programme bien défini. –Exemple: appel/exécution dune méthode. C1C2C3 A
29/82 Composant, aspects et points dinsertion Les points dinsertion sont les endroits où les aspects interagissent avec le reste du système. Source: [Bardou, 98]
30/82 Principes de la POA: 2. « Obliviousness » A noter que ni C2 ni C3 font des appels de A ! Même si les composants C2 et C3 ont été enrichis avec le comportement fourni par laspect A, ils ne sont pas au courant quun tel enrichissement existe – ils nont subit aucune modification pour sadapter à cet enrichissement. Différence avec les appels de méthodes usuels. C1C2C3 A
31/82 Implémenter un programme orienté aspects Deux étapes: 1. Décomposition: Identifier et implémenter les fonctionnalités de base (classes) et les concepts entrecroisés (aspects) –Exemples daspects: authentification, vérification de contrats, profilage, sauvegarde de données. 2. Définir les règles dinteraction entre les fonctionnalités de base et les concepts entrecroisés
32/82 Finalement… public class BusinessLogic { … data members for business logic public void someOperation { … perform core operation } // more operations similar to the above } public aspect Authenticator { … data members for authentication public void authenticate() {…} } public aspect Logger { … data members for logging public void log() {…} } public aspect ContractChecker { … data members for contract checking public boolean precondition() {…} public boolean postcondition(){..} } Compilateur
33/82 Plus précisément… Tissage daspects –À la compilation Comparer avec une compilation « traditionnelle »
34/82 Avantages de la POA En comparaison avec la programmation orientée objets: –Une séparation nette des préoccupations – espace bidimensionnel du domaine de limplémentation Code source moins dispersé et moins croisé –Meilleure modularité: il est plus facile de analyser, déboguer, changer et réutiliser les modules –Une maintenance plus facile La POA ne se limite pas à la POO. AspectL Aspect-C
35/82 AspectJ – POA pour Java AspectJ est une extension à Java qui fournit les moyens pour faire de la POA AspectJ est un sur-ensemble de Java. –Chaque programme valide de Java est aussi un programme valide dAspectJ
36/82 public class Buffer { private String[] BUFFER; int putPtr; // keeps track of puts int getPtr; // keeps track of gets int counter; // holds number of items int capacity; Buffer (int capacity) {…} public boolean isEmpty() {…} public boolean isFull() {…} public void put (String s) {…} public String get() {…} } Exemple: Mémoire tampon limitée La classe Buffer contient deux types de méthodes: –Ceux qui modifient son état: put(), get() –Ceux qui le consultent seulement: isFull(), isEmpty()
37/82 Comportement de la classe Buffer public class Buffer { … public void put (String s) { if (isFull()) System.out.println("ERROR: Buffer full"); else { BUFFER[putPtr++] = s; counter++; } public String get() { if (isEmpty()) return "ERROR: Buffer empty"; else { counter--; return BUFFER[getPtr++]; }
38/82 AspectJ language concepts Point de jonction (joinpoint): un point bien défini dans lexécution dun programme –Exemple: appel de la méthode get() de la classe Buffer Point daction (pointcut): Un ensemble de points de jonctions. –Exemple: lexécution de toutes les méthodes modifiant létat de la classe Buffer Greffon (advice): Un block qui spécifie le code à exécuter quand un point daction a été atteint –Exemple: avant lappel de la méthode get(), affiche un certain message
39/82 Exemple: Profilage But: afficher un message avant chaque appel de la méthode put() et de la méthode get() de la classe Buffer Comment: Définir un point de jonction. Le point de jonction suivant se réfère à une méthode publique dont le type de retour est vide, dont le nom est « put » et qui prend une chaîne de caractères en paramètre: call(public void Buffer.put(String)) Ce point de jonction représente le moment dexécution après lévaluations des paramètres mais avant que la méthode soit appelée
40/82 Identifier des points de jonction Le point de jonction suivant se réfère à chaque appel de la méthode « get » sans paramètres et dont le type de retour est une chaîne de caractères et dont la visibilité est « public » call (public String Buffer.get())
41/82 Définir un point daction Le point daction suivant définit un point daction dont le nom est « mutators » et qui sera activé si lun des points de jonction précédemment définis est satisfait pointcut mutators(): call(public void Buffer.put(String)) || call (public String Buffer.get());
42/82 Définir un greffon Le greffon définit le code à exécuter quand un point daction est activé. Un greffon est donc défini par rapport à un point daction Le type de greffon doit aussi être spécifié. Dans ce cas, le greffon sera exécuter avant ce qui est référé par le point daction before(): mutators() { System.out.println(" Mutator method called."); }
43/82 Il existe trois façons dassocier un greffon à un point daction: –before: sexécute juste avant le point daction –after: sexécute juste après le point daction –around: sexécute à la place du code qui est référé par le point daction. Donne la possibilité dexécuter le code défini par le point daction en plus en appelant proceed()
44/82 Définir un aspect Un aspect est un module Il définit ses points daction et ses greffons public aspect Tracer { pointcut mutators(): call(public void Buffer.put(String)) || call (public String Buffer.get()); before(): mutators() { System.out.println(" Mutator method called."); }
45/82 Profilage ? public class BufferDemo { public static void main(String[] args) { Buffer buffer = new Buffer(10); buffer.put("Hello"); buffer.put("there"); System.out.println(buffer.get()); } public aspect Tracer { pointcut mutators(): call(public void Buffer.put(String)) || call (public String Buffer.get()); before(): mutators() { System.out.println(" Mutator method called."); } Quelle serait la sortie du programme ???
46/82 Profilage public class BufferDemo { public static void main(String[] args) { Buffer buffer = new Buffer(10); buffer.put("Hello"); buffer.put("there"); System.out.println(buffer.get()); } public aspect Tracer { pointcut mutators(): call(public void Buffer.put(String)) || call (public String Buffer.get()); before(): mutators() { System.out.println(" Mutator method called."); } Mutator method called. Hello Mutator method called. there
47/82 Types de points de jonction 1. Appels de méthodes et constructeurs 2. Exécution de méthodes et constructeurs 3. Accès à un attribut 4. Gestion des exceptions 5. Initialisation dune classe 6. Structure lexicale 7. Flux de control 8. Objets visés et arguments 9. Tests de conditions
48/82 Appels de méthodes et constructeurs Appel de toute méthode dont le nom commence avec myMethod de MyClass. call (* MyClass.myMethod*(..)) Appel à myMethod() de MyClass prenant tout type darguments, retournant nimporte quel type. call (* MyClass.myMethod(..)) Appel à myMethod() de MyClass prenant tout type darguments, dont le type de retour est void, peu importe la visibilité call (void MyClass.myMethod(..)) Appel à myMethod() de MyClass prenant un argument de type String, retournant void, avec visibilité public call (public void MyClass.myMethod(String))
49/82 Appel au constructeur de MyClass avec nimporte quels types darguments call (MyClass.new(..)) Appel au constructeur de MyClass qui ne prend pas darguments call (MyClass.new()) Appel à myMethod() de nimporte quelle classe dans le paquetage default call (* *.myMethod(..)) Appel à toute méthode dont le nom commence avec myMethod de MyClass et dont le premier argument est de type String call (* MyClass.myMethod* (String,..))
50/82 Appel à toute méthode public dans chaque classe de nimporte quel paquetage dont le paquetage racine est com.company call (public * com.mycompany..(.*(..))) Appel au constructeur de MyClass ou un de ses enfants, dont les types darguments ne sont pas importants call (MyClass+.new(..))
51/82 Exécution de méthodes et constructeurs Exécution de toute méthode dont le nom commence avec myMethod de MyClass. execution(* MyClass.myMethod*(..)) Exécution de myMethod de MyClass prenant nimporte quels types dargu- ments et peu importe le type de retour execution(* MyClass.myMethod(..)) Exécution de myMethod() de MyClass prenant nimporte quels types darguments, retournant void execution(void MyClass.myMethod(..)) Exécution de myMethod() de MyClass prenant un argument de type String, retournant void, et avec visibilité public execution (public void MyClass.myMethod(String)) Exécution de toute méthode dont le nom commence avec myMethod de MyClass et dont le premier argument est de type String execution(* MyClass.myMethod* (String,..))
52/82 Exécution de tout constructeur de MyClass ou une de ses sous classes execution(* MyClass+.new(..)) Exécution de tout constructeur de MyClass execution(MyClass.new(..)) Exécution du constructeur par défaut de MyClass. execution(MyClass.new()) Exécution de myMethod() dans toute classe dans le paquetage default execution (* *.myMethod(..)) Toute méthode publique de toute classe avec paquetage racine com.company execution(public * com.mycompany..*.*(..))
53/82 Accès à un attribut Exécution de lécriture dans lattribut x de type int de classe MyClass. set (int MyClass.x) Exécution de lecture de lattribut out de type PrintStream de classe System get(PrintStream System.out)
54/82 Gestion des exceptions Exécution dun block catch dune exception dont le nom du type commence avec CreditCard. handler (CreditCard*) Exécution dun block catch dexception de type/ sous type de IOException handler (IOException) Exécution dun block catch dune exception de type RemoteException handler (RemoteException)
55/82 Initialisation dune classe Exécution dun block statique de MyClass ou une de ses sous classes. staticinitialization(MyClass+) Exécution dun block statique de MyClass staticinitialization(MyClass)
56/82 Structure lexicale Tout point de jonction dans la définition de toute méthode myMethod() de MyClass withincode(* MyClass.myMethod(..)) Tout point de jonction dans la définition de classes dont les noms commencent avec MyClass within(MyClass*) Tout point de jonction dans la définition de MyClass within(MyClass)
57/82 Flux de control Tout point de jonction dans le flux de control de lappel à la méthode myMethod() de MyClass excluant lappel à la méthode spécifiée cflowbelow(call (* MyClass.myMethod(..)) Tout point de jonction dans le flux de control de lappel à la méthode myMethod() de MyClass incluant lappel à la méthode spécifiée cflow(call (* MyClass.myMethod(..))
58/82 Objets visés et arguments Tout point de jonction où le type dargument ou lexception que lon veut gérer est RemoteException args(RemoteException) Tout point de jonction où le premier argument est de type String et le dernier est de type int. args(String, …, int) Tout pont de jonction où lobjet sur lequel la méthode est appelé est de type MyClass. target(MyClass) Tout point de jonction où this instanceof JComponent est vrai this(JComponent)
59/82 Tests de conditions Tout point de jonction où EventQueue.isDispatchedThread() est évalué à vrai if (EventQueue.isDispatchedThread())
60/82 Exemple: Vérification de contrats de la classe Buffer – nouvelle class BBuffer public class BBuffer { private String[] BUFFER; private int putPtr;// keeps track of puts private int getPtr;// keeps track of gets protected int capacity; BBuffer (int capacity) { BUFFER = new String[capacity]; this.capacity = capacity; } public void put (String s) {BUFFER[putPtr++] = s;} public String get() {return BUFFER[getPtr++];} }
61/82 Introduire des attributs à BBuffer et déclarer des points daction private int BBuffer.counter = 0; private boolean BBuffer.isEmpty() { return (this.counter==0); } private boolean BBuffer.isFull() { return (this.counter == this.capacity); } pointcut puts(BBuffer object): execution (* BBuffer.put(String)) && this(object); pointcut gets(BBuffer object): execution (* BBuffer.get()) && this(object);
62/82 around() greffon pour la méthode put() void around (BBuffer object): puts(object) { if (object.isFull()) System.out.println("ERROR: Buffer full"); else { // go ahead with the method call. // proceed() takes the same number and types of arguments // as the around() advice. proceed(object); object.counter++; }
63/82 around() greffon pour la méthode get() String around(BBuffer object) : gets(object){ if (object.isEmpty()) return "ERROR: Buffer empty"; else { object.counter--; return proceed(object); }
64/82 Laspect Synchronization public aspect Synchronization { private int BBuffer.counter = 0; private boolean BBuffer.isEmpty() {return (this.counter==0);} private boolean BBuffer.isFull() {return (this.counter == this.capacity);} pointcut puts(BBuffer object): execution (* BBuffer.put(String)) && this(object); pointcut gets(BBuffer object): execution (* BBuffer.get()) && this(object); void around (BBuffer object): puts(object) { if (object.isFull()) System.out.println("ERROR: Buffer full"); else { // go ahead with the method call. // proceed() takes the same number and types of arguments // as the around() advice. proceed(object); object.counter++; } String around(BBuffer object) : gets(object){ if (object.isEmpty()) return "ERROR: Buffer empty"; else { object.counter--; return proceed(object); }}}
65/82 Exécution du programme public class BufferDemo { public static void main(String[] args) { BBuffer buffer = new BBuffer(2); buffer.put("Item 1 "); buffer.put("Item 2 "); buffer.put("Item 3 "); buffer.put("Item 4 "); System.out.println(buffer.get()); } ERROR: Buffer full Item 1 Item 2 ERROR: Buffer empty
66/82 3. Conception par aspects Principes –Martin, Newkirk, and Koss ; Agile Software Development, Principles, Patterns, and Practices ; Prentice Hall, 2003 –Wampler ; Aspect-Oriented Design Principles: Lessons from Object-Oriented Design ; AOSD, 2007 Une conception par aspects doit préserver les principes clés du paradigme orienté objet.
67/82 3. Conception par aspects Single Responsibility Principle (SRP) –A class should have only one reason to change Open-Closed Principle (OCP) –Software entities (classes, aspects, modules, functions, etc.) should be open for extension, but closed for modification
68/82 3. Conception par aspects Interface Segregation Principle (ISP) –Clients should not be forced to depend upon methods that they do not use. Interfaces belong to clients, not to hierarchies Liskov Substitution Principle (LSP) –Subtypes must be substitutable for their base types Les aspects doivent préserver le sous- typage ou ils « cassent » le programme
69/82 3. Conception par aspects Dependency Inversion Principle (DIP) –High-level modules should not depend on low- level modules. Both should depend on abstractions –Abstractions should not depend upon details. Details should depend upon abstractions
70/82 3. Conception par aspects Dependency Inversion Principle (DIP)
71/82 3. Conception par aspects Common Closure Principle (CCP) –The classes in a package should be closed together against the same kinds of changes. A change that affects a closed package affects all the classes in that package and no other packages
72/82 3. Conception par aspects Stable Dependencies Principle (SDP) –Depend in the direction of stability Stable Abstractions Principle (SAP) –A package should be as abstract as it is stable
73/82 3. Conception par aspects Updated Open-Closed Principle (OCP) –Software entities (classes, aspects, modules, functions, etc.) should be open for extension, but closed for source and contract modification Updated Liskov Substitution Principle (LSP') –Subtypes must be substitutable for their base types. Aspects plus base types must be substitutable for the base types.
74/82 3. Conception par aspects Advice Substitution Principle (ASP) –Before advice must support the same or weaker preconditions of the join point it advices. –After advice must support the same or stronger postconditions of the join point it advices. –Around advice must support the same or weaker preconditions of the join point it advices and the same or stronger postconditions of the join point –All advice must support the invariants of the join point
75/82 3. Conception par aspects Introduction Substitution Principle (ISP) –An Introduction must conform to the contract of the advised module and, if called by advice, it must conform to the ASP of the advice
76/82 3. Conception par aspects Pointcut Inversion Principle (PIP) –Pointcuts should not depend on concrete details; they should depend on abstractions Pointcut Scope Principle (PSP) –The more pervasive the scope of a pointcut, the more abstract it should be
77/82 3. Conception par aspects Réécriture des patrons de conception sous forme daspects –Hannemann and Kiczales ; Design Pattern Implementation in Java and AspectJ ; OOPSLA, 2002 –17 patrons sur 23 sont modularisés
78/82 3. Conception par aspects
79/82 4. Avantages et inconvénients Problèmes –Bonne et mauvaise utilisation Peut être sur-utilisée ou mal utilisée De nouvelles connaissances sont nécessaires –Très « tendance », mais peu de cours Chaque nouvelle technologie a ses zélotes et ses critiques De vieilles leçons sont toujours dactualités –Outils Qualité et soutient –Enseignement, entrainement –Valeur daffaire ?
80/82 Avantages et inconvénients Problèmes : mauvaise utilisation –Mécanismes peuvent être mal utilisés Trop ou mauvaises procedures Trop ou mauvaises classes Trop ou mauvaises surcharge Trop ou mauvais aspects, advice, introductions… –Déjà une bonne leçon Règle des 15% –Encore à apprendre Interaction avec JSR-175 (metadata) Passage à léchelle
81/82 Références oad-time-weaving-basics.html oad-time-weaving-basics.html code.html code.html s/aspectjtutorial.pdf s/aspectjtutorial.pdf progguide/index.html progguide/index.html
82/82 Références progguide/index.html progguide/index.html AspectDesignPrinciples.pdf AspectDesignPrinciples.pdf opsla02-patterns.pdf opsla02-patterns.pdf