Aspektorientierte Programmierung

24px|Baustelle Beurteilung: Dieser Artikel ist zu unverständlich formuliert. Eine konkrete Begründung findet sich evtl. auf der Diskussionsseite des Artikels. Kannst du zu diesem Text ein Themengebiet feststellen, so weise bitte auf der Diskussionsseite des jeweiligen Portals auf die nötige Überarbeitung hin.

Aspektorientierte Programmierung (AOP) ist ein Begriff der Informatik. Es handelt sich um ein Programmierparadigma zur orthogonalen Programmierung. Semantisch und physisch unabhängige Programmstrecken werden zur Kompilierungszeit zu einem Ganzen verwoben. Sie basiert auf der objektorientierten Programmierung und kann als deren Nachfolgeparadigma angesehen werden.

Inhaltsverzeichnis

Erklärung als Ableitung von prozeduraler und objektorientierter Programmierung

In der prozeduralen Programmierung ist die Ausführung von Code vergleichsweise linear. Durch Verfolgung der Symbole ist jeder einzelne Programmschritt auch bei der Betrachtung eines Teilsystems direkt nachvollziehbar. Beispiel (C): "void function(Component* c) { Component_repaint(c); }" Die Funktion Component_repaint ist eindeutig. Unabhängig davon, ob der Zeiger c auf den Typ Component oder einen abgeleiteten Typ zeigt, wird immer dieselbe Funktion Component_repaint(Component*) aufgerufen.

In der objektorientierten Programmierung wird die Nachvollziehbarkeit durch die Polymorphie reduziert. Beispiel (Java): "void function(Component c) { c.repaint(); }" Es ist nicht eindeutig nachvollziehbar, welche repaint()-Methode ausgeführt wird, das hängt vom tatsächlichen Objekttyp von c ab. Ein von Component abgeleiteter Typ könnte seine eigene repaint()-Methode ausführen, indem er die von Component geerbte Methode repaint() überschreibt.

In der aspektorientierten Programmierung wird die Nachvollziehbarkeit durch die Verwendung von Pointcuts weiter reduziert. Ein Pointcut enthält für einen Join-Point auszuführenden Code, ein Join-Point ist ein genau definiertes Aufrufereignis. Beispiel (AspectJ): "void function(Component c) { c.repaint(); }" Es ist (im Extremfall) sogar möglich, dass die Methoden function(Component) oder repaint() überhaupt nicht ausgeführt werden. Es könnte ein Aspekt existieren, der für den Join-Point "Aufruf der Methode function" einen Pointcut definiert, der die Ausführung der Methode function sogar verhindert.

Während in der prozeduralen Programmierung die Nachvollziehbarkeit durch ein Quelltextfragment und in der objektorientierten Programmierung unter zusätzlicher Kenntnis über die Laufzeittypen vergleichsweise direkt gegeben ist, erfordert die Nachvollziehbarkeit in der aspektorientierten Programmierung die Kenntnis sämtlicher Aspekte, die Point-Cuts für die Join-Points des Code-Fragment definieren. Orthogonal bedeutet hier, dass Eigenschaften von Methoden quasi „senkrecht“ zur normalen Programmierrichtung definiert werden. Die tatsächliche Code-Ausführung wird nicht nur durch die Aufruf- und Typenhierarchive, sondern zusätzlich "senkrecht" (orthogonal) dazu von den Aspekten definiert.

Technische Betrachtung

In einer objektorientierten Laufzeitumgebung könnte aspektorientierte Programmierung durch veränderbare Sprungvektoren ermöglicht werden. Man kann sie als eine Art im Programmierparadigma vorgesehenes Patchen von Funktionen ansehen.

Dies erweist sich an vielen Stellen als praktische Ergänzung der Objektorientierung. Ein Objekt C wird in die Lage versetzt, Interaktionen zwischen zwei Objekten A und B zu überwachen, ohne dass dafür Veränderungen oder Erweiterungen an A und B notwendig sind. Natürlich ist tatsächlich doch eine Veränderung von A oder B oder beiden notwendig. AspectJ erzeugt diese Änderungen automatisch, der Vorgang dafür heißt Weaving.

