Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Java 2 von Friedrich Esser
Designmuster und Zertifizierungswissen
Zum Katalog
gp Kapitel 4 Modellierung und UML
  gp 4.1 Generalisierung und Spezialisierung
  gp 4.2 Klassen-Beziehungen in der UML
  gp 4.3 Objekt-Diagramm
  gp 4.4 Interaktions-Modellierung
  gp 4.5 Zusammenfassung

Kapitel 4 Modellierung und UML

Der objektorientierten Programmierung und ihren Vererbungsmechanismen liegen allgemeine Klassifikations- und Modellierungs-Prinzipien zugrunde.
Neben den Grundlagen werden anhand von Beispielen die wesentlichen Elemente der Klassen-, Objekt- und Sequenz-Diagramme der Unified Modeling Language (UML) vorgestellt.
Gerade für das Design und die Interaktion von Java-Klassen sind UML-Diagramme recht gut geeignet und werden deshalb in den folgenden Kapiteln bei komplizierteren Beispielen benutzt.

Design-Prinzip: wiederverwendbare Komponenten, Module, Systeme

Bisher konnten die Wirkungsweisen von Operatoren und vielen Anweisungen recht gut an einzelnen Methoden und Code-Fragmenten demonstriert werden. Klassen – wenn überhaupt – wurden bisher als isolierte Einheiten betrachtet.

Betrachtet man die Ingenieurwissenschaften allgemein, so sind diese geprägt vom Design wiederverwendbarer Komponenten, integriert zu Modulen und Systemen, die letztendlich dann Produkte ergeben.

In Analogie sind die Klassen im Software-Engineering die elementaren Komponenten, die im Verbund ein Produkt ergeben sollen.

Soweit zur Theorie der »Brave new world«.

Normen

Damit aus einer Fiktion Realität wird, müssen einheitliche Normen und Standards – über Programmiersprachen, Betriebssysteme und Hardware hinweg – definiert und akzeptiert werden.

Aber nicht nur das. Die Ausbildung, weg vom Programmierer als »Codierer« hin zum Software-Ingenieur, muss sich ebenfalls an dieser Idee orientieren. Dagegen steht aber häufig eine Programmiermentalität, die das Rad immer wieder neu erfinden will.

Ein erster wichtiger Schritt sind Modellierungen von Design-Ideen bzw.
-Entscheidungen, die neben einem implementationsunabhängigen Überblick auch die Basis für Design-Techniken und Idiome bilden. Es folgt deshalb ein kurzer Überblick auf Basis der UML.


Galileo Computing

4.1 Generalisierung und Spezialisierung  downtop

Objektorientiertes Paradigma

Die Vererbung war Mitte der Achzigerjahre der Ausgangspunkt für die Ablösung von C durch C++ und letztlich die Ablösung des strukturierten durch das objektorientierte Paradigma.

Ein Paradigma ist ein Denkmuster, eine Sicht oder ein Blickwinkel auf die Umwelt. Ein vertrautes Denkmuster wirft man nicht so einfach weg, d.h., eine Ablösung geschieht nicht abrupt, sondern graduell.

Wird aber umgekehrt ein Paradigma wie das objektorientierte dann en vogue, so meint man, mit ihm plötzlich alle Probleme der betrachteten Umwelt einfach und elegant lösen zu können.

Einige Zeit und kritische Geister werden benötigt, dies dann zu widerlegen. Der Prozess ist allerdings zyklisch.


Galileo Computing

4.1.1 Klassifikation: Denken in Hierarchien  downtop

Klassifikation

Ausgangspunkt der Objekt-Orientierung ist die Klassifikation. Für Biologen ist dies die natürliche Arbeitsweise. Generelles wird vom Speziellen separiert, um dies, wenn möglich, in Kategorien hierarchisch zu organisieren.

Hat man generelle Merkmale und Strukturen erkannt, kann man spezielle daraus ableiten. Es entsteht eine Hierarchie, die, grafisch betrachtet, häufig einem (umgekehrten) Baum mit einer Wurzel (oberstes Element) entspricht.


Abbildung
Abbildung 4.1   Klassifikation, d.h. Ableiten vom Generellen zum Speziellen

Diskriminator

Bei der Klassifikation ist der Diskriminator entscheidend. Es ist das Unterscheidungsmerkmal, anhand dessen man die Hierarchie überhaupt bildet.

gp  Ein guter Diskriminator ist erstens nicht einfach zu finden und zweitens selten eindeutig.

Diskriminatoren sind nicht eindeutig

Man kann sich also über Diskriminatoren trefflich streiten. Häufig sind sie auch nicht so richtig klar bzw. ändern ihre Bedeutung von (Hierarchie-) Stufe zu Stufe.

Als Lernobjekte bzw. Beispiele sind besonders die Hierarchien interessant, bei denen der Diskriminator innerhalb der Stufe oder von einer Stufe zur anderen wechselt, ohne explizit angegeben zu werden.

Ein sehr beliebtes Beispiel ist die Generalisierung bzw. Spezialisierung von Fahrzeugen (siehe Abb. 4.2).


Abbildung  

Wechselnde
Diskriminatoren

Abbildung 4.2   Fahrzeug-Hierarchie mit wechselnden Diskriminatoren

Die Diskriminatoren Di der drei Spezialisierungsstufen der Fahrzeug-Hierarchie sind dann:

gp  D1: Primärer Transport von Gütern vs. Personen (wobei bei Personentransport nach Anzahl der Räder unterschieden wird)
gp  D2: Karosserieform in Kombination mit Personen-/Sitzanzahl
gp  D3: Hersteller

Das in Abb. 4.2 dargestellte Beispiel kann natürlich durch Einfügen von Fahrzeugvarianten wie Van, Pickup, Chopper oder weiterer Diskriminatoren wie Schienenbindung, Antriebsart (Benziner, Diesel etc.) beliebig kompliziert gestaltet werden.

