Agile Modellierung mit
UML
Loading

5.5 Übersetzung von Sequenzdiagrammen

Ein Sequenzdiagramm ist in seiner Natur exemplarisch. Es zeigt einen einzelnen möglichen Ablauf eines Systems, das typischerweise abhängig von der aktuellen Objektstruktur, den Inhalten der Attribute und der Systemumgebung alternative Abläufe erlaubt. Die Beschreibung eines exemplarischen Ablaufs ist für die konstruktive Codegenerierung nur schlecht geeignet. Die einzige Möglichkeit, einen exemplarischen Ablauf konstruktiv einzusetzen, ergibt sich, wenn eine damit modellierte Methode nur einen Ablauf besitzt. Sie darf dann keine Verzweigungen des Kontrollflusses oder Iterationen beinhalten. Eine derartig einfache Struktur haben typischerweise Testtreiber und Testbeobachtungen.

Da Sequenzdiagramme jedoch vor allem für die Modellierung von Tests eingesetzt werden, ist eine Codegenerierung für die Prüfung von Testabläufen sinnvoll.

5.5.1 Sequenzdiagramm als Testtreiber

Abbildung 5.32 enthält ein typisches Sequenzdiagramm, von dem für das Objekt t eine Methode generiert werden soll, die die mit dem Stereotyp trigger markierten Aufrufe durchführt.


Abbildung 5.32: Sequenzdiagramm mit Treiber

Die zu generierende Methode benötigt einen Namen, der aus dem Diagrammnamen extrahiert werden kann. In Kombination mit einem standardmäßig festgelegten Präfix runSD ergibt sich damit der in der Klasse Class generierte Methodenname runSDTreiber in der Klasse.

Die Signatur dieser Methode ist wie bei der konstruktiven Umsetzung von Objektdiagrammen geprägt durch die freien Variablen des Sequenzdiagramms. Im Beispiel werden daher zumindest die beiden anderen Objekte a und b als Parameter eingesetzt. Freie Variablen, die zuerst als Argumente eines trigger-Aufrufs auftreten, werden ebenfalls als Parameter aufgenommen. Demgegenüber werden freie Variablen, die in Returns das erste Mal verwendet werden, durch diesen Return-Wert belegt. Aufrufe und Returns zwischen anderen Objekten des Sequenzdiagramms sowie Aufrufe an Treiber-Objekte werden bei der Codegenerierung für die angegebene Methode ignoriert. Als Ergebnis entsteht der in Abbildung 5.33 angegebene Code.

       Java   
   
 class Class { ...
  public void runSDTreiber(A a, B b, Type2 args2) {
      Type value = a.m1();
      a.m2(args2);
      b.m3();
  }
}
Abbildung 5.33: Aus einem Sequenzdiagramm generierter Treiber

Die Typen der im Sequenzdiagramm angegebenen Variablen können, soweit nicht aus dem Sequenzdiagramm ersichtlich, aus dem Kontext, also zum Beispiel der Signatur der Klassen A und B, bestimmt werden. Als Erweiterung könnte es von Interesse sein, zwischendurch und am Ende zusätzliche Aktionen in Form von Java-Code anzugeben, die zum Beispiel ein explizites Return-Ergebnis der Methode berechnen. Für den Einsatz als Testtreiber ist allerdings die angegebene Form der Codegenerierung ausreichend. Eine Vereinfachung entsteht zum Beispiel, wenn die Variable a bereits als Attribut der Klasse Class deklariert ist. Dann wird auf die Verwendung des Parameters a verzichtet.

Das Verfahren zur konstruktiven Umsetzung von Teilen eines Sequenzdiagramms kann auch eingesetzt werden, wenn der Methodenaufruf der generierten Methode im Sequenzdiagramm selbst angegeben ist. Abbildung 5.34 beschreibt ein Dummy-Objekt, das eine einfache Implementierung der Methode foo() benötigt. Diese wird nach demselben Verfahren generiert und steht damit für den in Abbildung 5.34 angegebenen Test zur Verfügung. Das Zielobjekt b der durch foo() aufzurufenden Methode ist als Attribut in der Klasse Dummy festgelegt. Die Besetzung der Objektstruktur wird typischerweise durch ein Objektdiagramm vorgenommen.


Abbildung 5.34: Sequenzdiagramm mit Treiber und Dummy

5.5.2 Sequenzdiagramm als Prädikat

Ein Sequenzdiagramm beschreibt genau wie ein Objektdiagramm eine exemplarische Eigenschaft des Systems. Im Gegensatz zu einem Objektdiagramm kann diese aber nicht an einem Snapshot eines Systems, sondern muss während eines Systemablaufs geprüft werden. Um die während eines Ablaufs auftretenden Interaktionen prüfen zu können, ist eine entsprechende Instrumentierung des Codes notwendig. Dabei muss zu Beginn und am Ende jeder beobachteten Methode eine Mitteilung über deren Aufruf beziehungsweise Terminierung erfolgen. Dabei ist auch die anormale Terminierung durch eine Exception zu protokollieren. Dies kann innerhalb der instrumentierten Methode, aber auch durch das Adapter-Entwurfsmuster [GHJV94] erfolgen. Ein solcher Adapter kann zum Beispiel durch Redefinition der Methode in einer Unterklasse gebildet werden. Das Prinzip für die Klasse A aus Abbildung 5.34 ist in Abbildung 5.35 dargestellt.

       Java   
   
 class Ainstrumented extends A { ...
  public Type method() {
    // Protokolliere Methodenaufruf (Objekt, Methode, leere Argumentliste)
    SDlog.call(this, "method", new Object[] {});
    Type result;
    try{
      // Eigentlicher Aufruf
      result = super.method();
    catch (Exception ex) {
        // Protokolliere Exception
        SDlog.exceptionReturn(this, "method", ex);
        throw ex;
    }
    // Protokolliere Return + Ergebnis
    SDlog.normalReturn(this, "method", result);
    return result;
  }
}
Abbildung 5.35: Adapter zur Codeinstrumentierung

Es ist allerdings in der Praxis sinnvoll, diese Informationen statt an ein globales Objekt SDlog zu übergeben, aus dem Aufrufkeller über die Virtual Machine auszulesen.

Der Algorithmus zur Erkennung, ob ein Sequenzdiagramm in der angegebenen Form abgearbeitet wurde, basiert auf der in Abbildung 6.14, Band 1 angegebenen Form zur Interpretation eines Sequenzdiagramms als regulärer Ausdruck. Der reguläre Ausdruck wird zunächst in einem nichtdeterministischen endlichen Automaten.15

Im Sequenzdiagramm können prototypische Objekte auftreten, für die zunächst keine Zuordnung zu echten Objekten existiert. Diese Zuordnung findet bei der jeweils ersten Interaktion mit einem passenden Objekt statt. Deshalb werden die Zustände des nichtdeterministischen Automaten um die Konfiguration dieser Objekte sowie weiterer freier Variablen erweitert. Deshalb kann ein Automat mehrfach denselben Zustand mit verschiedenen Objektkonfigurationen einnehmen.

Nach jeder Interaktion werden eventuell im Sequenzdiagramm nachfolgende OCL-Bedingungen geprüft und die Konfigurationen, welche die Bedingung nicht erfüllen, entfernt. Dies kann im Automaten durch eine zusätzliche Transition dargestellt werden, die keine Interaktion verarbeitet, aber eine Vorbedingung besitzt. Beginnend mit einer Konfiguration im Startzustand wird unter Umständen eine leere Menge von gültigen Konfigurationen erreicht.

Ein Sequenzdiagramm gilt als erfüllt, wenn nach der Testdurchführung der Endzustand in den erreichten Konfigurationen enthalten ist. Als Nebenerzeugnis der Prüfung entsteht dabei auch die Belegung der freien Variablen und der prototypischen Objekte.

Die verschiedenen durch den Stereotyp match wählbaren Semantiken für Sequenzdiagramme werden durch die in Abbildung 6.14, Band 1 beschriebene Beschränkung von Interaktionen, die ignoriert werden dürfen, umgesetzt.

Der beschriebene Algorithmus kann anhand des in Abbildung 5.36 beschriebenen Sequenzdiagramms illustriert werden. Dabei wird von konkreten Werten der Parameter abstrahiert. Diese können genauso behandelt werden, wie die OCL-Bedingungen, also zusätzlich geprüft werden. Abbildung 5.36 beinhaltet auch den Automaten, der zur Erkennung des Sequenzdiagramms verwendet wird.


Abbildung 5.36: Erkennender Automat aus einem Sequenzdiagramm

Die übliche Konstruktion, um den Automaten deterministisch zu machen und zu minimieren [HU90], kann aufgrund der Konfigurationen, die Auswirkungen auf die Schaltbereitschaft weiterer Transitionen und die Evaluierung der OCL-Bedingungen haben, im Allgemeinen nicht durchgeführt werden. Dies wäre zum Beispiel möglich, wenn die im Sequenzdiagramm dargestellten Objekte im Voraus auf echte Objekte zugeordnet werden könnten. Andererseits hat zum Beispiel ein Automat, der aus einem mit dem Stereotyp match:complete markierten Sequenzdiagramm entsteht, keine Schleifen und ist damit bereits deterministisch.

Ein Sequenzdiagramm, das bereits teilweise zur konstruktiven Generierung von Treibern benutzt wurde, kann zusätzlich zu einer Prüfung verwendet werden. Dass dabei auch der Testtreiber instrumentiert wird, erzeugt nur wenig Zusatzaufwand, ist aber notwendig, um die Reihenfolge der auftretenden Nachrichten und die OCL-Bedingungen prüfen zu können.


Bernhard Rumpe. Agile Modellierung mit UML. Springer 2012