Archive for January, 2004

Ytterligare en Aspekt på din kod

January 20, 2004

Har du någonsin kopierat vissa snuttar kod – om, om och om igen? Trots att du redan har en snygg objektorienterad arkitektur? Trots att den är full med de bästa design-mönster som går att uppbringa? Trots att du valt de bästa verktygen och ramverken? Lika förbaskat så sitter vissa rader, som loggning, både i ryggraden och i varje klass. Vad är fel? Och hur kan man undvika det? Aspektorienterad programmering (AOP) kan vara svaret – om inte annat så är det ett paradigmskifte värt några hjärnceller.

Trots att vi anstränger oss till det yttersta att modellera våra system intill perfektion, nåja i alla fall väldigt, väldigt nära, så är det fortfarande svårt att fånga strukturen hos ett problemområde. Detta märks t ex genom att vi kopierar välbehövliga små bitar kod och lägger in dem överallt. Inte nog med detta, de klasser som helst bara skulle behöva lösa en uppgift, tvingas att känna till säkerhet, databas, loggning osv. Allt detta leder till system som är svåra att underhålla och förändra.

Vad skulle hända om vi kunde plocka ut all loggning i våra projekt och lägga dem i en separat klass – en aspekt? Istället för att sprida ut en viss lösning i vårt system, kan vi nu underhålla den på ett enda ställe. När programmet kompileras så förs koden tillbaks ut där vi hade den innan.

För att bättre förstå vad aspekter är tittar vi närmare på AspectJ, som är en variant av AOP baserad på öppen källkod. Den har flera år på nacken och skapades på Xerox PARC som ett forskningsprojekt med stöd av DARPA.

Ett exempel: Var finns egentligen koden för loggning?

En vanlig syn är loggningen som ligger utspridd över ett helt system. När vi inför en aspekt så får vi ett enda ställe att underhålla den på.

Kodpunkt, punktsnitt och direktiv

I aspektorienterad programmering kan man samla alla dessa små bitar kod på ett enda ställe. Men vi måste ju fortfarande kunna köra dessa på givna ställlen i programmet? Lösningen i AspectJ är att identifiera punkter i exekveringen av ett program – så kalllade kodpunkter (joinpoints).
Det finns flera olika typer av kodpunkter man kan identifiera; metodanrop, skapande av objekt, att sätta attribut, utlösning av undantag (exceptions) osv. Ett punktsnitt samlar en omgång kodpunkter och ger dem ett namn.
Genom att ange kod, direktiv (advice), som ska köras vid dessa kodpunkter kan vi samla tvärgående intressen (crosscutting concerns) i en enda fil – en aspekt. Eller annorlunda uttryckt:

aspekt = punktsnitt(kodpunkter) + direktiv

En aspekt består av en omgång identifierbara punkter i programmet där extra kod ska köras.

Vad kan man göra mer?

Vi har bara gått igenom ett hyfsat enkelt exempel till höger, men det finns många saker man kan använda AspectJ:

  • Olika former av loggning och debugging.
  • Design-by-contract, d v s att man kollar inoch utgående tillstånd och parametrar. När man är nöjd att systemet uppfyller kraven kan man ta bort dessa aspekter.
  • Testa optimering, alternativa lösningar etc.
  • Cachning, synkronisering, accesskontroll etc.
  • … och fler tekniska aspekter som inte har med vårt problemområde att göra.

Innan har vi endast talat om dynamisk förändring av ett program, d v s att påverka arbetsflödet. Men det går också att ändra den statiska strukturen genom att lägga till t ex klassvariabler, metoder, ändra arv. Här uppstår fler möjligheter:

  • Multipelt arv som t ex mix-in i C++
  • Genomförande av kodstandard. ”Ingen får ändra en variabel direkt”. Denna koll kan t o m göras vid kompilering!

Listan kan göras mycket längre. Vi befinner oss bara i början av vår förståelse om var och hur vi bäst använder aspektorienterad programmering. Även om denna inte kommer att ersätta objektorienterad programmering (OOP) så kommer den att vara grunden för AOP på samma sätt som funktioner utgör grunden för OOP.

Ett enkelt (nåja) exempel

Vi skulle kunna gå igenom ett enkelt ”Hello Aspect World”, men jag väljer iställlet att dyka i lite djupare. Jag vill mäta hur lång tid det tar att anropa databasen i vårt hemmasnickrade modelllager.
Vår modellkod innehålller en abstrakt klass som definierar ett gränssnitt för att arbeta mot databasen. Personklassen representerar en post i person-tabellen och implementerar själva anropen med SQL mot databasen i metoderna dbSave, dbUpdate och dbRemove. I ett anfall av hybris lägger jag in mig själv i databasen. Demokod:

Vårt enkla modell-lager som kapslar in databasen.

DBObject tPerson = new Person();
tPerson.setField("firstName", "Björn");
tPerson.setField("lastName", "Granvik");
tPerson.dbSave();

tPerson.setField("company", "Jayway");
 tPerson.dbUpdate();

Du gamla vanliga lösning

