Wat is AOP en waarom zou ik het toepassen?

Trefwoorden: AOP, OOP, Aspect

Randinformatie

Dit artikel is er na een uitleg van Benny Michielsen (van men klas) waarvoor dank dus. :)
Iedereen heeft al wel eens gehoord van OOP oftewel Object Oriented Programming. Dit artikel richt zich dan ook tot mensen die niet toekomen met het huidige OOP gedachtegoed.
OOP's belangrijkste troeven zijn zonder twijfel:

  • Inkapseling
  • Overerving
  • Polymorphisme
  • Modulariteit

Maar wat missen we dan nog ? Wel het probleem moeten we zoeken bij grotere projecten waar dezelfde code op vele plaatsen terug komt. Het grootste voorbeeld is bijvoorbeeld een 'log'. We gaan dan dus gemeenschappelijke code krijgen in componenten die op zich niets gemeenschappelijk hebben --> gaat in tegen het idee achter OOP.

De tekortkoming van OOP kunnen we dus stellen als:

  1. Wat met functionaliteit die doorheen verschillende klassen gaat?
    Vb: loggen, beveiligingcontrole, foutenafhandeling,...
  2. Huidige oplossingen zorgen voor verspreide en verwarrende code.

Hetgeen dan weer zorgt voor:

  • Slechte opspoorbaarheid
  • Lagere productiviteit
  • Minder code herbruik
  • Slechte code kwaliteit
  • Moeilijke tot onmogelijke evolutie

Wanneer we voor dit alles een oplossing gaan zoeken springt er één in manier van werken in het oog : AOP.
AOP staat voor "Aspect Georienteerd Programmeren".
Dan nu direct naar de realiteit voor een klein voorbeeldje :

Zonder AOP :
CODE
  1. Public class CreditCardWithLog
  2. {
  3.         Logger log;
  4.  
  5.         public void debit(CreditCard card, Money amount) throws InvalidCardexception, NotEnoughAmountException, CardExpiredException
  6.         {
  7.                 logger.log("Start CreditCardProcessor.debit(CreditCard, money) "+ "Card: " + card +  " Amount: " + amount);
  8.                
  9.                 / / debit logica
  10.  
  11.                 logger.log("Einde CreditCardProcessor.debit(CreditCard, money) " + "Card: " + card + "Amount: " + amount):
  12.                
  13.         }
  14. }
We zien hier al meteen een groot probleem :
Waarom zou de kredietkaart moeten weten dat er een log wordt bijgehouden en als er voor elke methode al die code moet komen = veel dubbele code......

Nu gaan we via AOP dit oplossen in 3 stapjes :

  1. Aspectuele decompositie
  2. Concern implementatie
  3. Aspectuele recompositie

En dan krijgen we dit :
CODE
  1. Public aspect LogCreditCard {
  2.         Logger log = new StdoutLogger;
  3.  
  4.  
  5.         Pointcut publicOperation():
  6.         execution(public * CreditCardProcessor.*(..));
  7.  
  8.         Pointcut publicOperationCardAmountArgs(CreditCard card, Money Amount):
  9.         publicOperation() && args(card, amount);
  10.  
  11.         Before(CreditCard card, Money Amount): publicOperationCardAmountArgs(card, amount){
  12.                 logOperation("Starting", thisjoin point.getSignature().toString(), card, amount);
  13.         }
  14.  
  15.         after(CreditCard card, Money amount) returning:
  16.                 publicOperationCardAmountArgs(card, amount) {
  17.                         logOperation("Completing",             thisjoin point.getSignature().toString(), card, amount);
  18.                 }
  19.  
  20.         after (CreditCard card, Money amount) throwing (Exception e):
  21.                 publicOperationCardAmountArgs(card, amount) {
  22.                         logOperation("Exception " + e,              thisjoin point.getSignature().toString(), card, amount);
  23.                 }
  24.  
  25.         private void logOperation(String status, String operation, CreditCard card, Money amount) {
  26.                 logger.log(status + " " + operation + " Card: " + card + " Amount: " + amount);
  27.         }
  28. }
De voordelen zijn direct zichtbaar :

  1. Code voor te loggen is gebundeld
  2. Geen dubbele code
  3. Error afhandeling zit hier mee in verwerkt