Baum vs.
azyklischer Graph

Bei konkurrierenden Diskriminatoren entartet ein Baum dann häufig in einen azyklischen Graphen (ohne Kreise), was sofort Auswirkungen auf die Programmiersprache hat.

Icon

Doch egal welchen Diskriminator man wählt, allen Spezialisierungen sollte die folgende Beziehungsart gemeinsam sein.

Design-Prinzip:
Is-A

Is-A-Beziehung: Ein Objekt der spezielleren Kategorie (Klasse) ist aufgrund des Diskriminators ein besonderer Repräsentant der generelleren Kategorie (Klasse).


extends:
Is-A in Java

In Java wird eine Spezialisierung durch das Schlüsselwort extends ausgedrückt, genauer:

      class SpecialClass extends GeneralClass {...}
      class PKW extends Landfahrzeug {...}
      class Coupe extends PKW {...}
      ...

Icon

Aus der Is-A-Beziehung leitet sich das für objektorientierte Sprachen äußerst wichtige »Stellvertreter«-Prinzip ab.

Substitutions-Regel

Substitutions-Regel: Ein Objekt der spezielleren Kategorie (Klasse) kann jederzeit anstelle eines Objekts der generelleren Kategorie (Klasse) stehen (wobei die Umkehrung nicht gilt!).


Is-A und Substitution findet man in alltäglichen Situationen:

Bucht man einen Mietwagen einer Kategorie, so steht es dem Verleihunternehmen frei, wie es diese Buchung konkret spezialisiert. Bucht man umgekehrt ein Fahrzeug eines speziellen Herstellers (Porsche Cabrio), so ist man über eine Limousine der gleichen Kategorie eines anderen Herstellers nicht sonderlich erfreut (Verletzung des Substitutions-Prinzips!).

Werden die Kosten für einen Mietwagen (PKW) z.B. in Java durch die Methode

  static double gesamtKosten(PKW 
mw,int anzTage) {
     //...
  }

berechnet, so kann die Methode für jeden speziellen PKW aufgerufen werden:

  kostenCoupe= gesamtKosten(coupe,4);  // Coupe-Objekt
 

Abstraktionsgrad

Im Zusammenhang mit der Generalisierung ist der Grad der Abstraktion von Bedeutung, den jede Stufe in der Hierarchie einfügt.

Icon


Abbildung
Abbildung 4.3   »Merkwürdige« Spezialisierung von Abteilung

Spezialisierung:
Was ist eine signifikante Änderung?

Spätestens bei der letzten Spezialisierung in die diversen Werkstätten merkt man, dass an dieser Hierarchie etwas nicht in Ordnung ist. Untersucht man den zugehörigen Diskriminator, so heißt er schlicht »Werkstattnummer«.

Des Weiteren stellt man fest, dass mit der genannten Spezialisierung nicht Kategorien bzw. Klassen von Objekten beschrieben werden, sondern nur einzelne Objekte.

   class Fertigung {}
   class Werkstatt1 extends Fertigung {}
   class Werkstatt2 extends Fertigung {}
   //...

In Java könnte sinnvoll nur ein Objekt pro Klasse angelegt werden:

   Werkstatt1 w1= new Werkstatt1();
   Werkstatt2 w2= new Werkstatt2();
   //...

Auch hier gibt es eine Orientierungshilfe.

Icon
Klassen

Klassifikations-Prinzip: Eine Klassifikation bzw. Spezialisierung führt potenziell zu Klassen, wobei diese eine Menge von Objekten mit gleichem Verhalten, aber unterschiedlichen Eigenschaftswerten enthalten.


Dieses Prinzip hat ein positives bzw. negatives Korollar:

Spezialisierung: Neue(s)
Verhalten/ Eigenschaften

gp  Positiv: Eine neue Spezialisierung verändert das Verhalten bzw. führt neue Eigenschaften ein.
gp  Negativ: Verändert eine neue Spezialisierung nur die Werte von bestehenden Eigenschaften, so hat man keine neue Spezialierung, sondern nur eine Variation in den Werten gefunden (spezielle Objekte).

Aufgrund des (negativen) Korollars sind zumindest die Werkstätten in Abb. 4.3 in eine Klasse Werkstatt zusammenzufassen.

   class Werkstatt {}

Von dieser Klasse können Werkstätten als Objekte erschaffen werden:

   Werkstatt[] werkstatt= new Werkstatt[10];

Betrachtet man das Diagramm in Abb. 4.3 als eine Art Überblick über die Abteilungsstruktur – sprich Organigramm –, so ist die gesamte Spezialisierung hinfällig.

Der Diskriminator ist dann »Abteilungsname«. Man hat wieder eine Variation in der Eigenschaft name, und die Lösung ist dann schlicht und ergreifend eine einzige Klasse Abteilung (siehe auch Abb. 4.10):

  class Abteilung {
    String name;      // "Verkauf", "Einkauf", "Werkstatt5"
    void setName (String bez) { name= bez; }
    //...
  }

Nun sind alle in Abb. 4.3 dargestellten Spezialisierungen Objekte:

  Abteilung verkauf=    new Abteilung();
  Abteilung werkstatt5= new Abteilung();

Icon

Die vorgestellten Regeln und Prinzipien haben weit reichende Bedeutung für die Java-Klassenbildung:

Spezielle Klassen »erben« Methoden und Eigenschaften

gp  Das Verhalten von Klassen-Objekten wird durch die Methoden der Klasse festgelegt, Objekt-Eigenschaften – auch Attribute genannt –durch die Felder der Objekte.

Verhalten = Methode,
Eigenschaften = Felder

gp  Die Substitutions-Regel impliziert, dass alle öffentlich zugänglichen Methoden und Eigenschaften der generellen Klasse bei der speziellen auch vorhanden sein müssen.

