Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Die Basis der Objektorientierung
3 Die Prinzipien des objektorientierten Entwurfs
4 Die Struktur objektorientierter Software
5 Vererbung und Polymorphie
6 Persistenz
7 Abläufe in einem objektorientierten System
8 Module und Architektur
9 Aspekte und Objektorientierung
10 Objektorientierung am Beispiel: Eine Web-Applikation mit PHP 5 und Ajax
A Verwendete Programmiersprachen
B Literaturverzeichnis
Stichwort
Ihre Meinung?

Spacer
 <<   zurück
Objektorientierte Programmierung von Bernhard Lahres, Gregor Rayman
Das umfassende Handbuch
Buch: Objektorientierte Programmierung

Objektorientierte Programmierung
2., aktualisierte und erweiterte Auflage, geb.
656 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1401-8
Pfeil 9 Aspekte und Objektorientierung
  Pfeil 9.1 Trennung der Anliegen
    Pfeil 9.1.1 Kapselung von Daten
    Pfeil 9.1.2 Lösungsansätze zur Trennung von Anliegen
  Pfeil 9.2 Aspektorientiertes Programmieren
    Pfeil 9.2.1 Integration von aspektorientierten Verfahren in Frameworks
    Pfeil 9.2.2 Bestandteile der Aspekte
    Pfeil 9.2.3 Dynamisches Crosscutting
    Pfeil 9.2.4 Statisches Crosscutting
  Pfeil 9.3 Anwendungen der Aspektorientierung
    Pfeil 9.3.1 Zusätzliche Überprüfungen während der Übersetzung
    Pfeil 9.3.2 Logging
    Pfeil 9.3.3 Transaktionen und Profiling
    Pfeil 9.3.4 Design by Contract
    Pfeil 9.3.5 Introductions
    Pfeil 9.3.6 Aspektorientierter Observer
  Pfeil 9.4 Annotations
    Pfeil 9.4.1 Zusatzinformation zur Struktur eines Programms
    Pfeil 9.4.2 Annotations im Einsatz in Java und C#
    Pfeil 9.4.3 Beispiele für den Einsatz von Annotations


Rheinwerk Computing - Zum Seitenanfang

9.2 Aspektorientiertes Programmieren  Zur nächsten ÜberschriftZur vorigen Überschrift

Aspektorientierte Mechanismen erweitern die objektorientierten Sprachen, indem sie definierte Verfahren anbieten, über die in die Struktur von Programmen eingegriffen werden kann. Die aktuellen Implementierungen der aspektorientierten Vorgehensweisen sind sehr unterschiedlich. Sie unterscheiden sich in ihrer Zielsetzung, ihrem Umfang und ihrer technischen Realisierung.


Rheinwerk Computing - Zum Seitenanfang

9.2.1 Integration von aspektorientierten Verfahren in Frameworks  Zur nächsten ÜberschriftZur vorigen Überschrift

Eine Basis für die aspektorientierte Vorgehensweise bilden die sogenannten Anwendungscontainer, in die immer häufiger Fähigkeiten der Aspektorientierung integriert werden.

Container

Anwendungscontainer sind nicht notwendigerweise ein Framework, das die aspektorientierte Vorgehensweise generell ermöglicht, vielmehr übernehmen sie die Umsetzung bestimmter wichtiger technischer Aspekte wie Zugriffs- und Transaktionssicherheit, Persistenz, Lastverteilung und die Bereitstellung von verschiedenen Diensten.

Die Container unterscheiden sich in der Art, in der sie mit den in ihnen laufenden Anwendungen kooperieren. Manche Container verlangen, dass die Anwendung nach bestimmten Mustern strukturiert wird, so dass sie dann nur in solchen Containern laufen kann. Auch wenn die Container eine große Hilfe bei der Trennung der ausgewählten technischen Anliegen von der fachlichen Aufgabe der Anwendung sind, zwingen sie die Anwendungsklassen, sich an bestimmte für sie spezifische Schnittstellen zu halten, und binden sie so an die Architektur der jeweiligen Containerspezifikation.

EJB-Container