Låt oss nu göra en enkel tidtagning. På det gamla sättet så blir det till att kopieraoch-klistra, så mycket som vi nu orkar. Till exempel skulle Person.dbSave kunna se ut som följer:

void dbSave() {
    long tTime = System.currentTimeMillis();
    // Do something...
    tTime = System.currentTimeMillis() tTime;
    System.out.println( thisJoinPoint + ": " + tTime );
}

Och på samma sätt för de andra metoderna dbUpdate och dbRemove. Även om vi förbättrar situationen genom att introducera en tidtagningsklass blir det bara marginellt bättre – det är fortfarande korkad kod! Dessutom så kommer den att ta tid att exekvera. Inget vi vill ha kvar i en produktionsversion.

Du sköna nya aspekt

Låt oss titta på hur man skulle kunna lösa problemet med hjälp av aspekter. Vår nya lösning innehåller en del nya ord som tillägg till Java-språket, se fetstil nedan, men är inte så svår som det först verkar.

/* A timing aspect on DBObject.db methods */
public aspect DbTimerAspect {

    /* Public DBObject methods starting with 'db',
     * taking no parameters and returning anything. */
    pointcut dbMethods() : execution( public * DBObject.db*());

    /* A simple timer advice to be called instead
     * of the original method. */
    void around() : dbMethods() {
        long tTime = System.currentTimeMillis();
        proceed();    // Proceed with call to DbObject.
        tTime = System.currentTimeMillis() tTime;
        System.out.println( thisJoinPoint + ": " + tTime );
    }
}

Vår aspekt DbTimerAspect innehåller dels de metoder som vi vill mäta och den tidmätare vi vill ska köras.
I kodsnittet dbMethods (pointcut) väljs de publika metoder i DBObject och dess subklasser som börjar på ”db”. Vi anger också att det är när metoden körs (execution) som är intressant. Här kan man välja på fler varianter; när metoden anropas, när ett attribut ändras, när ett objekt skapas och många fler sorters kodpunkter.

När demokoden anropar Person.dbSave så är det vårt direktiv som körs istället. Nyckelordet som anger detta är around. Vi hade naturligtvis kunnat ange att den skulle köras före, efter osv. Det första vi gör är att ta reda på tiden. I nästa steg anropar vi originalmetoden med ”proceed”. Nu körs Person.dbSave som vanligt.

Efter att vi kommer tillbaka räknar vi ut den tid som tillbringats i anropet och skriver ut den på konsolen. Lägg märke till att vi har tillgång till den kodpunkt som vi befinner oss i m h a thisJoinPoint. Resultat
När vi kompilerar vår kod gör vi det med kompilatorn från AspectJ. Den genererar ren Java ”byte code”, dvs. koden kan köras precis som vanligt.

Resultatet på konsolen blir:

execution(void se.jayway.jayview.aop.Person.dbSave()): 111
execution(void se.jayway.jayview.aop.Person.dbUpdate()): 30

Ganska mycket det vi förväntade oss.

Slutkläm

Denna artikel räcker egentligen inte för att förstå hela vidden av det man kan göra med aspekter. Jämför vi med en vinprovning har vi bara hunnit titta på etiketten. Men, likväl, vårt lilla exempel pekar på de stora möjligheter som finns med aspekter.
När och var bör man då använda AOP? Än så länge är aspekter inte ”bevisade”. Hur kommer de att skala? Hur påverkas arkitekturen i skarpt läge etc?
Däremot så är det mycket intresssant att förbättra hur vi utvecklar dessa system. Mitt råd blir därför; Skapa aspekter som stödjer din kodstandard, testar varianter av din lösning utan att behöva modifiera koden, gör verktyg som t ex loggningsaspekter, etc.
Om jag ska våga mig på en gissning tror jag att vi inom några år kommer att ha sådana ”tekniska” aspekter som öppen källkod. Fast jag kan ha fel – de kommer kanske redan om ett år eller så 🙂

Termer

kodpunkt (joinpoint) En punkt i exekveringen av ett program, till exempel i anropet av en metod i en klass.
punktsnitt (pointcut) En samling utvalda kodpunkter.
direktiv (advice) Kod som körs vid utvalt punktsnitt under angivna omständigheter, t ex kan man logga ett anrop innan det körs.
tvärgående intressen (crosscutting concerns) Ett system har oftast flera mål som det försöker uppfylla. Förutom att lösa grundproblemet (t ex lönehantering) så finns där också uppgifter som t ex loggning, säkerhet etc. Dessa uppgifter, eller intressen, är typiskt utspridda i systemet – de går på tvären genom strukturen.
OOP Objektorienterad programmering en programmeringsteknik och synsätt som gör det möjligt att öka komplexitetsgrad och återanvändning.
AOP Aspektorienterad programmering att göra fristående moduler som hanterar tvärgående intressen på ett sådant sätt att de kan sömlöst kan föras in i koden igen vid t ex kompilering.

Resurser
AspectJ
Eclipse, AspectJ plug in
JavaWorld: I want my AOP!
AOSD – Aspect Oriented Software Development

Originally published in JayView.