Spezialisierung:
Neue bzw. veränderte Methoden bzw. Felder

gp  Nach dem Klassifikations-Prinzip ist eine spezialisierte Klasse nur dann notwendig, wenn die öffentlich zugänglichen Methoden und Eigenschaften der generellen Klasse modifiziert werden müssen.

Galileo Computing

4.1.2 Dynamische Polymorphie  downtop

Der letzte Punkt führt scheinbar zu einem Dilemma. Denn nach der Substitutions-Regel muss die spezialisierte Klasse alle Methoden der generellen übernehmen, andererseits soll sie ihr Verhalten aber signifikant verändern.

Polymorphie – die Vielgestaltigkeit – angewendet auf Methoden bedeutet, dass die spezielle Klasse zwar die Methoden der generellen Klasse übernehmen muss, aber sie passend zum veränderten Verhalten neu implementieren darf.

Icon
Bei Substitution: Aufruf der passenden Methode

Damit wird die Substitutions-Regel nicht nur gewahrt, sondern sogar auf die Methoden ausgedehnt:

gp  Von einem speziellen Objekt, das ein generelles substituiert (z.B. als Argument beim Methodenaufruf), wird automatisch die zugehörige Methode aufgerufen.

Hat z.B. die Coupe-Klasse einen 10-prozentigen Aufschlag, so erwartet man beim Aufruf der Methode gesamtKosten(coupe,4), dass die zum Coupe-Objekt passende Methode getTarif() aufgerufen wird:

class Kategorie { double tarif; }
class PKW {
  Kategorie kat;
  public double getTarif() { return kat.tarif; }
}
class Coupe extends PKW {
  public double getTarif() { return kat.tarif * 1.1; }
}

Galileo Computing

4.2 Klassen-Beziehungen in der UML  downtop

Abbildungen und Diagramme haben den Vorteil, durch eine grafische Darstellung Zusammenhänge zu verdeutlichen, die verbal nur mühsam zu formulieren sind.

UML: Ein
grafischer Werkzeugkasten

Wenn die Art, Bau- und Konstruktionspläne zu zeichnen, vom jeweiligen Architekten bzw. Ingenieur abhängen würde, wäre diese Welt um viele skurrile Gebäude, Maschinen und Abenteuer der Technik reicher.

Die UMLUnified Modeling Language – ist im Software-Engineering ein De-facto-Standard für grafisch orientiertes Design. Sie gleicht einer Werkzeugkiste, die auf Programmiersprachen anpassbare Werkzeuge für die verschiedenen Belange der Design-Phasen enthält.

Wie bei Bauzeichnungen gibt es zwar feststehende Symbole, Normen und Regeln, aber dies bedeutet noch keine logische Vorgehensweise bzw. kein grandioses Design.

Icon

Frei nach »a fool with a tool is still a fool« können UML-Diagramme auch Schwachsinn korrekt dokumentieren.


Diese Einführung ist sehr kurz und beschränkt sich auf die Werkzeuge, die hauptsächlich benötigt werden, d.h. Klassen-, Objekt- und Sequenz-Diagramme.

Alle anderen Diagrammtypen – sofern verwendet – werden hier nicht vorgestellt, sie werden im Kontext erklärt oder sind intuitiv verständlich (siehe z.B. Abb. 3.1).


Galileo Computing

4.2.1 Klasse  downtop

Die einfachste Art, in der UML eine Klasse darzustellen, ist ein Klassensymbol: ein Rechteck, welches den Klassennamen enthält.

Einfache Darstellung von Klassen


Abbildung
Abbildung 4.4   Einfache Darstellung von Klassen in der UML

Sollen Interna der Klasse dargestellt werden, kann das Rechteck je nach Bedarf in zwei oder drei Fächer (compartments) eingeteilt werden (siehe Abb. 4.5).

Klassen mit Fächern (Compartments)


Abbildung
Abbildung 4.5   Klassen-Darstellung mit drei Fächern (compartments)

In der Klassen-Notation treten noch folgende Begriffe auf, deren Semantik auf die Programmiersprache abzubilden ist:

Sichtbarkeit
(Visibility)

gp  Unter Sichtbarkeit versteht man die Zugriffsrestriktionen:
    gp  + für public: alle haben Zugriff,
    gp  # für protected: die Klasse und alle ihre Spezialisierungen haben Zugriff und
    gp  - für private: nur die Klasse hat Zugriff.

Klassen-Attribute und -Operationen

gp  Klassen-Attribute bzw. -Operationen werden durch Unterstreichen gekennzeichnet.

Da Klassen-Attribute nicht zum Objekt, sondern zur Klasse gehören, werden sie von allen Objekten benutzt und enthalten daher objektunabhängige Informationen (siehe rechtes Beispiel in Abb. 4.5).

Diese ausführliche Klassen-Darstellung ist nahezu beliebig variierbar.

Variationen in der Darstellung von Klassen

Sehr häufig werden nur die Operationen dargestellt, die außerhalb der Klasse verwendet werden können (zwei Fächer, ohne Sichtbarkeitsangabe). Bei Operationen werden häufig die Parameternamen und/oder Typenangaben weggelassen.

Wird ausschließlich Java als Implementierungssprache verwendet, werden sogar Attribute und/oder Operationen als Java-Deklarationen geschrieben (was natürlich nicht der UML-Notation entspricht, aber durchaus praktisch sein kann).


Galileo Computing

4.2.2 Generalisierung und Spezialisierung  downtop

Generalisierung und Spezialisierung werden in Form von Oberklassen (Superklassen) und Unterklassen (Subklassen) modelliert.

Eine Unterklasse wird durch einen geschlossenen Pfeil aus der Oberklasse abgeleitet, wobei der Pfeil in Richtung Oberklasse zeigt.

Superklasse – Subklasse