Als ein Beispiel für solche Container in Java können die früheren Versionen der Enterprise Java Beans-Container dienen. Die fachliche Funktionalität der Enterprise Java Beans (EJB) ist von den technischen Aspekten, die von den EJB-Containern übernommen werden, im Wesentlichen getrennt. Die EJB sind aber von der EJB-API abhängig und können nur eingeschränkt ohne einen Container eingesetzt werden – auch wenn in bestimmten Kontexten die von den Containern bereitgestellten Aspekte gar nicht benötig werden.

Andere Container fordern von den fachlichen Klassen keine speziellen Abhängigkeiten. Wenn die fachlichen Klassen innerhalb eines solchen Containers laufen, erhalten sie über den Container die Funktionalität bestimmter Aspekte zur Verfügung gestellt. Wenn allerdings Aspekte wie die Persistenz, die Sicherheit oder andere vom Container bereitgestellten Aspekte nicht benötig werden, können die fachlichen Klassen auch ohne den Container eingesetzt werden.

Ein gutes Beispiel für eine erfolgreiche Umsetzung dieses Konzepts ist zum Beispiel das Framework Spring, das einen Container für Java-Klassen zur Verfügung stellt. Doch auch die neueren Spezifikationen der EJB bewegen sich in diese Richtung. So mussten zum Beispiel die persistenten Entity Beans vor der Version 3.0 der EJB-Spezifikation spezielle Schnittstellen implementieren. Ab der Version 3.0 der EJB-Spezifikation kann es sich um ganz einfache Java-Klassen handeln, die selbst nicht wissen müssen, dass sie in manchen Kontexten als EJB eingesetzt werden sollten.

Aspektorientierte Frameworks

Im Gegensatz zu den Containern, die sich nur um bestimmte Aspekte kümmern und gleich deren Implementierung bereitstellen, besteht der Zweck von aspektorientierten Frameworks darin, den Programmierern die Entwicklung beliebiger Aspekte zu ermöglichen.

Auch diese Frameworks unterscheiden sich bezüglich Umfang und der gewählten technischen Realisierung: Ändern sie das Programm während dessen Übersetzungszeit? Ändern sie die Klassen dynamisch während der Laufzeit des Programms, oder beschränken sie sich auf die Möglichkeiten von Reflexion und Introspektion?

