Eine redundante Geschichte der Programmierung: Von Parnas bis Scala

Objektorientierte Programmierung

Die von Simula 67 eingeführte und von Smalltalk-80 verbreitete Technik der Objektorientierung übernimmt das Information Hiding für Objektattribute und enthält als Neuerungen Beerbung, Polymorphie und dynamisches Binden. Beerbung alleine ermöglicht eine geringe Redundanzvermeidung, indem gemeinsame Daten mehrerer Records in eine Basisklasse ausgelagert werden. Gegenüber der Komposition spart das nur Schreibaufwand beim Attributzugriff. Polymorphie der Referenzen ermöglicht eine Flexibilität wie mit ungetypten Pointern in C. Jedoch ist es ein wenig sicherer, da man die gezeigten Elemente auf Unterklassen der Basisklasse beschränkt. Mit dem dynamischen Binden für virtuelle Funktionen (C++, 1983) kam der große, redundanzvermeidende Fortschritt, der heute als Template Method Pattern [3] allgemein bekannt ist.

Problem der Transaktionsbegleitung: Als Beispielproblem dient hier die Transaktionsbegleitung. In Enterprise-Applikationen soll jede Operation der Geschäftslogik als Transaktion ausgeführt werden. Im Erfolgsfall sollen die Datenbankveränderungen freigegeben, im Misserfolgsfall gemeldet und rückabgewickelt werden. Statt dieses Verhalten in jeder Logikmethode redundant zu programmieren, kann man es in eine abstrakte Klasse auslagern, die eine zu überschreibende Aktionsmethode aufruft. Die Lösung sieht bei Java wie in Listing 1 aus.

Listing 1
abstract class Transaction {
    public Transaction(){
        final Connection con = Util.getConnection();
        try{
            doAction();
            con.commit();
        }catch(Exception ex){
            report(ex);
            con.rollback();
        }
    }
    abstract void doAction();
}

Der Konstruktor spielt hier die Rolle der Template Method. Sie folgt einem festen Ablauf, um das commit oder rollback sicherzustellen. Nur die konkret auszuführende Nutzaktion wird in der Template Method der abstrakten Methode doAction übertragen. Es ist die Aufgabe des Programmierers der Unterklasse, diese Methode zu überschreiben. Eine Benutzung würde folgendem Muster folgen und in einem realen System hunderte Male vorkommen, was eine enorme Redundanzvermeidung bedeutet, auch wenn der Schreibaufwand noch problematisch ist:

new Transaction(){
    public void doAction(){
        //Hier wird die eigentliche Nutzaktion untergebracht.
    }
};

Traits: Gegenüber herkömmlicher Beerbung erlaubt Scala (2003) das widerspruchsfreie Einmischen mehrerer Traits (teilimplementierte Interfaces), die jeweils solche Template Methods anbieten können. Dadurch ist eine freie Kombinierbarkeit verschiedener Dienste in eine Klasse möglich. Tatsächlich zeichnet sich die Scala Collections Library durch eine äußerst hohe, innere Wiederverwendung weniger Template Methods aus. Das ist ein großer Beitrag zur Redundanzfreiheit. Eine alternative Lösungsmöglichkeit in Java wäre mittels Reflection, wie es die meisten Applikationsserver intern realisieren.

Aspektorientierte Programmierung

Aspektorientierte Programmierung ermöglicht es, Querschnittsbelange in einem Softwaresystem zentral in einem Aspekt zu behandeln. Das obige Problem der Transaktionsbegleitung ist genau ein solcher Querschnittsbelang: Jede Methode einer Logikfassade soll als Transaktion ausgeführt werden. Die obige Lösung mit Implementierung der Methode doAction in einer anonymen Unterklasse von Transaction ist zwar technisch redundanzfrei, aber mit viel Schreibaufwand verbunden. Die Lösung mit AspectJ (2001) in Listing 2 muss nur einmal für das gesamte System notiert werden und ist damit hoch redundanzfrei. Schlüsselwörter zusätzlich zu Java sind hier aspect, pointcut, execution, around und proceed.

Listing 2
aspect TransactionAspect {
    pointcut executeAnyFacadeMethod(LgFacade lgFacade): 
        execution(public * *(..)) && this(lgFacade);
        
    Object around(LgFacade lgFacade): executeAnyFacadeMethod(lgFacade) {
        Connection con = DatabaseUtil.getConnection();
        try{
            final Object result = proceed(lgFacade);
            con.commit();
            return result;
        }catch(Exception ex){
            report(ex);
            con.rollback();
        }
    }
}

Der Pointcut executeAnyFacadeMethod fängt jede Ausführung einer Methode von Objekten des Typs LgFacade ab. Der around-Advice umgibt an der durch proceed markierten Stelle die abgefangenen Methodenausführungen und bewirkt dadurch die vereinheitlichte Transaktionsbegleitung. Diese Lösung ist nicht nur technisch, sondern auch schreibmäßig redundanzfrei. Der Einsatz von AspectJ kann in Java-Projekten auf Anhieb enorme Redundanzersparnisse bringen.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.