Abbildung
Abbildung 4.6   Superklasse «Person», Subklasse «Mitarbeiter»
Galileo Computing

4.2.3 Klassifikation von Beziehungen  downtop

Auch wenn es dem OO-Paradigma zuwiderläuft, nicht sehr viele Beziehungen zwischen Klassen bzw. Objekten lassen sich in das Generalisierungs-/Spezialisierungs-Schema pressen. Versucht man es trotzdem, rächt sich dies spätestens bei der Implementierung, d.h. bei der Erstellung der Applikation.

In Abb. 4.7 sind neben der Spezialisierung weitere wichtige Beziehungen dargestellt.

Arten von
Beziehungen (nicht in der UML)


Abbildung
Abbildung 4.7   Klassifikation von Beziehungen

Für die Unterscheidung zwischen Ähnlichkeit und Spezialisierung gibt es keine einfachen Kriterien.

Icon

Im Zweifelsfall halte man sich an die einfache Heuristik: »Es liegt Ähnlichkeit vor«.


Die UML – bereits abgestimmt auf Java – differenziert ein wenig anders als oben dargestellt.

Wichtige UML-Beziehungen für Java


Abbildung
Abbildung 4.8   Wichtige UML-Beziehungen für Java

Die Assoziation umfasst Zuordnung/Mitglied, Aggregation und Komposition differenzieren die Teil-von-Beziehung, und die Implementation (Realization) deckt das große Feld der Ähnlichkeit ab.

Anhand von Java-Klassen-Fragmenten sollen die einzelnen Beziehungen kurz vorgestellt werden.


Galileo Computing

4.2.4 Assoziation  downtop

Bei der Assoziation beschränken wir uns hier auf den häufigsten Fall der binären Assoziation. Assoziationen beschreiben Beziehungen wie:

Assoziation:
isRelatedTo, isMemberOf

Student besucht Vorlesung (isRelatedTo eine Beziehung), Student gehört zur Praktikumsgruppe (isMemberOf – eine Gruppen-Beziehung).


Abbildung
Abbildung 4.9   Assoziation (isMemberOf) mit Multiplizität

Beziehungen vs. Relationen

Beziehungen sind mathematisch gesehen Relationen mit entsprechender Semantik. Diese Sicht ist für das Verständnis der Multiplizitäten interessant, die sich auf die Objekte beziehen (siehe Regeln!).

Mathematische Relationen

Sei Student = { s1, ... ,sn } und Praktikum = { p1, ... ,pn }, dann ist die Assoziation mathematisch eine Relation:

isMemberOf = { (s1,p1), (s2,p1), (s2,p2), (s2,p3), (s3,p1), (s3,p3), (s5,p1), ... }

Icon

Für Assoziationen gelten folgende Regeln:

Abhängigkeiten,
Multiplizität, Symbol *

1. Zwischen den beteiligten Klassen bestehen keine essenziellen Abhängigkeiten, d.h., ihre Objekte können unabhängig voneinander erschaffen und zerstört werden.
2. Die Multiplizität min..max an der Beziehung beschreibt die minimale bzw. maximale Anzahl von Objekten, mit der ein Objekt der gegenüberliegenden Klasse verbunden ist. Ist min gleich max, genügt eine Angabe.
3. Der Stern * steht für ein beliebiges oder nicht näher spezifiziertes Maximum. Steht nur ein Stern, ist dies die Abkürzung für 0..*.

Erklärung zu Abb. 4.9: Zwischen Student und Praktika bestehen keine essenziellen Abhängigkeiten (ein Student wird nicht entsorgt, wenn das Praktikum aufgelöst wird!).

Die angegebene Multiplizität 4..18 steht für die Forderung, dass jedes Praktikum aus zumindest vier bis maximal 18 Studenten bestehen sollte. Ein Student kann an einer nicht näher spezifizierten Anzahl von Praktika teilnehmen.

isMemberOf

Die isMemberOf-Beziehung soll den besonderen Set-Charakter der Assoziation hervorheben und hat die Bezeichnung member.

Referenzen: Assoziationen in Java

In Java werden (binäre) Assoziationen am einfachsten durch Referenzen der beteiligten Klassen aufeinander realisiert.

class Student { ... }
class Praktikum {         // nur relevanter Teil
  final int MAXSTUDS= 18;
  int numStud;
  Student[] pStudent;     // hält isMemberOf-Beziehung fest
  boolean joinStudent (Student stud) {    // z.B. 
triviale 
    if (numStud==MAXSTUDS) return false;  // Implementation
    if (pStudent==null) pStudent= new Student[MAXSTUDS];
    pStudent[numStud++]= stud;
    return true;
  }
}
 

Rollen

Ist die Rolle unklar, die ein Objekt bei der Beziehung spielt, kann sie durch Beziehungsnamen konkretisiert werden.

Dies ist immer bei rekursiven Beziehungen (der Klasse mit sich selbst) notwendig (siehe Abb. 4.10).


Rekursive Assoziationen

Abbildung
Abbildung 4.10   Organigramm – rekursiver Aufbau einer Hierarchie

Abb. 4.10 zeigt eine Lösung zu dem Organigramm in Abb. 4.3, welche nicht nur keine Spezialisierung verwendet, sondern den entscheidenden Vorteil hat, dass die Hierarchie nicht begrenzt ist.

Ein Einmannunternehmen kann somit genauso gut dargestellt werden wie ein Konzern.

Rekursive Klassen-Struktur in Java

Die zugehörige Java-Lösung ist natürlich auch rekursiv und damit verblüffend einfach:

 class Abteilung {  // nur relevanter Teil
  Abteilung oAbt;   // Referenz auf Objekt derselben Klasse,
                    // d.h. die übergeordnete Abteilung
  void setOberAbteilung (Abteilung oAbt) {
    this.oAbt= oAbt;                 // zu this siehe 3.5.3
  }
}