De AOP werkt op de volgende manier om dit te bereiken :

  • Joinpoints: Punten (plaatsen) in een programma vb: de aanroep van een methode
  • Pointcuts: Duidt joinpoints aan in een programma en verzamelt informatie
  • Advices: code die wordt uitgevoerd bij een pointcut.
  • De bundeling van advices en pointcuts wordt weaving genoemd
  • Aspect: een eenheid van pointcuts en advices cfr.: een klasse. Een aspect behandelt croscutting concerns.

Praktijk beats theorie daarom een eenvoudig voorbeeldje met de klassieke Hello World :
CODE
  1. // HelloWorld.java
  2. public class HelloWorld {
  3.         public static void say(String message) {
  4.                 System.out.println(message);
  5.         }
  6.  
  7.         public static void sayToPerson(String message, String name) {
  8.                 System.out.println(name + ", " + message);
  9.         }
  10. }
Nu willen we voor elke zin Good Day zeggen en na elk zin Thank you.
Dan krijgen we dit :
CODE
  1. // MannersAspect.java
  2. public aspect MannersAspect {
  3.     pointcut callSayMessage() :
  4.         call(public static void HelloWorld.say*(..));
  5.  
  6.     before() : callSayMessage() {
  7.         System.out.println("Good day!";
  8.     }
  9.  
  10.     after() : callSayMessage() {
  11.         System.out.println("Thank you!";
  12.     }
  13. }
Nu gaan we even dieper in op de gebruikte technieken :

Joinpoint is een welgedefinieerd punt in de uitvoering van een programma.
Mogelijke joinpoints:

  • Methode aanroep en uitvoering
  • Constructor aanroep en uitvoering
  • Het lezen en of schrijven van een variabele
  • Exception handler
  • Initialisatie van een klasse - object

Pointcut :
Programma constructie die joinpoints aanduidt en laat de mogelijk op advice te implementeren.
Vb:
CODE
  1. pointcut callSayMessage() :
  2.         call(public static void HelloWorld.say*(..));
Pointcuts kunnen zowel stringent zijn en of betrekking hebben op attributen, klassen, exception handlers....

Advice specificeerd de uitvoerbare code wanneer een pointcut wordt bereikt.
CODE
  1. before() : call(public * MyClass.*(..)) {
  2.         System.out.println("Before: " + thisJoinPoint + " " +  System.currentTimeMillis());
  3. } after() : call(public * MyClass.*(..)) {
  4.         System.out.println("After: " + thisJoinPoint + " " +  System.currentTimeMillis());
  5. } void around(Connection conn) : call(Connection.close()) && target(conn) {
  6.         if (enablePooling) {
  7.                 connectionPool.put(conn);
  8.         } else {
  9.                 proceed();
  10.         }
  11. }
Wanneer AOP goed wordt toegepast op bijvoorbeeld een applicatie waar logging een grote rol speelt kan men van 35.213 lijnen naar 1.039 lijnen code gaan !
Aop staat nu waar OOP 10-20 jaar geleden was en zal waarschijnlijke een nieuwe leidende rol innemen......

Dit artikel werd geschreven door Bridge_Burner op vrijdag 28 mei 2004 om 23:09 en werd sindsdien 4627 keer gelezen.

  • Pagina
  • 1 van 1

Bericht geplaatst door een gast op dinsdag 28 juni 2005 om 14:42:08.
Leuk en goed geschreven.
Scope is alleen een beetje klein.
Ik zie hier alleen maar AOP vanuit wat ik geloof dat ApectJ is.
AOP wordt op een veel bredere schaal ondersteund.
Ook het idee dat AOP nieuw is is niet geheel waar.
Bijvoorbeeld CGLIB een bekend project is al heel lang in de lucht, en biedt dezelfde functionaliteit mits goed aangestuurd. Hiervoor zijn bekende frameworks als springframework beschikbaar.
Goed werk toch overall.

mvg


Frank Verbruggen

[edit by admin]
Hartelijk dank voor de reactie; je e-mailadres achterlaten op een publieke website is niet echt verstandig. Ik heb het dan ook even geknipt ;).

Martijn
[/edit by admin]
Deze tekst werd het laatst bewerkt voor 17 % door Martijn op woensdag 29 juni 2005 om 23:44:54.
  • Pagina
  • 1 van 1