Einsatzgebiete

Die aspektorientierte Programmierung ist in der Lage, die bisher in der objektorientierten Programmierung eingesetzte ereignisgesteuerte Programmierung (Event-Handling) komplett zu ersetzen. Die ereignisgesteuerte Programmierung dient dazu, ein Objekt X über Veränderungen an einem Objekt Y zu benachrichtigen. Das Objekt Y soll das Objekt X aber eigentlich nicht wirklich kennen. Die bisherige Lösung wird hier am Beispiel eines Fensters in Java (java.awt.Frame) erklärt. Zu Ereignissen, die speziell für Fenster eintreten und über die ein anderer Programmteil benachrichtigt werden soll, zählt unter anderem das Schließen, das Aktivieren und das Deaktivieren. Eine Schnittstelle java.awt.event.WindowListener definiert dafür unterschiedliche Methoden und muss von den Objekten, die über Veränderungen am Fenster benachrichtigt werden möchten, implementiert werden. Objekte, die benachrichtigt werden möchten, müssen sich bei dem jeweiligen anderen Objekt registrieren. Die aspektorientierten Programmierung kann die Definition solcher Schnittstellen überflüssig machen. Ein Aspekt X definiert für das zu überwachende Objekt Y die genau zu überwachenden Code-Ereignisse, genannt Point-Cut, zusammengesetzt aus Joint-Points (Gesamte Methode, Methoden-Aufruf, Methoden-Rückkehr unterscheidbar in Methoden-Rückkehr mit Rückgabewert und Methoden-Rückkehr mit Exception), und definiert für die verschiedenen Point-Cuts den Advice, das ist der auszuführende Code. Die Ausführung von Code in X durch Veränderungen an einem Objekt Y kann also ohne zusätzliches Interface, Methoden und Registrierungsmechanismus erfolgen.

Ein weiteres Einsatzgebiet ist das Software-Testen, wo insbesondere das Einführen neuer Member in Klassen ohne die Veränderung ihrer Quelltexte (Inter-type Declarations) neue interessante Möglichkeiten für die Entwicklung von White-Box-Tests darstellt, z.B. um ein Tracing privater Member durchzuführen.

Entwicklungsstand der aspektorientierten Programmierung