Rekursive Strukturen implizieren rekursive Methoden

Die Referenz oAbt zeigt immer auf die übergeordnete Abteilung, wobei nur die »Geschäftsführung« keine mehr hat, d.h., oAbt ist dann null.

Rekursive Klassen-Strukturen ziehen zur Traversierung ihrer Struktur rekursive Algorithmen, d.h. Methoden, nach sich.

Navigation

Assoziationen zeigen die logischen Zusammenhänge zwischen Klassen. Interessant an der Implementation ist aber, ob man von jeder Seite der Beziehung oder nur von einer Seite zur anderen »navigieren« kann.

Gerichtete Pfeile signalisieren
Navigierbarkeit

gp  Ist aufgrund der Implementation nur in eine Richtung ein Zugriff möglich, sollte die Linie durch einen gerichteten Pfeil in diese Richtung ersetzt werden (siehe Abb. 4.11).

Ein Linie ohne Pfeile bedeutet in den weitaus meisten Fällen nicht Navigation in beide Richtungen, sondern nur, dass sie nicht näher spezifiziert ist. Man lässt die Implementation einfach offen.

gp  Soll ein Diagramm die Implementation widerspiegeln, so sollten Linien durch Doppelpfeile ersetzt werden, sofern in beide Richtungen navigiert werden kann (siehe Abb. 4.11).

Abbildung
Abbildung 4.11   Navigierbarkeit in der Implementation
Galileo Computing

4.2.5 Aggregation und Komposition  downtop

Aggregation:
Eine istTeilvon-Beziehung

Ist die Assoziation eine Ganzes-Teile-Beziehung (whole-part-relation), in der ein Objekt der einen Klasse logisch gesehen aus Teilen der anderen Klasse besteht, modelliert man dies als Aggregation oder sogar Komposition.

Komposition: Eine existenzabhängige Aggregation

Der Unterschied zwischen Aggregation und Komposition liegt darin, dass die enthaltenen Objekte existenziell abhängig sind vom Aggregations-Objekt, d.h., wird das Aggregations-Objekt zerstört, sind auch die enthaltenen Objekte nicht mehr existent.

gp  Bei einer Aggregation wird die Verbindungslinie auf der Aggregat-Seite durch eine Raute ergänzt, bei der Komposition ist das Innere der Raute schwarz.

Die Beziehungen im folgenden Diagramm sind an das Collection Framework angelehnt, wobei Map und Entry als Klasse modelliert sind. In der J2SE sind sie Interfaces.


Abbildung
Abbildung 4.12   Map mit Einträgen, Schlüssel und Werten

Map: Container für Schlüssel-Werte-Paare

Unter einer Map versteht man in Java einen Container (Behälter) – in Java generell auch Kollektion genannt –, der Werte (Values) zusammen mit ihren Schlüsseln (Keys) aufnehmen kann. Ein Schlüssel-Werte-Paar heißt dann Entry bzw. Eintrag (siehe Abb. 4.12).

Der Schlüssel muss eindeutig sein, darf also nur einmal in der Map bzw. in allen Einträgen vorkommen (Multiplizität 1–1). Anhand seines Schlüssels kann somit ein Wert-Objekt eindeutig in der Map gefunden werden.

Ein Eintrag gehört nur zu einer Map. Wird die Map zerstört, sind alle Einträge ebenfalls entsorgt.

Die Schlüssel- und Werte-Objekte sind unabhängig von der Map. Sie können durchaus in anderen Maps oder Objekten enthalten sein. Ihre Existenz hängt damit nicht von dem Eintrag oder der Map ab.

Multiplizität 0..1 bei Kompositionen

Objekte können nur in einem Kompositions-Objekt enthalten sein, die Multiplizität ist damit auf der Kompositions-Seite 0..1 oder 1 und wird in der Regel weggelassen.

Für Kompositionen lässt die UML auch eine alternative Darstellung zu (siehe rechtes Diagramm in Abb. 4.13).

Icon
Primitive Typen werden nicht modelliert

Felder mit primitivem Typ haben immer Werte-Semantik und sind damit immer existenziell vom Objekt abhängig. Hier gilt die Regel:

gp  Primitive Attribute eines Objekts sind nicht als Komposition zu modellieren (Beziehungen bestehen nur zwischen Klassen!).

Im folgenden Diagramm (Abb. 4.13) wird ein Top-Level-Container Frame aus dem Package java.awt dargestellt, welches aus einer Titel-, einer Menü-Leiste und vielen Komponenten besteht.10 


Abbildung
Abbildung 4.13   Alternative für Kompositionen am Beispiel Frame

Aggregation und Komposition werden in Java ebenfalls durch Referenzen in den Klassen dargestellt, da sie ja nur spezielle Formen der Assoziation sind (siehe Abb. 4.8).

Man könnte damit zur Tagesordnung übergehen, wenn da nicht die essenzielle Abhängigkeit bei der Komposition und die Forderung bestehen würde, dass die Teil-Objekte nur in einem einzigen Kompositions-Objekt sein dürfen.

Es gibt keine reine Objekt-Komposition in Java

gp  Für Komposition gibt es – mit Ausnahme der Felder vom primitiven Typ – in Java kein direktes sprachliches Äquivalent/Konstrukt.

Das Problem liegt in der Referenz-Semantik von Objekten.

Sobald das Kompositions-Objekt eine Referenz des enthaltenen Objekts nach außen liefert, können beliebig viele Objekte das Teil-Objekt referenzieren. Damit kann es gar nicht mehr essenziell abhängig sein und jederzeit in beliebigen Assoziationen (auch Kompositionen) auftreten.

Komposition durch Programm-Design

gp  Die Kompositions-Problematik ist also nicht trivial und taucht leider in vielen Bereichen auf.

Icon

Komposition muss durch spezielle Methoden, Konventionen oder (im Spezialfall) durch innere Klassen sichergestellt werden.11 

