Mi tömören az aspektus orientált programozás? Legáltalánosabban megfogalmazva egy magasabb szintű absztrakciót vezet be az OO-hoz képest. Másképp fogalmazva: megadja nekünk az alkalmazáslogikát keresztbe-kasul vagdosó síkok (mint például a felhasználó-azonosítás) kiemelésének lehetőségét.
Miért hívják aspektus-orientált programozásnak? Azért, mert a feladatokat bizonyos aspektusok szerint értelmezzük: egy átutalás a naplózás vagy a tranzakció-kezelés szempontjából - hogy csak kettőt említsek a klasszikusok közül.
Ramnivas Laddad, akinek AOP-t és az AspectJ-t elemző és ismertető munkái mérföldkőnek tekinthetőek, a következő két ábrán mutatta be a 'klasszikus' tervezés problémáját és az aspektus orientált programozás által rá adott választ.
Az 1. ábrán a különböző feladatok, megszorítások, elvárások (például logolás, perzisztancia, biztonság) keveredése látható a rendszer moduljaiban.
1. ábra
A 2. ábra - az úgynevezett 'Prizma analógia' - azt szemlélteti, hogyan bomlik fel a követelményrendszer - mint egy fénynyaláb a spektrum szineire - különállóan megvalósitandó feladatokra.
2. ábra
A dolog szépsége az, hogy miután az elvárásokat külön-külön megvalósitottuk - anélkül, hogy bizonyos osztályok kódjat szétszórtuk volna a többiben -, a compiler (itt: weaver) a megfelelő utasítások alapján összeszövi őket egy szerves egésszé. A kérdés már csak az: mi az a rugalmas, barátságos és stabil módszer, ami ezt biztosítja?
Tekintsük újra a logolás példáját, de most az osztály megvalósításakor kizárólag a fontos feladatot valósítsuk meg.
public class FontosOsztaly {
public static void fontosMetodus() { System.out.println(" ->A fontos dolog"); } }
Látható: az osztály csak és kizárólag a rá háruló munkát végzi el. A kérdés már csak az, vajon mi módon vesszük rá, hogy előtte és utána naplózza a történteket?
A megoldás egy apsect (aspektus) létrehozása. Egyelőre minden magyarázat nélkül tekintsük - az amúgy igen beszédes - kódot:
before() : greeting() { System.out.println("[A fontos metodus elindul]"); }
after() : greeting() { System.out.println("[A fontos metodus lefutott]"); }
}
Futás után a kimeneten az alábbi eredményt kapjuk:
A nyelvi elemek értelmezését egyelőre kinek-kinek a ráérzésére bízom - a következő részben részletesen el is magyarázom -, a lényeg talán e nélkül is látható:
Külön kerül az alkalmazáslogika szempontjából lényeges és lényegtelen (Legalábbis ritka, hogy egy programot csak azért készítenénk, hogy naplózgathassunk.)
Bármikor hozzáadható új aspektus. Ne legyünk fantáziátlanok: ez nem csak egy új logger lehet, hanem akár az alkalmazás szempontjából fontos aspektus is, például authentikáció. Az érintett metódusok névvel vagy mintaillesztéssel is megadhatóak. (FontosOsztály.*)
Bármikor összeköthetjük az aspektusokat és az osztályokat, így a tervezés folyamatában nagyobb szabadságot élvezünk.
A tiszta funkcionalitású osztályok sokkal könnyebben fejleszthetőek (nem lesz például véletlenül kikommentezett vagy bentfelejtett logolás vagy tranzakciókezelés), mozgathatóak és rakhatóak össze újra.
A fenti kritériumok némelyike ismerős lehet: a szoftvertervezés világában ezek egyben a komponensalapú programozás követelményei is. Azt pedig, hogy az aspektusorientált programozás kapcsolatban áll a másodrendű logikával, már csak halkan jegyzem meg...