Die aspektorientierte Programmierung steckt derzeit noch in den Kinderschuhen. Ähnlich wie bei der objektorientierten Programmierung sind auch bei der aspektorientierten Programmierung spezielle Compiler und Laufzeitumgebungen erforderlich, um auch bei dem vollständigen Einsatz eine gute Performance zu erreichen (stark polymorphe Programme zeigen erst mit Java oder C# gute Performance, bessere sogar als mit fast jedem C++-Compiler). AspectJ z.B. stellt keine vollständig aspektorientierte Lösung dar, weil die Aspektorientierung sich nur auf Klassen beschränkt, die von AspectJ compiliert werden, nicht jedoch für Klassen gilt, die lediglich verwendet werden. Das besonders interessante Einsatzgebiet der Überwachung von Collections ist damit nur sehr eingeschränkt möglich.

Außerdem ist derzeit noch strittig, ob und wie aspektorientierte Programmierung auf Objekt-Ebene zu realisieren sei. Schnell aber enorm speicherintensiv sind Patchlisten für jedes einzelne Objekt. Eine Lösung könnte die Einführung einer Methodeneigenschaft "controllable" oder "aspect" sein, welche als Joint-Point verwendbare Methoden kennzeichnet, vergleichbar mit "virtual" in C++, das polymorphe Methoden kennzeichnet. Jedoch zeigen einerseits moderne OO-Sprachen wie C# oder Java auf Objektebene ausschließlich virtuelle Methoden (mit "final" wird lediglich die Überschreibbarkeit aufgehoben, der Aufruf erfolgt dennoch virtuell), was ein Indikator dafür ist, eine solche Einschränkung nicht einzuführen, andererseits läuft so eine Einschränkung der Natur der aspektorientierten Programmierung zuwider. Andere Ansätze, wie AspectJ, beschränken sich auf die Aspektorientierung auf Klassenebene, die gezielte Überwachung bestimmter einzelner Objekte ist nicht deklarativ möglich.

Zahlreiche bestimmte Erfahrungen müssen auch erst noch gemacht werden. Klassische Entwurfsmuster, Refaktorisierungen und Fallstricke der Objektorientierung waren schließlich auch nicht von Anfang an bekannt. Auch heute noch kranken zahlreiche OO-Programme an Fehlentwürfen bezüglich der Mutability von Attributen und damit unnötigen defensiven Objektkopien.

Bis sich die aspektorientierte Programmierung zu einem dominierenden Programmierparadigma wie heute die objektorientierte Programmierung entwickelt hat, dürften noch einige Jahre und Programmiersprachen ins Land gehen. AspectJ ist dabei mit den ersten OO-Schritten in C vergleichbar, aus denen später Objective C und C++ hervorgegangen sind. Man kann davon ausgehen, dass sich auf der Basis der objektorientierten Programmiersprachen Perl, Java und C# drei aspektorientierte Programmiersprachen entwickeln werden, wie einst Oberon und C++ aus Pascal und C entstanden sind. Diese werden den Programmierern ermöglichen, die Aspektorientierung zu erkunden, aspektorientierte Programmiermuster zu entwerfen und schließlich zur Entwicklung ausgereifter aspektorientierter Sprachen und Laufzeitumgebungen führen, wie heute Java oder C# für die Objektorientierung.

Beispiel

Folgendes Beispiel erläutert den Grundgedanken der aspektorientierten Programmierung. Die verwendete Programmiersprache ist AspectJ, die Java um die Aspektorientierung erweitert.

Einführendes Beispiel

Als einführendes Beispiel soll eine Standardaufgabe bei der Softwareentwicklung dienen: Tracing von Informationen in eine Datei. Das Vorgehen ohne aspektorientierte Programmierung besteht darin einen Logger zu erzeugen und dort eine entsprechende Methode aufzurufen, die die eigentliche Information in die Logdatei speichert:

public void eineMethode() {
     logger.trace("Betrete \"eineMethode\"");
 
     // Abarbeitung der Methode
     int i = 2 + 2;
 
     logger.trace("Verlasse \"eineMethode\"");
 }
 

Zu Beginn der Methode wird an den Logger gemeldet, dass die Methode betreten wird. Danach folgt die eigentliche Logik der Methode. Zum Schluss wird das Verlassen der Methode protokolliert.

In einer typischen Anwendung sind derartige Methodenaufrufe an den Logger in vielen Methoden und Klassen vorhanden – sie sind über die komplette Anwendung verstreut und keinesfalls modular. Der Logger muss

Somit wird auch klar was mit Semantisch und physisch unabhängige Programmstrecken gemeint ist. In obiger Methode sind zwei eigentlich unabhängige Aufgaben miteinander vermengt. Dies ist zum einen das Protokollieren und zum anderen die eigentliche Logik der Methode, die darin besteht das Ergebnis der Addition in der Variablen i zu speichern.

Die aspektorientierte Programmierung erlaubt es nun, auch Aufgaben wie das Tracing zu modularisieren. Angenommen, das Betreten und Verlassen einer jeden Methode der Klasse soll auf die oben gezeigte Weise protokolliert werden. Gegenüber der konventionellen Programmierung lässt sich eine solche Anweisung in der AOP direkt als Aspekt formulieren:

public aspect Tracing {
     pointcut traceCall():
         call(* AOPDemo.*(..));
 
     before(): traceCall() {
         System.out.println("Betrete \"" + thisJoinPoint + "\"");
     }
 
     after(): traceCall() {
         System.out.println("Verlasse \"" + thisJoinPoint + "\"");
     }
 }
 

Im Aspekt wird bestimmt, dass alle Methoden der Klasse AOPDemo unabhängig von ihrer Signatur einbezogen werden sollen. Die Aufgaben werden auf diese Weise separiert und die ursprüngliche Methode kann verkürzt geschrieben werden:

public void eineMethode() {
     // Abarbeitung der Methode
     int i = 2 + 2;
 }
 

Begrifflichkeit

Das Beispiel beinhaltet bereits die wichtigsten Konzepte, wenn auch nicht alle. In diesem Abschnitt werden die fehlenden hinzugefügt und den in der AOP verwendeten Begriffen zugeordnet.

Dazu wird das Beispiel um folgende Codesequenz erweitert und der bisherige Ablauf grafisch dargestellt:

public void quellMethode() {
     eineMethode();
 }
 

Schematische Darstellung der Verwendung eines Aspekts

Der Aspekt kommt zum Tragen, noch bevor eineMethode() betreten wird. Der Grund dafür ist der Join Point direkt davor. Diese Join Points sind implizit gegeben. D.h. sie sind vor jeder Methode vorhanden. Das Muster, das aus allen vorhandenen Join Points diejenigen aussucht, die für einen Aspekt interessant sind, nennt sich Pointcut. Tatsächlich handelt es sich bei den Pointcuts um Muster, die auch Wildcards erlauben. Um festzulegen, wann welcher Code innerhalb des Aspekts auszuführen ist, kommen Advices zum tragen. Im Beispiel sind dies before und after. Diese Advices sind, neben weiteren, implizit gegeben.

Die Programmierung mit Aspekten erlaubt es zudem in und mit Aspekten das Verhalten von Klassen zu verändern. Es können durch Aspekte Felder und Methoden zu Klassen hinzugefügt werden. Auch hier ist es durch die Angabe von Wildcards gleichzeitig bei mehreren Klassen möglich. In einer Sprache wie Java verletzen diese Inter-Type Declarations die Regel, dass sämtliche Felder und Methoden einer Klasse in einer Datei, bzw. der Vererbungshierarchie der Klasse zu finden sind, da Aspekte dieser nicht angehören.

Typische Aufgaben

AOP ist besonders zur Programmierung von sogenannten Cross-Cutting Concerns geeignet. Beispiele dafür sind Fehlerbehandlung, Validierung und Security.

Profiling APIs, wie bspw. in Java enthalten, arbeiten auf ähnliche Weise wie AOP. Sie dienen dazu, unperformante Codestellen zu ermitteln. Dazu werden durch einen sog. Profiler Zeitmessungen für die Abarbeitung sämtlicher Methoden angestellt. Der eigentliche Profiler kann sich von der Virtual Machine mitteilen lassen, wann eine Methode betreten und verlassen wird. Mit aspektorientierter Programmierung lässt sich daher auch ohne weiteres ein Profiler realisieren.

Zukünftige Entwicklung

Es ist anzunehmen, dass die aspektorientierte Programmierung in den nächsten Jahren, ähnlich wie die generische Programmierung derzeit, auf starke Akzeptanz stoßen wird.

In Deutschland forschen u.a. die TU Darmstadt und die TU Berlin auf diesem Gebiet (siehe Weblinks).

Anmerkungen

Vorteil der Aspektorientierung ist die logische und physische Trennung der Semantik (der Komponente) von dem technischen Detail (Aspekt). Als Nachteil der aspektorientierten Programmierung sei hier insbesondere der Overhead, der nach dem Weaving im generierten Programm entsteht, erwähnt. Dies führt im Allgemeinen zu Performance-Einbußen. Desweiteren reduziert aspektorientierte Programmierung die Nachvollziehbarkeit von Programmverhalten, da die Stellen, an einen ein Aspekt zuständig ist, im betroffenen Code nicht direkt erkennbar sind. Debugging wird so stark erschwert, allerdings kann dieser Nachteil durch Unterstützung einer IDE neutralisiert oder zumindest reduziert werden.

Aspekte und Komponenten können in verschiedenen Programmiersprachen definiert sein.

Siehe auch

Weblinks

See also: Aspektorientierte Programmierung, Cross-Cutting Concern, Debugging, IDE, Informatik, Java (Programmiersprache), Performanz (Informatik), Polymorphie (Programmierung), Profiling (Informatik)