Ein passendes Design-Konzept zur Komposition ist ein

Komposition durch exklusiven Zugriff

exklusiver Zugriff: Das äußere Objekt erschafft das innere und liefert keine Referenz des inneren Objekts nach außen, sondern – sofern notwendig – nur eine Kopie, d.h. einen Klon.


Klon (Clone):
Eine Kopie des Objekts

Ein Klon bzw. Clone ist ein neues Objekt derselben Klasse, wobei die Felder dieselben Werte haben wie die des Original-Objekts.

Beispiel

Komposition in Java am Beispiel Auftrag

Das o.a. Design-Konzept wird anhand des Auftrags in Abb. 4.14 mit minimalistischen (Java-)Mitteln (ohne Cloning, Konstruktoren, Ausnahmebehandlung und Zugriffsschutz) umgesetzt.


Abbildung
Abbildung 4.14   Auftrag mit einzelnen Positionen
class AuftragsPosition {  // Klasse als Datenstruktur
  String artikel;         // wie ein struct in C++
  double preis;
}
class Auftrag {  // nur relevanter Code
  private List apList= new ArrayList();  // Positionen
  void addAuftragPos (String artikel, double preis) 
{
    AuftragsPosition ap= new AuftragsPosition();             ¨
    ap.artikel= artikel; ap.preis= preis;
    apList.add(ap);       // am Listen-Ende einfügen
  }
  AuftragsPosition getAuftragsPosition(int pos) 
{
    if (pos < apList.size()) {
      AuftragsPosition ap= (AuftragsPosition)apList.get(pos);
      AuftragsPosition clone= new AuftragsPosition();        ¦
      clone.artikel= ap.artikel; clone.preis= ap.preis;
      return clone;   // nicht: return ap;                   ¬
    } else return null;
  }
}

Erklärung: Bei ¨ wird eine Auftragsposition als inneres Objekt erschaffen, zu dem äußere Objekte keinen Zugriff haben. Mit ¦ und ¬ wird eine Kopie herausgereicht.

Wird alternativ bei ¬ die Auftragsposition mit return ap; zurückgeliefert, kann mit Hilfe der Referenz ap das Auftragspositions-Objekt auch außerhalb des Auftrags verwendet werden. Damit wäre dies dann keine Komposition mehr, sondern eine Aggregation.


Galileo Computing

4.2.6 Nützliche UML-Erweiterungen  downtop

Da die UML sprachunabhängig ist, muss es möglich sein, durch Erweiterungen (oder Auslassungen) UML-Diagramme an die spezifischen Belange einer Sprache anzupassen.

Zusicherung/Einschränkung

Constraints:
Zusicherungen und Einschränkungen in Diagrammen

Mit Hilfe von Zusicherungen bzw. Einschränkungen (constraints) können wichtige Details – sprachabhängig oder -unabhängig – Diagrammen hinzugefügt werden.

gp  Zusicherungen werden in geschweifte Klammern {...} gefasst.

Nachfolgend Zusicherungen, die häufig verwendet werden:

{abstract}: Klasse hat keine Objekte (siehe auch Abb. 4.15) {disjoint}: Subklassen sind disjunkt, haben keine Objekte gemeinsam {incomplete}: Unvollständigkeit der Darstellung {overlapping}: Subklassen nicht disjunkt {subset}: Assoziation ist Untermenge einer anderen

Zum Beispiel sollten die drei Subklassen, stellvertretend für alle PKW-Hersteller in Abb. 4.2, durch {incomplete} gekennzeichnet werden.

Zusicherungen können aber auch frei gewählte Bedingungen enthalten:

{wert>=10}

Stereotyp

UML-Anpassung an Java mittels Stereotype

Durch Stereotype wird die UML anpassbar. Stereotype erweitern oder variieren vorhandene Modell-Elemente:

gp  Ein Stereotyp ist ein in «..» eingerahmter Begriff, der nicht direkt zum Sprachumfang der UML gehört, aber für die Anwendung, das Modell oder die Programmiersprache notwendig ist.

Für Java bieten sich die in der UML unbekannten Modifikatoren oder der Begriff »Interface« als Stereotyp an.

Beispiele bekannter und in Java verwendeter Stereotype sind:

«constructor», «implements», «interface», «native»

Ein weiteres, interessantes Stereotyp ist «Powertype». Bei der Generalisierung werden die Mengen aller Objekte der Subklassen in der Superklasse zusammengefasst.

«Powertype»-Klassen führen zu Spezialisierungen (Teilmengen)

Damit beschreibt ein Diskriminator verbal eine Teilmenge der Potenzmenge (Powerset)12  der Superklasse. Ein Diskriminator kann so als spezielle «Powertype»-Klasse gezeichnet oder einfach an eine gepunktete Linie geschrieben werden (Abb. 4.15).


Abbildung
Abbildung 4.15   Zwei verschiedene Teile-Spezialisierungen

Die Klasse Teil ist in Abb. 4.15 mit der Zusicherung {abstract} gekennzeichnet. Es gibt keine Objekte von Teil, sondern nur von den Subklassen. Je nach Diskriminator werden zwei Spezialisierungen vorgenommen.

Die Spezialisierung nach Fremd- oder Eigenbezug ist nicht disjunkt, da dies z.B. von der benötigten Menge oder den Kapazitäten der Fertigung abhängt. Die Spezialisierung nach dem Strukturierungsgrad ist disjunkt, beginnend bei unbearbeitetem Material (Roh- oder Hilfsstoffen) bis hin zum Erzeugnis, dem Endprodukt.

Interface

Interface: Definition eines Services

Interfaces (Schnittstellen) werden in der UML mittels des Stereotyps «interface» gekennzeichnet, da sie sprachabhängig sind. In Java gehören Interfaces zum Sprachumfang, in C++ müssen sie mit Hilfe abstrakter Klassen nachgebildet werden.