Mit der wachsenden Verbreitung und Popularität der Aspektorientierung werden immer mehr Container um solche generellen aspektorientierten Frameworks erweitert. Zum Beispiel bieten Spring oder JBoss [JBoss wurde ursprünglich als ein EJB-Container entwickelt. Mittlerweile unterstützt er neben der EJB-Funktionalität auch viele andere Bereiche. Das JBoss-AOP Framework kann auch außerhalb eines Containers verwendet werden, siehe auch http://labs.jboss.com/portal/jbossaop/index.html. Informationen zu den AOP-Fähigkeiten von Spring finden sich unter http://www.springframework.org/docs/reference/aop.html. ] auch den Einsatz von selbst entwickelten Aspekten an.


Rheinwerk Computing - Zum Seitenanfang

9.2.2 Bestandteile der Aspekte  Zur nächsten ÜberschriftZur vorigen Überschrift

Schauen wir uns die Konzepte der Aspektorientierung jetzt etwas näher an. Da sich im Deutschen noch keine allgemein akzeptierte Terminologie im Bereich der Aspektorientierung etabliert hat, werden wir für viele Konzepte lieber die im Englischen üblichen Bezeichnungen verwenden.

Crosscutting Concerns implementieren

Die aspektorientierte Vorgehensweise ermöglicht es, Anliegen, die sich kompakt und zusammenhängend spezifizieren lassen, auch kompakt und zusammenhängend zu implementieren. Und das auch, wenn sie Objekte verschiedener Klassen betreffen und so nach der puren objektorientierten Vorgehensweise in die Quelltexte vieler Module des Systems eingearbeitet werden müssten. Diese Anliegen werden übergreifende Anliegen, Crosscutting Concerns, genannt. Wir haben Sie bereits in Abschnitt 9.1 vorgestellt.

Auch in den aspektorientierten Systemen werden die übergreifenden Anliegen in alle betroffenen Klassen eingearbeitet. Allerdings geschieht dies automatisiert, gesteuert durch die programmierten Aspekte. Den Prozess der Manipulation der Klassen durch die definierten Aspekte nennt man auch das Einweben (engl. weave) der Aspekte in das Geflecht der Klassen.


Icon Hinweis Dynamisches Crosscutting, statisches Crosscutting

Grundsätzlich unterscheiden wir zwei Arten der Manipulation von Klassen:

  • Mit dem dynamischen Crosscutting wird das Verhalten der Objekte modifiziert – es werden zusätzliche Schritte in die Ausführung der Methoden hinzugefügt.
  • Durch das statische Crosscutting werden die Strukturen des Programms selbst verändert. Einer Klasse können Sie hierdurch zusätzliche Datenelemente oder Methoden hinzufügen oder die Vererbungshierarchie anpassen. Wir können sogar teilweise die Programmiersprache selbst verändern, so dass bestimmte zusätzliche Übersetzungsüberprüfungen stattfinden.


Rheinwerk Computing - Zum Seitenanfang

9.2.3 Dynamisches Crosscutting  Zur nächsten ÜberschriftZur vorigen Überschrift

Interzeptor Einschubmethode

Beim dynamischen Crosscutting erhalten Sie die Möglichkeit, eine Aktion innerhalb des Programms mit einer vorbereitenden Aktion oder einer abschließenden Aktion zu erweitern. Möglich ist es auch, die Aktion komplett durch eine neu definierte Aktion zu ersetzen, welche die ursprüngliche Aktion an einer definierten Stelle durchführt. Dabei kommen die sogenannten Interzeptoren zum Einsatz.


Icon Hinweis Interzeptor (engl. Interceptor)

Interzeptoren unterbrechen den vorher definierten Programmfluss und klinken sich in dessen Ausführungspfad ein. An definierten Stellen kommen sogenannte Einschubmethoden zur Ausführung.

Je nachdem, an welcher Stelle ein Interzeptor zum Einsatz kommt, werden die Methoden in before-, after- oder around-Methoden unterschieden.


Implementierung von Interzeptoren

Die aspektorientierten Frameworks haben mehrere Möglichkeiten, solche Interzeptoren zu implementieren. Sie können sie zum Beispiel während der Übersetzung der Quelltexte an den richtigen Stellen generieren. Eine andere Möglichkeit ist es, die Klassen zur Laufzeit zu modifizieren, so dass im Rahmen der Ausführung einer Methode jeweils entsprechende Interzeptoren aufgerufen werden. Eine technisch durchaus mögliche Lösung ist es auch, dynamisch Proxy-Klassen zu erzeugen, die statt der Originalklassen in der Anwendung verwendet werden. Aufrufe von Methoden auf den Proxy-Klassen können dann auf die Originalklassen umleiten, nachdem sie die before- und bevor sie die after-Einschubmethode ausgeführt haben.

Um festzulegen, an welchen Stellen ein Interzeptor ausgeführt wird, werden die Joinpoints verwendet.


Icon Hinweis Joinpoints

Die Stellen, an denen Sie sich mit Interzeptoren in ein bestehendes Programm einklinken können, werden Joinpoints genannt. Joinpoints können Methodenausführungen sein. Sie können aber auch andere Joinpoints definieren: die Zugriffe auf die Attribute eines Objekts, die Aufrufe der Operationen einer Klasse, die Erstellung von Objekten oder auch das Auslösen einer Exception.


Die ersten EJB-Container zum Beispiel konnten nur mit einem definierten Satz von Joinpoints umgehen und die EJB-Klassen und -Schnittstellen mussten diese Joinpoints explizit bereitstellen.

Wir werden uns die verschiedenen Joinpoints gleich näher anschauen, zuerst beschreiben wir aber die anderen wichtigen Konstrukte einer aspektorientierten Anwendung.

Die sogenannten Pointcuts führen Joinpoints und Interzeptoren zusammen.


Icon Hinweis Pointcut

Ein Pointcut definiert, an welchen konkreten Joinpoints Interzeptoren aufgerufen werden sollen. Während Joinpoints nur mögliche Einstiegspunkte für Interzeptoren definieren, legen Pointcuts fest, welche Joinpoints in einem konkreten Fall genutzt werden sollen. Ein Pointcut legt aber noch nicht fest, welche konkreten Interzeptoren verwendet werden.


Ein Pointcut kann mehrere Joinpoints beinhalten. Zum Beispiel können Sie einen Pointcut als »die Ausführung jeder Methode der Klasse Customer, deren Name mit set anfängt« definieren. Andererseits kann ein Joinpoint in mehreren Pointcuts enthalten sein. Sie können einen anderen Pointcut als »die Ausführung jeder öffentlichen Methode der Klasse Customer« definieren. Die öffentliche Methode setName wäre in beiden dieser Pointcuts enthalten.

Nun fehlt uns aber noch ein Konstrukt, das auch festlegt, welche Interzeptoren denn verwendet werden sollen. Das erledigt ein Advice.


Icon Hinweis Advice

Ein Advice beschreibt, was wann an den Joinpoints eines Pointcuts passiert. Ein Advice ist also eine Anweisung, die festlegt, welche Interzeptoren an den durch einen Pointcut selektierten Joinpoints in den Programmfluss eingefügt werden und welcher Code an diesen Stellen ausgeführt wird.


Advices stellen damit einfach ein Stück Code dar, das die einzufügende Funktionalität repräsentiert.

Mit den bisher definierten Begriffen können wir nun auch beschreiben, was einen Aspekt in der aspektorientierten Programmierung ausmacht.


Icon Hinweis Aspekt

Ein Aspekt fasst Pointcuts und Advices zusammen. So kann zum Beispiel ein Aspekt der Sicherheit aus mehreren konkreten Advices bestehen, die sich auf verschiedene Pointcuts beziehen. Zusätzlich kann ein Aspekt auch existierende Klassen modifizieren, indem er sogenannte Introductions definiert. Introductions werden wir in Abschnitt 9.2.4 vorstellen.


Beispiel

Bevor wir zu den verschiedenen Arten von Joinpoints kommen, betrachten wir am besten ein kurzes Beispiel, in dem Joinpoints, Pointcuts und Advices zu einem Aspekt zusammengefasst werden. In Listing 9.2 ist die Definition eines Aspekts in AspectJ, einer aspektorientierten Erweiterung zur Sprache Java, dargestellt.

public aspect Logging {   
  pointcut toBeLogged() : 
      execution (public * MyPackage.*(..));  
 
  before(): toBeLogged() {  
    System.out.println("Before " +                 
       thisJoinPointStaticPart.toLongString());    
  } 
}

Listing 9.2    Definition eines einfachen Aspekts in AspectJ

In Zeile wird der Aspekt Logging deklariert. Dieser beschreibt den technischen Aspekt der Protokollierung von Daten bei Methodenaufrufen. Der Aspekt definiert in Zeile den Pointcut toBeLogged. Betroffen ist die Ausführung (execution) aller öffentlichen Methoden (public) im Package MyPackage (MyPackage.*) mit beliebiger Parameterliste ((..)). Dieser Pointcut legt damit die Menge der betroffenen Joinpoints fest. Ein Joinpoint ist dabei zum Beispiel die Ausführung einer konkreten Methode. Aus der Menge der möglichen Methodenausführungen selektiert der Pointcut diejenigen, bei denen die Methoden im Bereich MyPackage deklariert wurden.

Schließlich wird in Zeile der zugehörige Advice festgelegt. Das ist der Code, der ausgeführt wird, wenn einer der Joinpoints erreicht wird, die durch den Pointcut toBeLogged festgelegt werden. Im konkreten Fall handelt es sich um einen before-Advice, der den Pointcut toBeLogged verwendet.

Der Code wird nun immer direkt in den mit markierten Zeilen ausgeführt, bevor eine Methode ausgeführt wird, die im Bereich MyPackage enthalten ist.

Arten von Joinpoints

Prinzipiell kann man in die Programmstruktur an jeder beliebigen Stelle eingreifen – schließlich sind die Programme aus der Sicht eines Metaprogramms »nur« Daten. Doch die existierenden aspektorientierten Systeme können sich nur in bestimmte Arten von Joinpoints einklinken. Je nach System gibt es unterschiedliche Joinpoint-Arten, an denen Sie in das Programm eingreifen können. Die Joinpoints, deren Manipulation ein aspektorientiertes System ermöglicht, werden offen gelegte Joinpoints (englisch Exposed Joinpoints) genannt. Die meisten aspektorientierten Systeme legen die im Folgenden beschriebenen Joinpoints zumindest teilweise offen.

  • Ausführung einer Methode (Method Execution) Dieser Joinpoint wird angesteuert, wenn eine konkrete Implementierung einer Methode einer Klasse ausgeführt wird. Ein Ausführungs-Joinpoint umfasst die Ausführung dieser Methode. Ein before execution-Advice wird direkt vor dem Ausführen der Methode aktiviert, ein after execution-Advice direkt danach. Ein aroundexecution-Advice ersetzt die Ausführung der Methode. Wenn die Methode in einer Unterklasse überschrieben wird, so wird der Joinpoint nicht durchlaufen, es sei denn, die überschreibende Methode ruft die Originalmethode selbst auf. Da zum Beispiel eine abstrakte Methode keine Implementierung hat, kann man keinen Ausführungs-Joinpoint für diese Methode definieren.
  • Aufruf einer Operation (Operation Call) Dieser Joinpoint liegt auf den Aufrufstellen einer Operation. Im Gegensatz zu den Ausführungs-Joinpoints geht es nicht um die konkrete Implementierung einer Methode, sondern um die Aufrufe der entsprechenden Operation. Also kann man durchaus einen Aufruf-Joinpoint auch für abstrakte Methoden definieren. Wenn für eine Methode sowohl ein Ausführungs- als auch ein Aufruf-Joinpoint durch entsprechende Advices ergänzt worden sind, dann werden die Advices in der folgenden Reihenfolge durchgeführt:
before call, before execution, die Methode selbst, after execution, after call
    • Wenn in einem before call-Advice die Aufrufkette unterbrochen wird, zum Beispiel durch eine Exception, kann man verhindern, dass es zu der Ausführung der Methode und somit auch dem Erreichen des execution-Joinpoints kommt.
  • Ausführung eines Konstruktors (Constructor Execution) und Aufruf eines Konstruktors (Constructor Call) Diese Joinpoints umfassen die Erzeugung der Exemplare der Klassen. Je nach Programmiersprache kann und muss man unterscheiden, welchen Abschnitt des Konstruktionsvorganges eines Objekts der jeweilige Joinpoint umfasst.
    • In Java zum Beispiel bewirkt der Aufruf eines Konstruktors, dass zuerst die Konstruktoren der Oberklassen in ihrer Vererbungshierarchie nacheinander ausgeführt werden, um schließlich den aufgerufenen Konstruktor auszuführen. In diesem Falle umfasst der constructor call-Joinpoint die gesamte Konstruktion des Objekts, inklusive der Ausführung der Konstruktoren der Oberklasse; der constructor execution-Joinpoint dagegen umfasst ausschließlich die Ausführung des Konstruktors der Klasse, nicht aber die Ausführung der Konstruktoren der Oberklassen.
    • Eng mit diesen Joinpoints sind in Java die Klassen- und Objektinitialisierungs-Joinpoints verbunden. Sie umfassen die Initialisierung der Klassen selbst beziehungsweise den Initialisierungsteil der Objekte, der außerhalb der Konstruktoren liegt.
    • Ein before- und ein after-Advice an den constructor call-Joinpoints aller Unterklassen einer Klasse wären also die Lösung für das Messen der Gesamtzeit, die für das Erstellen aller Exemplare einer Klasse und ihrer Unterklassen gebraucht wird.
  • Zugriff auf die Datenelemente (Field Access) Diese Joinpoints umfassen auch das Lesen, das Ändern, das Erstellen und das Entfernen von Datenelementen der Exemplare einer Klasse. So können wir von field get-, field set-, field create-, und field delete-Joinpoints sprechen. In Java ist die Menge der Datenelemente, die ein Objekt hat, bereits zu Übersetzungszeit bekannt, somit können aus dieser Kategorie also nur die field get- und field set-Joinpoints existieren.

Auch wenn die call- und die Datenelement-Joinpoints innerhalb der Methoden liegen, lassen sie sich durch die Strukturelemente der Klassen beschreiben. Um sie definieren zu können, müssen wir nicht die Struktur der Methoden kennen. Um allerdings an diesen Joinpoints die entsprechenden Interzeptoren erstellen zu können, muss das aspektorientierte System die Struktur der Methoden analysieren und eventuell verändern.

Natürlich kann man sich auch Joinpoints vorstellen, die sich nur durch die Struktur der Methoden selbst beschreiben lassen. So sind Joinpoints für den Zugriff auf lokale Variablen denkbar, für die Grenzen von for-Schleifen oder in Java für die Grenzen der synchronized-Blöcke. Uns ist zurzeit kein aspektorientiertes System bekannt, das solche Joinpoints offen legen würde.

Ausnahmebehandlung

Eine Ausnahme bildet hier die Ausnahmebehandlung. [Ausnahmsweise nennen wir hier die Exceptions deutsch »Ausnahmen«, nur aus reiner Freude an dem dadurch möglich gewordenen Wortspiel. ] Zum Beispiel sind in ApectJ, der aktuell populärsten aspektorientierten Erweiterung von Java, die catch-Abschnitte offen gelegt.

AspectJ


AspectJ

AspectJ erweitert die Sprache Java um aspektorientierte Möglichkeiten. AspectJ selbst ist die Spezifikation dieser Erweiterung. Meistens wird jedoch auch die Umsetzung des Compilers für diese Spezifikation durch das AspectJ-Projekt damit gleichgesetzt. Diese Umsetzung von AspectJ erlaubt sowohl statisches als auch dynamisches Einweben von Aspekten. Zurzeit ist AspectJ die am weitesten verbreitete Variante von aspektorientierten Sprachen oder Spracherweiterungen. Personell besteht über Gregor Kiczales eine Kontinuität mit der Entwicklung von Metaobjekt-Protokollen. Gregor Kiczales war maßgeblich an der Entwicklung des Metaobjekt-Protokolls für das Common Lisp Object System beteiligt. Außerdem war er der Leiter des Teams, das AspectJ bei Xerox PARC entwickelte. Infomationen zu AspectJ finden Sie unter http://eclipse. org/aspectj/.

Neben AspectJ haben vor allem die Entwickler von Containern für Java-Anwendungen aspektorientierte Erweiterungen für die Sprache Java umgesetzt. So gibt es eine aspektorientierte Erweiterung, die zusammen mit dem Spring-Container entwickelt wurde (Spring-AOP), und eine Erweiterung, die für die Zusammenarbeit mit dem JBoss-Container umgesetzt wurde (JBoss-AOP). Beide Varianten sind aber auch eigenständig nutzbar.


Arten von Pointcuts

Es gibt verschiedene Arten von Pointcuts, jeder davon selektiert eine Menge der offen gelegten Joinpoints eines Programms. Jeder Pointcut spezifiziert eine Bedingung, die entscheidet, ob ein Joinpoint von diesem Pointcut erfasst wird oder nicht. Wir können diese Bedingungen auch miteinander kombinieren, so dass wir die Joinpoints, an deren Stelle ein Interzeptor erzeugt werden soll, ziemlich präzise bestimmen können.

Grundsätzlich können wir zwischen zwei Arten von Pointcuts unterscheiden.

1. Die statischen Pointcuts lassen sich bereits zur Übersetzungszeit der Anwendung bestimmen. Schon vor dem Programmstart können Sie die Stellen im Programm identifizieren, an denen die nötigen Interzeptoren eingefügt werden sollen.
       
2. Bei den dynamischen Pointcuts kann dagegen erst zur Laufzeit bestimmt werden, ob der hinzugefügte Interzeptor aktiviert werden soll.
       

Statische Pointcuts

Die statischen Pointcuts können zum Beispiel folgende Kriterien für die Selektion der Joinpoints anwenden:

  • Die Art des Joinpoints. Ist es ein method call-Joinpoint oder ein field get-Joinpoint?
  • Die Klasse, um deren Methoden, Felder beziehungsweise Initialisierung es sich handelt. Ist es eine konkrete Klasse? Ist es eine Unterklasse einer Klasse? Endet der Name der Klasse auf Test?
  • Der Name und die Signatur der Methoden. Fängt der Name der Methode mit set an? Hat die Methode bestimmte spezifizierte Argumenttypen? Hat sie einen Rückgabewert? Ist es eine öffentliche oder eine private Methode?
  • Die Paketstruktur des Programms. Findet ein Aufruf im Paket A oder in einem Unterpaket davon statt?

Dynamische Pointcuts

Alle diese Kriterien lassen sich bereits zur Übersetzungszeit bestimmen. Andere Kriterien sind jedoch nur zur Laufzeit bekannt. Die dynamischen Pointcuts können zum Beispiel folgende Kriterien anwenden:

  • Gehört das aufrufende Objekte zu einer bestimmten Klasse?
  • Gehört das aufgerufene Objekt zu einer bestimmten Klasse?
  • Befindet sich das Programm an einer bestimmten Stelle seines Ablaufs?

Wir werden uns die verschiedenen Arten von Pointcuts in Abschnitt 9.3 an Beispielen zur Anwendung der aspektorientierten Vorgehensweise anschauen.


Rheinwerk Computing - Zum Seitenanfang

9.2.4 Statisches Crosscutting  topZur vorigen Überschrift

Wir haben bereits erwähnt, dass die Funktionalität, die an Joinpoints ausgeführt werden kann, über sogenannte Advices beschrieben wird. Advices stellen in der Regel einfach ein Stück Code dar, das die einzufügende Funktionalität repräsentiert.

Nun ist es aber durchaus möglich, dass die in einem Advice implementierte Funktionalität zusätzliche Daten braucht, die einem Objekt zugeordnet werden. Eine andere mögliche Anforderung ist es auch, dass wir die nach außen sichtbare Funktionalität einer Klasse erweitern möchten. Nehmen wir als Beispiel an, dass Sie alle Exemplare einer Klasse nach dem Observer-Muster beobachtbar machen möchten.

Dazu müssen Sie die Definition der Klasse erweitern. Sie benötigen die sogenannten Introductions.


Icon Hinweis Introductions

Introductions fügen in Klassendefinitionen neue Datenelemente und Methoden ein. Sie erweitern damit nachträglich eine bereits existierende Klasse. Eine spezielle Form von Introductions sind die sogenannten Mixins, die Sie bereits in Abschnitt 5.4.3, »Mixin-Module statt Mehrfachvererbung«, kennengelernt haben.


Neben den Mixins gibt es eine Reihe von weiteren Möglichkeiten zur Umsetzung von Introductions. Eine Variante von Introductions basiert auf der Veränderung der Klassenhierarchie. Damit können Sie in einem Aspekt zu einer Klasse nicht nur die nötigen Daten und Methoden hinzufügen, damit ihre Exemplare beobachtbar werden, Sie können sogar bestimmen, dass die Klasse eine Unterklasse einer andere Klasse, zum Beispiel von Observable, werden soll.

Methoden implementierung in Interfaces

Wenn Sie AspectJ verwenden, können Sie dabei sogar Methodenimplementierungen in explizite Schnittstellen (Interfaces) einführen. Dies hilft Ihnen dabei, Schnittstellen von Klassen möglichst schmal zu halten, diese aber dennoch einfach nutzbar zu machen. Diese beiden Anforderungen sind grundsätzlich zunächst gegenläufig. Wir sprechen dabei von einer Abwägung zwischen minimalen und benutzerorientierten Schnittstellen. [Wir haben es hier (wieder) mit einer Begriffsprägung durch Martin Fowler zu tun. Er unterscheidet im Englischen zwischen Minimal Interface und Human Interface. Wir haben für Letzteres die Übersetzung »benutzerorientierte Schnittstelle« gewählt. Der zugehörige Artikel von Martin Fowler findet sich unter http://www.martinfowler.com/bliki/HumanInterface.html. ]


Minimale versus benutzerorientierte Schnittstellen

Bei der Definition einer abstrakten Schnittstelle verfolgt man zwei sich widersprechende Interessen.

Einerseits möchte man nur das Nötigste spezifizieren, um die Entwicklung der Klassen, die diese Schnittstelle implementieren, zu erleichtern. Außerdem sollten die Operationen der Schnittstelle tatsächlich abstrakt sein und sich nicht auf die Aufrufe anderer Operationen abbilden lassen. Ließe sich eine Operation der Schnittstelle auf ihre anderen Operationen abbilden, würden alle Im-plementierungen dieser Schnittstelle den Code wiederholen.

Andererseits, wenn die nicht abstrakten, aber häufig benutzten Operationen nicht in der Schnittstelle spezifiziert sind, wiederholt sich deren Implementierung an den Aufrufstellen.

Nehmen wir als Beispiel die Schnittstelle einer nur lesbaren Liste, auf deren Elemente man über einen nullbasierten Index zugreifen kann. Um Ihre Schnittstelle komplett zu beschreiben, reichen zwei Operationen: get(i), die das Element an der i-ten Stelle zurückgibt, und size(), welche die Anzahl der Elemente liefert.

Wenn wir aber häufig auf das letzte Element zugreifen, wäre es nett, die Methode last() { return get(size()-1); } definieren zu können. Nun, diese Methode ist nicht abstrakt – sie lässt sich komplett auf die anderen Operationen der Schnittstelle abbilden. In einer Sprache wie Java, die keine Mehrfachvererbung der Implementierung zulässt, müsste man diese Methode entweder auf allen Aufrufstellen durch den Aufruf list.get (list.size()-1) wiederholt ersetzen, oder man müsste sie in allen Klassen, welche die Schnittstelle implementieren, wiederholt ausprogrammieren.

Weder die Minimalschnittstelle noch die »humane Schnittstelle« (mach es dem Aufrufer so einfach wie möglich) lösen unser Problem der Redundanzvermeidung.

Wir könnten die Redundanz vermeiden, wenn wir die Schnittstelle als eine abstrakte Klasse mit der konkreten Methode last() implementieren würden. In einer Programmiersprache ohne Mehrfachvererbung würden wir dadurch aber verhindern, dass die Unterklassen von anderen Klassen erben können. Sprachen mit der Mehrfachvererbung haben hier die Nase vorn.

Die Fähigkeit von AspectJ, Schnittstellen um Methodenimplementierungen zu erweitern, hilft uns dabei, einen Kompromiss zwischen minimalen und benutzerorientierten Schnittstellen zu finden.


Zusätzliche Warnungen und Fehlermeldungen

Ein anderer Anwendungsbereich von Introductions ist die Überprüfung zusätzlicher Bedingungen bei der Übersetzung von Programmen.

In einem vernünftig entworfenen MVC-System würden Sie zum Beispiel verlangen, dass die Modell-Klassen nie direkt von den View-Klassen abhängig sind, sondern nur von einer abstrakten View- oder Observer-Schnittstelle.

Durch die Spezifikation eines geeigneten Pointcuts könnten Sie alle Stellen finden, an denen aus dem Quelltext einer Modell-Klasse eine Methode einer konkreten View-Klasse aufgerufen wird. Die Existenz von Joinpoints, welche die Bedingungen dieses Pointcuts erfüllen, könnten Sie bei der Übersetzung des Programms als einen Fehler oder zumindest als eine Warnung signalisieren.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 <<   zurück
  Zum Rheinwerk-Shop
Neuauflage: Objektorientierte Programmierung






Neuauflage:
Objektorientierte Programmierung

Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: Einstieg in Python






 Einstieg in Python


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern