Agile Modellierung mit
UML
Loading

5.2 Übersetzung von Objektdiagrammen

Die vollständige Beschreibung zur Generierung von Code aus der UML/P würde den Rahmen dieses Buchs sprengen. Deshalb werden in den folgenden Abschnitten einige der interessantesten Aspekte der Transformation weiterer Diagramm- und Textarten in Java-Code erläutert, ohne alle Details zu diskutieren.

Objektdiagramme können auf zwei Arten verwendet werden. Zum einen können Objektdiagramme konstruktiv eingesetzt werden, um damit Objektstrukturen zu erzeugen. Diese Funktionalität kann sowohl im Produktionssystem als auch zur Darstellung von Objektstrukturen, auf denen automatisierte Tests stattfinden sollen, verwendet werden. Zum anderen werden Objektdiagramme als Prädikate eingesetzt, um zu prüfen, ob eine bestimmte Objektstruktur vorhanden ist. In Abschnitt 4.4, Band 1 wurden diese Verwendungsarten bereits vom methodischen Standpunkt aus beleuchtet. Dieser Abschnitt wird deshalb Aspekte der Codegenerierung aus Objektdiagrammen diskutieren. Dabei sei zunächst die in Kapitel 4, Band 1 vorgenommene Integration von Objektdiagrammen und OCL vernachlässigt.

5.2.1 Konstruktiv eingesetzte Objektdiagramme

Die Transformation eines Objektdiagramms in Funktionalität zum konstruktiven Aufbau von Objektstrukturen erfolgt gesteuert durch ein Skript beziehungsweise durch Merkmale, die dem Objektdiagramm angefügt werden. Dabei sind einige Parameter festzulegen, die bei der Codegenerierung wichtig sind:

  1. Die Klasse, der die Methode zur Erzeugung der Objektstruktur zugeordnet wird. Ist im Diagramm ein Objekt eindeutig ausgezeichnet, so kann dies entfallen.
  2. Der Name der zu erzeugenden Methode. Als Default wird setupDiagramName verwendet, wenn dieser Name eindeutig ist.
  3. Die Objekte des Diagramms, die bereits existieren und als Parameter übergeben werden. In der Regel ist dies keines oder eines, das als übergeordnetes Objekt bereits erzeugt wurde.
  4. Treten im Objektdiagramm freie Variablen auf, so werden sie ebenfalls als Parameter für die generierte Methode interpretiert.

Da die Ausgangssituation und die damit jeweils bereits existenten Objekte unterschiedlich sein können, kann es sinnvoll sein, mehrere Methoden aus einem Objektdiagramm zu generieren. Diese können sich durch die Signatur oder, falls dies nicht eindeutig ist, auch durch die Methodennamen unterscheiden.

Die Verwendung freier Variablen und unbesetzter Attribute als Parameter der generierten Funktion erlaubt es, Objektdiagramme wie in Abschnitt 4.2.2, Band 1 diskutiert als Muster mit prototypischen Objekten zu interpretieren, die eine mehrfache Instanziierung mit unterschiedlichen Inhalten erlauben.

Bei der Generierung der setup-Methoden gibt es mehrere Aspekte, die zu beachten sind. Für die Erzeugung eines Objekts ist die Verwendung eines Konstruktors notwendig. Idealerweise sollte ein Konstruktor ohne Parameter zur Verfügung stehen, der nur das leere Objekt erzeugt. Steht ein solcher Konstruktor nicht zur Verfügung, so kann im Testsystem ein solcher generiert werden. Im Produktionssystem muss allerdings auf einen existenten Konstruktor zurückgegriffen werden, der entsprechend ausgezeichnet wurde.

Ein weiteres Problem ist die Besetzung der Attribute entsprechend der im Objektdiagramm vorgegebenen Werte. Dafür sollten geeignete Hilfsfunktionen zur Verfügung stehen oder direkter Zugriff auf die Attribute erfolgen. Die in Abschnitt 5.1.1 diskutierten set-Methoden sind dafür nur partiell geeignet, da sie unter Umständen zusätzliche Funktionalität beinhalten.

Im Prinzip können dafür auch Konstruktoren mit Parametern weiterhelfen, wenn die Zuordnung zwischen dem Parameter und dem zu besetzenden Attribut aus der Konstruktordefinition eindeutig hervorgeht.11

Ein Objektdiagramm kann grundsätzlich unvollständig sein, indem etwa die Klasse eines Objekts nicht angegeben ist oder manchen Attributen kein Wert zugewiesen wurde. Zum einen können unbesetzte Attribute als freie Variable verstanden werden und als Parameter in die generierte Methode aufgenommen werden. Ist dies nicht gewünscht, so sind abhängig von der Art des Einsatzes im Testsystem, zur Simulation oder im Produktionssystem verschiedene Strategien zur Behandlung unbesetzter Attribute möglich. Im Testsystem wird eine Failure-Strategie genutzt: Unbesetzte Attributwerte sollten für den getesteten Systemablauf keine Rolle spielen und der Zugriff darauf mit einem sofortigen Scheitern des Tests reagieren. Entsprechendes Verhalten kann in die get-Funktionen integriert werden. Bei der Simulation ist die bereits in Abschnitt 5.1.2 diskutierte Vorgehensweise sinnvoll, fehlende Attributwerte während des Simulationslaufs interaktiv zu erfragen oder mit Default-Werten zu arbeiten. Bei der Codegenerierung für das Produktionssystem ist schließlich eine vollständige Definition der Objekte im Objektdiagramm Voraussetzung. Dies verhindert Unachtsamkeiten bei der Definition von Objektdiagrammen und gibt so dem Entwickler Zutrauen in die Zuverlässigkeit des modellierten Systems.

Nicht alle im Objektdiagramm formulierbaren Angaben werden bei konstruktiver oder auch der später diskutierten prädikativen Codegenerierung verwendet. Sichtbarkeiten, die Information über Kompositionalität eines Links, Merkmale wie {frozen} und ähnliches mehr bedürfen keiner Umsetzung in den hier diskutierten Code, sondern werden mit den in Klassendiagrammen vorhandenen Informationen abgeglichen oder in Tests eingesetzt.

Ein alternativer, mit Methodenspezifikationen verbundener Ansatz zum konstruktiven Einsatz von Objektdiagrammen ist bereits in Abschnitt 4.4.7, Band 1 diskutiert. Er nutzt ein Objektdiagramm in der Nachbedingung eines Konstruktors beziehungsweise einer Initialisierungsmethode, das nach denselben Prinzipien wie den hier gezeigten in konstruktiven Code umgesetzt werden kann.

5.2.2 Beispiel einer konstruktiven Codegenerierung

Statt nun die Transformationsregeln für jedes Modellelement des Objektdiagramms zu diskutieren sollen diese anhand des in Abbildung 5.11 gezeigten Objektdiagramms beispielhaft erläutert werden. Dieses Diagramm beschreibt einen Ausschnitt der initialen Objektstruktur des Applets im Auktionssystem und ist eingebettet in eine OCL-Methodenspezifikation für die Initialisierungsfunktion init(). Der generierte Code ist in Abbildung 5.12 dargestellt, wobei hier und in den folgenden Beispielen vereinfachend angenommen wird, dass der direkte Zugriff auf die Attribute erst noch transformiert wird:

       OCL  
context WebBidding.init()
let String language = getParameter("language");
    String login    = getParameter("login")
post: OD.WBinit
Abbildung 5.11: Objektdiagramm zur Initialisierung einer Struktur

       Java   
   
 class WebBidding { ...
  public void init() {
    // aus dem let-Konstrukt
    String language = getParameter("language");
    String login    = getParameter("login");
    // aus Objekt this
    status = AppStatus.INITIAL;
    person = null;
    auction = null;
    auctionChooserPanel = null;
    multibiddingPanel = null;
    appletLanguage = language==null ? "German" : language;
 
    // aus Objekt :LoginPanel
    setLoginPanel(new LoginPanel(login==null ? "":login,""));
 
    // aus Objekt :HttpServerProxy
    setHttpServerProxy(
      new HttpServerProxy(
          "https://"+getCodeBase().getHost()+":443/"));
  }
}
Abbildung 5.12: Aus dem Objektdiagramm generierte init()-Funktion

Aus dem zugehörigen (hier nicht wiedergegebenen) Klassendiagramm kann das System ableiten, dass die Links durch die Attribute loginPanel und httpServerProxy in der Klasse WebBidding realisiert werden. Für die Klassen HttpServerProxy und LoginPanel wird ein geeignet parametrisierter Konstruktor verwendet, der die Besetzung der angegebenen Attribute vornimmt.12

5.2.3 Als Prädikate eingesetzte Objektdiagramme

Der Einsatz eines Objektdiagramms als Prädikat, das prüft, ob eine bestimmte Objektstruktur vorliegt und die im Objektdiagramm angegebenen Werte übereinstimmen, wird in eine boolesche Methode transformiert. Ähnlich wie bei der konstruktiven Variante steuern mehrere Parameter den zu erzeugenden Code:

  1. Die Klasse, der die boolesche Methode zugeordnet wird. Ist im Diagramm ein Objekt eindeutig ausgezeichnet, zum Beispiel durch den Namen this:Classname, so kann dies entfallen. Alternativ kann die Methode als statisch definiert und/oder einer Testklasse zugeordnet werden.
  2. Der Name der zu erzeugenden Methode. Ist der Name nicht gegeben, so wird als Default isStructuredAsDiagramName verwendet.
  3. Die Objekte des Diagramms, die als Ausgangsobjekte bereits identifiziert sind und deshalb als Parameter übergeben werden. In der Regel ist dies ein einzelnes Objekt, das als eine Art Master für die Objektstruktur gilt.
  4. Treten im Objektdiagramm freie Variablen auf, so werden sie im Normalfall nicht weiter beachtet. Sollen diese Variablen aber bestimmte Werte annehmen, so werden die Variablen ebenfalls als Parameter für die generierte Methode interpretiert.

Im Gegensatz zur konstruktiven Variante eines Objektdiagramms kann ein prädikativ eingesetztes Diagramm in mehrerer Hinsicht unvollständig sein. Attribute und Attributwerte dürfen ebenso weggelassen werden wie die Klassen der dargestellen Objekte. Auch Eigenschaften mit einer zeitlichen Implikation, wie etwa das Merkmal {frozen} für Links, können nicht in einem Prädikat über einen Zustand geprüft werden. Dazu ist zusätzliche Infrastruktur nötig, die dies entweder konstruktiv sichert, indem keine Methoden zur Modifikation eines Links angeboten werden, oder zur Laufzeit prüft, indem der ursprüngliche Zustand des Links in einer Kopie aufgehoben wird.

In Abschnitt 4.3, Band 1 wurde die Bedeutung eines Objektdiagramms als Prädikat im Kontext der Integration mit OCL-Bedingungen bereits ausführlich diskutiert. Dabei wurde festgestellt, dass ein Objektdiagramm grundsätzlich als OCL-Bedingung dargestellt werden kann. In genau dieser Bedeutung werden prädikative Objektdiagramme in entsprechende boolesche Methoden, die mit dem in Abschnitt 3.4.1, Band 1 eingeführten Stereotyp query markiert sind, übersetzt. Die so generierten Methoden können genau wie die Referenz auf das Objektdiagramm bei Invarianten, Vor- und Nachbedingungen von Methoden und von Transitionen in Statecharts, aber auch in Java-Rümpfen des Produktionscodes eingesetzt werden.

Für die Verwendung im Produktionscode ist jedoch auf die Effizienz der Umsetzung zu achten. Wie in Abschnitt 4.3, Band 1 diskutiert, wirken anonyme Objekte des Objektdiagramms als existenzquantifiziert. In derselben Weise werden benannte Objekte behandelt, die jedoch nicht bereits als Parameter an das boolesche Prädikat übergeben werden. Diese Objekte werden vom Prädikat selbst gesucht, indem die entsprechenden Assoziationen geprüft werden. Die Belegung der freien Objekte erfolgt in einer dem Struktur-Matching der Graph Grammatiken [Roz99EEKR99] analogen Form.

Bei mengenwertigen Assoziationen kann eine derartige Suche von linearer Komplexität sein und sollte deshalb vermieden werden. Möglichkeiten zur Verbesserung der Situation bieten der Einsatz eines Qualifikators bei der Assoziation oder die explizite Übergabe gesuchter Objekte als Parameter, wenn diese aus dem Kontext effizient ermittelt werden können.


Abbildung 5.13: Für ein Prädikat bestimmtes Objektdiagramm

Anhand des aus dem Auktionssystem stammenden und in Abbildung 5.13 dargestellten Objektdiagramms wird die Transformation in ein Prädikat illustriert. Der für den Einsatz im Testsystem generierte Code ist in Abbildung 5.14 dargestellt.

       Java   
   
  
class Auction { ...
  public static boolean isStructedAsKupfer(Auction kupfer912,
                         int status, boolean isInExtension) {
    // Namen festlegen (optimierbar)
    BiddingPolicy bidPol = kupfer912.bidPol;
    TimingPolicy timePol = kupfer912.timePol;
    Money min = bidPol.min;
    Money max = bidPol.max;
    Time start = timePol.start;
    Time finish = timePol.finish;
 
    return
     // Hauptobjekt
     kupfer912.auctionIdent == 912 &&
     kupfer912.title.equals("420t Kupfer") &&
     kupfer912.numberOfBids == 0 &&
 
     // BiddingPolicy
     bidPol instanceof DownwardBiddingPolicy &&
     bidPol.kind == BiddingPolicy.DOWNWARD &&
     bidPol.bidCountMax == BiddingPolicy.UNLIMITED &&
 
     // Money Objekte
     min.amount == 52290000 &&
     min.decimalplaces == 2 &&
     min.currency.equals("$US") &&
     min.full.equals("522.900,00 $US") &&
     max.full.equals("720.000,00 $US") &&
 
     // TimingPolicy
     timePol instanceof ConstantTimingPolicy &&
     timePol.status == status &&
     timePol.isInExtension == isInExtension &&
     timePol.extensionTimeSecs == 180 &&
 
     // Times
     start.timeSec == 953640000 &&
     start.time.equals("13:00:00") &&
     start.date.equals("21. Februar 2000") &&
     finish.timeSec == start.timeSec + 2⋆60⋆60 &&
     finish.time.equals("15:00:00") &&
     finish.date.equals("21. Februar 2000") ;
  }
}
Abbildung 5.14: Aus Objektdiagramm generiertes Prädikat

Nach einer Phase der Belegung von Objektnamen werden alle Attribute geprüft. Dabei dürfen Attributwerte aufeinander Bezug nehmen. Werden statt der im (hier nicht dargestellten) Klassendiagramm angegebenen Typen echte Unterklassen angegeben, so wird geprüft, ob das entsprechende Objekt tatsächlich zu dieser Unterklasse gehört.

Anhand der Länge des in Abbildung 5.14 dargestellten Codes ist ersichtlich, dass eine Darstellung im Objektdiagramm kompakter und übersichtlicher ist, also dem Modellierer insbesondere bei der schnellen Erfassung eines Überblicks und der Suche einzelner Werte Vorteile bringt.

Als Erweiterung beziehungsweise Alternative bei der Codegenerierung kann eine weitere Methode mit dem Namen isExactlyStructuredAsDiagramName generiert werden. Diese Methode kann zusätzlich sichern, dass die Objektstruktur nur die im Diagramm angegebenen Objekte enthält. Das ist besonders bei mehrwertigen und optionalen Assoziationen von Interesse und kann zum Beispiel durch einen geeigneten Stereotyp complete für Objektdiagramme gesteuert werden. Tabelle 5.15 gibt eine knappe Einführung zu diesem Stereotyp.



Stereotyp complete


Modellelement

Objektdiagramm.


Motivation

Die Bedeutung eines Objektdiagramms als Prädikat ist normalerweise so festgelegt, dass die explizit angegebenen Eigenschaften erfüllt sein müssen. Weitere, im Diagramm nicht angegebene Objekte können existieren.


Rahmenbedingung

In einem mit complete markierten Objektdiagramm müssen alle Attribute und Links angegeben sein. Geordnete Assoziationen sind vollständig darzustellen.


Wirkung

Der Stereotyp complete fordert, dass keine weiteren Objekte in der angegebenen Objektstruktur existieren. Das angegebene Objektdiagramm ist also eine vollständige Darstellung der Objektstruktur.

Damit kann die Methode isExactlyStructuredAsDiagramName generiert werden, die diese Vollständigkeit zusätzlich zur Erfüllung der angegebenen Eigenschaften des Objektdiagramms prüft.



Tabelle 5.15.: Stereotyp complete

5.2.4 Objektdiagramm beschreibt Strukturmodifikation

Eine weitere interessante Form des Einsatzes von Objektdiagrammen ergibt sich aus der Kombination beider Einsatzformen. Dabei wird eine existente Objektstruktur auf das Vorhandensein der im Objektdiagramm beschriebenen Objekte geprüft, die fehlenden Objekte generiert und die falsch besetzten Attribute modifiziert. Derartige Methoden erhalten als Namen adaptToDiagramName. Mit diesen Methoden kann eine bereits vorhandene Objektstruktur in Abhängigkeit des aktuell gewünschten Zustands umgebaut werden. Damit lassen sich Objektdiagramme zum Beispiel als Zustandsinvarianten im Statechart oder zur Adaption der jeweiligen Objektstruktur in der Entry-Aktion von Zuständen einsetzen. Wird beispielsweise das in Abbildung 5.11 gegebene Objektdiagramm (ohne Einbettung in die dort stehende OCL-Bedingung) in dieser Form eingesetzt, so wird die in Abbildung 5.16 dargestellte Methode erzeugt.

       Java   
 class WebBidding {
 
  public void adaptToWBinit(String language, String login) {
    // aus Objekt this
    status = AppStatus.INITIAL;
    person = null;
    auction = null;
    auctionChooserPanel = null;
    multibiddingPanel = null;
    appletLanguage = language==null ? "German" : language;
 
    // aus Objekt :LoginPanel
    if(loginPanel != null &&
                loginPanel instanceof LoginPanel) {
      loginPanel.loginField = (login==null) ? "" : login;
      loginPanel.passwordField = "";
    } else {
      setLoginPanel(new LoginPanel(
                login==null ? "" : login, ""));
    }
 
    // aus Objekt :HttpServerProxy
    if(httpServerProxy != null &&
                httpServerProxy instanceof HttpServerProxy) {
      httpServerProxy.secureURL =
                "https://"+getCodeBase().getHost()+":443/";
      httpServerProxy.connectStatus =
                HttpServerProxy.NOT_CONNECTED;
      httpServerProxy.lastConnectionTime = Time.now();
    } else {
      setHttpServerProxy(
        new HttpServerProxy(
                "https://"+getCodeBase().getHost()+":443/"));
    }
}}
Abbildung 5.16: Aus Objektdiagramm generierte Adaptionsmethode

Fehlt ein Objekt oder hat es den falschen Typ, so wird es neu erzeugt. Ist das Objekt bereits vorhanden, so werden seine Attribute in der gewünschten Form modifiziert. Deshalb werden sowohl Konstruktoren, die in diesem Beispiel bereits Attributwerte als Parameter enthalten, als auch set-Methoden zur Attributbesetzung verwendet.

Die Eindeutigkeit des entlang eines Links zu identifizierenden Objekts wie zum Beispiel dem anonymen Objekt :LoginPanel ist nur bei Assoziationen der Kardinalität „1“ oder „0..1“ gegeben. Bei mengenwertigen Assoziationen ist ein Vergleich mit allen vorhandenen Objekten durchzuführen, der auch dessen Attribute einbezieht. Findet sich kein Objekt in der Objektstruktur, das dem im Diagramm angegebenen prototypischen Objekt entspricht, so wird in diesem Fall keines der vorhandenen Objekte angepasst, sondern ein neues Objekt erzeugt. Dadurch vergrößert sich die Menge der Links entsprechend. Der Nachteil dieser Methode ist allerdings, dass die Löschung unerwünschter Objekte nicht dargestellt werden kann. Außerdem kann diese Methode ineffizient werden, wenn zum Beispiel mit dem in Abbildung 3.28 gegebenen Objektdiagramm (ohne die OCL-Bedingung) und der daraus generierten und in Abbildung 5.17 dargestellten Methode hundert Personen einzeln angelegt werden.

       Java/P   
 class Auction { ...
 
  public void adaptToNPersons(Auction test32, int x) {
    // setze Objekt test32
    test32.auctionIdent = 32;
    test32.title = "Testauktion";
 
    // gibt es p:Person?
    Person p = null;
    for(Iterator<Person> ip = participants.iterator();
                                ip.hasNext() && p==null; ) {
      Person pit = ip.next();
      if(pit.personIdent == 1000+x &&
         pit.login == "log" +x &&
         pit.name == "Tester " +x &&
         pit.isActive == (x%2 == 0)) {
             p = pit;
      }
    }
    // p:Person anlegen
    if(p == null) {
      p = new Person(); // Default-Konstruktor
      p.personIdent = 1000+x;
      p.login = "log" +x;
      p.name = "Tester " +x;
      p.isActive = (x    }
  }
}
Abbildung 5.17: Adaptionsmethode mit Suche in -Assoziation

5.2.5 Objektdiagramme und OCL

Ein wesentliches Mittel zur Steigerung der Ausdrucksmächtigkeit von Objektdiagrammen ist die ab Abschnitt 4.3, Band 1 durchgeführte Integration mit der OCL. Die Transformation eines um OCL-Bedingungen erweiterten Objektdiagramms in konstruktiven Code beziehungsweise in ein Prädikat hängt daher von der Umsetzbarkeit der OCL-Bedingungen ab. Die Umsetzung und die Ausführbarkeit von OCL-Bedingungen wurde bereits in Abschnitt 4.1.2 diskutiert.

Zu beachten ist aber auch, dass die in der vorangegangenen Diskussion erwähnte lineare Suchkomplexität für existenzquantifizierte Objekte polynomial ansteigen kann, wenn über mehrere mengenwertige Assoziationen navigiert wird und die so erreichten Objekte über eine OCL-Bedingung verknüpft sind.


Bernhard Rumpe. Agile Modellierung mit UML. Springer 2012