Fünf wichtige Eigenschaften eines Interfaces in der UML

Die Definition eines Interfaces ist in der UML und Java sehr ähnlich:

1. In der UML ist ein Interface eine benannte Kollektion von (public) Methoden ohne Implementierung, die einen Service bezeichnet, den eine Klasse anderen anbieten kann.
2. Eine Klasse implementiert ein Interface, indem es alle Methoden des Interfaces deklariert und auch implementiert (Notation: gestrichelter Generalisierungspfeil, optional mit Stereotyp «implements»).
3. Eine Klasse kann mehrere Interfaces implementieren.
4. Ein Interface kann spezialisiert, d.h. erweitert werden, wobei das Sub-Interface die Methoden des Super-Interfaces implizit übernimmt.
5. Interfaces können an Assoziationen beteiligt sein, wobei eine Navigation, die vom Interface startet, in der UML nicht erlaubt ist.

Interface: Unterschiede der UML zu Java

Die UML erlaubt keine Attribute in Interfaces, wogegen Java konstante statische (public static final) Felder im Interface erlaubt.

Interfaces verbergen Klassen

Interfaces werden in Java benutzt, um Klassen zu entkoppeln. Klassen benutzen andere Klassen nur über ihr Interface, d.h., Klassen »verbergen« sich hinter ihrem Interface.13 

Dies wird durch zwei Notationen in der UML unterstützt:

gp  Ein Lollipop-Symbol an der Klasse mit Namen des Interfaces

Abbildung
Abbildung 4.16   Lollipop-Notation für Kundenaufträge

Interfaces haben Rollen-Charakter

Eine Assoziations-Linie mit dem Zusatz Rollenname:Interface-Name oder vereinfacht uses:Interface-Name


Abbildung
Abbildung 4.17   Alternative Benutzung in der Form uses:Interface

Interfaces werden ausführlich in Kapitel 6, Interfaces und Pattern, behandelt (siehe insbesondere das Teile-Beispiel in 6.11.1).


Galileo Computing

4.3 Objekt-Diagramm  downtop

Klassen vs. Objekte

Die bisher verwendete Klassen-Notation bzw. die zugehörigen Klassen-Diagramme zeigen nur den Bauplan für Objekte bzw. die Beziehungen zwischen Mengen von Objekten.

In der Interaktions-Modellierung (siehe 4.4) werden aber Objekte statt Klassen verwendet.


Galileo Computing

4.3.1 Objekt-Notation  downtop

Die Objekt-Notationen mit einem zugehörigen Beispiel sind in Abb. 4.18 dargestellt.

Vier Objekt-
Notationen mit Beispielen


Abbildung
Abbildung 4.18   Notationen für Objekte

Je nach Typ des Diagramms, das Objekte verwendet, wird einer der vier in der Abbildung dargestellten Notationen bevorzugt.

Die Angabe eines Objekt-Zustands ist optional, wird aber häufig bei Prozess- oder Aktivitäts-Diagrammen benutzt.


Galileo Computing

4.3.2 Objekt-Diagramme  downtop

Objekt-Diagramm: Momentaufnahme von Klassen-
Beziehungen

Objekt-Diagramme sind Momentaufnahmen von Klassen-Diagrammen. Sie verdeutlichen anhand einzelner Objekte und deren Verbindungen (Links) ein Klassen-Diagramm. Dabei sind die Verbindungen Instanzen einer Assoziation(s-Linie) und haben deshalb auch keine Multiplizität.

In Abb. 4.19 wird ein Klassen-Diagramm eines Unternehmens und in Abb. 4.20 ein zugehöriges Organigramm als Objekt-Diagramm gezeigt.


Abbildung
Abbildung 4.19   Unternehmen mit drei Sparten
Abbildung
Abbildung 4.20   Organigramm als Objekt-Diagramm
Galileo Computing

4.4 Interaktions-Modellierung  downtop

Interaktions-Diagramme: Beschreibung des dynamischen Verhaltens von Objekten

Klassen- wie Objekt-Diagramme sind statische Diagramme. Sie zeigen kein dynamisches Verhalten, d.h. weder den Ablauf von Prozessen, die Interaktion zwischen Objekten aufgrund ihrer Methoden noch das individuelle Verhalten einzelner Objekte.

In der UML gibt es verschiedene Arten von Interaktions-Diagrammen:

gp  Zustands-Diagramm (für einzelne Objekte)
gp  Sequenz-Diagramm (für Interaktion zwischen Objekten)
gp  Kollaborations-Diagramm (für komplexe Interaktionen)

Nachfolgend wird nur das Sequenz-Diagramm vorgestellt, da es u.a. vorzüglich für einfache Interaktionen zwischen verschiedenen Objekten geeignet ist (siehe zu Threads Kapitel 9).


Galileo Computing

4.4.1 Sequenz-Diagramme  downtop

Sequenz-Diagramme visualisieren die Sequenz, in der Objekte miteinander durch Aufruf von Methoden agieren.

Sequenz-Diagramme:
Zeitlicher Ablauf der Nachrichten bzw. Methoden

Die Methodenaufrufe werden auch als »Senden von Nachrichten« (sending messages) bezeichnet, die Resultate der Methoden als »Erhalten von Anworten« (receiving answers).

Die Objekte werden waagerecht angeordnet und die Interaktionen entlang einer Lebenslinie senkrecht unter den Objekten eingetragen (siehe Abb. 4.21).

Notation zu Sequenz-Diagrammen


Abbildung
Abbildung 4.21   Synchrone Objekt-Nachrichten

Darstellung der Programm-
Steuerung

Hat ein Objekt die Steuerung des Programms, z.B. durch Ausführen von main(), so kann man auch einen durchgezogenen Aktivierungsbalken zeichnen, obwohl das Objekt bei einem (synchronen) Aufruf einer Methode warten muss. Dies macht die Zeichnung einfacher.

Beispiel

Zu einem kleinen Code-Fragment soll ein passendes Sequenz-Diagramm gezeichnet werden. Dazu wird eine Klasse Teil angelegt, wobei zur Vereinfachung ein Konstruktor verwendet wird (siehe Abschnitt 5.5):

class Teil { 
  String id;   // Ident des Teils
  String bez;  // Bezeichnung
               // zu Konstruktor: siehe 5. Kapitel
  Teil(String id, String bez) { this.id=id; this.bez=bez; }
}

Vom Java-Code zum Diagramm

Die Klasse Test hat die Programmkontrolle:

public class Test {
  public static void main(String[] args) {
    Teil[] tarr= { new Teil("4-01-001","Pumpe"),
                   new Teil("0-45-070","Lager"),
                   new Teil("3-71-101","Bohrer")};
     Map map= new HashMap();  // eine Map, siehe 
4.2.5
     for (int i=0;i<tarr.length;i++)
       // Teil unter Schlüssel teil.id ablegen
       map.put(tarr[i].id,tarr[i]);
     tarr= null;         // kann zerstört werden
     // Teil mit Schlüssel "0-45-070" holen
     Teil teil= (Teil) map.get("0-45-070");
     System.out.println(teil.id+": "+teil.bez);
     map= null;          // kann zerstört werden
     //...
  }
}

Abbildung
Abbildung 4.22   Sequenz-Diagramm zur Klasse Test

Multi-Objekt: Menge von Objekten, auf die eine Methode ausgeführt wird

Ein Multi-Objekt-Symbol wird hier verwendet um anzuzeigen, dass new() zu einer Menge von (unbenannten) Teilen führt.

Zerstörung von Objekten ist einzig Aufgabe der JVM

Java kennt keine Destruktoren. Die Zerstörungen am Ende der Lebenslinien sind fiktiv, d.h. bleiben der Garbage Collection (Speicherbereinigung) der JVM überlassen (siehe hierzu 5.9.1).


Galileo Computing

4.5 Zusammenfassung  toptop

Generalisierung/Spezialisierung – die Is-A-Beziehung – ist der Ausgangspunkt für die Vererbung in Java. Hieraus leiten sich Klassifikation als ein Design-Prinzip und Substitution als zentrale Regel ab.14 

Mit Hilfe von Vererbung und Polymorphie können generelle Methoden geschrieben werden, die – abhängig vom konkreten Objekt – die passende Operation ausführen.

Die UML ist eine zu Java komplementäre grafische Design-Methodik, die umfangreiche Werkzeuge zur Modellierung bereitstellt.

Anhand von Diagrammen mit zugehörigen kleinen Code-Fragmenten in Java wurden Klassen-, Objekt- und Sequenz-Diagramme vorgestellt.

Klassen/Objekt-Diagramme zeigen die statischen Beziehungen zwischen Klassen, wobei die Assoziation die häufigste und damit die wichtigste Beziehungsform darstellt.

Assoziationen werden in Java durch Referenzen realisiert, wobei sie die Navigationsrichtungen vorgeben. Komposition kann nur durch Design sichergestellt werden, da die Referenz-Semantik in Java nur Aggregation direkt unterstützt.

Von den Interaktions-Diagrammen, die das dynamische Verhalten eines oder mehrerer Objekte beschreiben, wurden die Sequenz-Diagramme vorgestellt, die den zeitlichen Ablauf und die Interaktion der Objekte recht gut darstellen.

Am letzten Code-Beispiel bzw. Diagramm (Abb. 4.22) wird aber auch deutlich, dass Sequenz-Diagramme nur für die Darstellung von einfachen Interaktionen geeignet sind, da die Modellierung recht schnell groß und unübersichtlich wird.






1    Hier ändert auch der Komponenten-Markt zurzeit nicht allzu viel, da er zu sehr an die Sprachen und Betriebssysteme gebunden ist.

2    Das Vererbungsmuster à la Java ergibt einen Baum (mit genau einer Wurzel), im Gegensatz zu C++, welches Mehrfachvererbung mit beliebig vielen Wurzeln zulässt.

3    Potenziell deshalb, weil man natürlich von einer Klasse auch kein oder nur ein Objekt erzeugen kann.

4    Unter Verhalten versteht man die Reaktion auf die Umwelt. Eigenschaften halten die Werte der Beschreibung eines Objekts fest.

5    Korollar: Folgerung aus einem bewiesenen Satz.

6    Generalisierung und Spezialisierung sind synonym.

7    Erklärung siehe weiter unten.

8    Sofern der Konzern eine Stab-Linien-Organisation hat.

9    Zu Kollektionen siehe Kapitel 14

10    Hier ist nicht modelliert, dass Frame selbst auch eine Komponente ist.

11    Vgl. zu dieser Problematik auch Abschnitt 8.4.3 und 9.8.3.

12    Potenzmenge: Menge PM, deren Elemente alle möglichen Teilmengen der betrachteten Menge M sind. Besteht M aus n Elementen, enthält die Potenzmenge PM 2 Elemente.

13    Die Idee ist äquivalent zu der von Microsofts Component Object Model (COM).

14    Der Zusammenfassung folgen keine Testfragen, da sie konzeptionell zur UML und nicht zu Java gehören. Die UML gehört nicht zur Java-Zertifizierung.

  

Perl – Der Einstieg




Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken und speichern. 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.
Die Veröffentlichung der Inhalte oder Teilen davon bedarf der ausdrücklichen schriftlichen Genehmigung von Galileo Press. Falls Sie Interesse daran haben sollten, die Inhalte auf Ihrer Website oder einer CD anzubieten, melden Sie sich bitte bei: stefan.krumbiegel@galileo-press.de


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de