Implementationsdiagramme
In der Einführungsphase hast du Klassendiagramme gezeichnet, um deine Entwürfe festzuhalten. Jetzt wird die Darstellung genauer – und verbindlich.
Entwurf und Implementation
Man unterscheidet zwei Stufen:
- Ein Entwurfsdiagramm entsteht früh im Modellierungsprozess. Es nennt Klassen und Beziehungen, oft ohne Datentypen und ohne Sichtbarkeiten. Es beantwortet die Frage: Woraus besteht das System?
- Ein Implementationsdiagramm ist vollständig. Es nennt zu jedem Attribut den Datentyp, zu jeder Methode die Parameter mit Typ und den Rückgabetyp, und zu jedem Element die Sichtbarkeit. Es beantwortet die Frage: Wie sieht der Quelltext aus?
Aus einem Implementationsdiagramm lässt sich das Klassengerüst ohne Rückfragen schreiben – und umgekehrt.
Weitere Darstellungsformen findest du unter Objektorientierte Modellierung.
Der Aufbau
classDiagram
class Konto {
-String besitzer
-double kontostand
-int NUMMER_LAENGE$
+Konto(String pBesitzer)
+String getBesitzer()
+double getKontostand()
+void zahleEin(double pBetrag)
+boolean hebeAb(double pBetrag)
}
| Zeichen | Bedeutung |
|---|---|
- |
private |
# |
protected |
+ |
public |
| unterstrichen | Klassenattribut oder Klassenmethode (static) |
GROSS_MIT_UNTERSTRICH |
eine Konstante (final) |
Attribute stehen im mittleren Feld in der Form sichtbarkeit typ name, Methoden im unteren Feld in der Form sichtbarkeit rückgabetyp name(typ parameter).
Konstanten
Neu in der Qualifikationsphase: Konstanten. Ein Wert, der sich nie ändert, wird mit final gekennzeichnet.
finalbedeutet: Der Wert kann nach der Zuweisung nicht mehr geändert werden.staticbedeutet: Der Wert gehört zur Klasse, nicht zu einzelnen Objekten. Es gibt ihn genau einmal, unabhängig davon, wie viele Kreise existieren.- Zusammen ergibt das eine Konstante. Sie wird
GROSS_MIT_UNTERSTRICHgeschrieben und über den Klassennamen angesprochen:Kreis.MAX_RADIUS.
Konstanten sind kein Selbstzweck. Sie geben einer Zahl einen Namen – und damit eine Erklärung. if (pRadius > MAX_RADIUS) sagt mehr als if (pRadius > 1000.0).
Aufgabe 1: Vom Diagramm zum Quelltext
Setze das folgende Implementationsdiagramm um. Achte auf jede Angabe: Sichtbarkeiten, Datentypen, Rückgabetypen.
Das Gerüst gibt die Signaturen schon vor – die Attribute, die Konstante und die Rümpfe musst du selbst aus dem Diagramm ableiten.
classDiagram
class Rechteck {
-double breite
-double hoehe
-int MIN_SEITE$
+Rechteck(double pBreite, double pHoehe)
+double getBreite()
+double getHoehe()
+double flaeche()
+double umfang()
+boolean istQuadrat()
#void skaliere(double pFaktor)
}
Zusätzliche Angaben, die im Diagramm nicht stehen können:
MIN_SEITEist eine Konstante mit dem Wert 1.- Der Konstruktor setzt Seiten unterhalb von
MIN_SEITEaufMIN_SEITE. istQuadratlieferttrue, wenn beide Seiten gleich sind.skalieremultipliziert beide Seiten mit dem Faktor, aber nur bei positivem Faktor.
Tipp: Wie erkennt man die Konstante im Diagramm?
MIN_SEITE ist unterstrichen – das bedeutet static. Und der Name in Großbuchstaben mit Unterstrich ist die Konvention für final.
Damit der Test Rechteck.MIN_SEITE lesen kann, muss die Konstante außerdem public sein.
Aufgabe 2: Vom Quelltext zum Diagramm
Zeichne auf Papier das Implementationsdiagramm zur folgenden Klasse. Trage alles ein, was in ein Implementationsdiagramm gehört.
public class Spielfigur {
public static final int MAX_LEBEN = 3;
private String name;
private int leben;
private double x;
private double y;
protected boolean unverwundbar;
public Spielfigur(String pName) {
name = pName;
leben = MAX_LEBEN;
unverwundbar = false;
}
public String getName() {
return name;
}
public int getLeben() {
return leben;
}
public void bewege(double pDx, double pDy) {
x = x + pDx;
y = y + pDy;
}
public boolean erleideSchaden(int pMenge) {
if (unverwundbar) {
return false;
}
leben = leben - pMenge;
return true;
}
private void pruefeLeben() {
if (leben < 0) {
leben = 0;
}
}
}
Auflösung
classDiagram
class Spielfigur {
-String name
-int leben
-double x
-double y
#boolean unverwundbar
-int MAX_LEBEN$
+Spielfigur(String pName)
+String getName()
+int getLeben()
+void bewege(double pDx, double pDy)
+boolean erleideSchaden(int pMenge)
-void pruefeLeben()
}
Häufige Fehler bei dieser Aufgabe:
- Die private Methode
pruefeLebenwird vergessen. Sie gehört ins Diagramm – ein Implementationsdiagramm zeigt alles, nicht nur das Öffentliche. - Bei
unverwundbarwird-statt#eingetragen. - Die Konstante wird nicht unterstrichen.
- Bei
bewegefehlen die Parametertypen.
Aufgabe 3: Beurteilen
Die Klasse Spielfigur hat einen Entwurfsfehler: Die private Methode pruefeLeben wird nirgends aufgerufen.
a) Wo müsste sie aufgerufen werden?
b) Warum ist sie überhaupt private?
c) Nenne einen weiteren Schwachpunkt des Entwurfs.
Auflösung
a) Am Ende von erleideSchaden, bevor true zurückgegeben wird. Sonst kann die Lebenszahl negativ werden.
b) Weil sie eine interne Aufräumarbeit ist. Von außen soll niemand die Lebenszahl korrigieren können – das wäre wieder ein Loch in der Kapselung. Private Methoden sind Hilfsmethoden der Klasse für sich selbst.
c) Mehrere Antworten sind vertretbar:
- Die Attribute
xundyhaben keine Getter – man kann die Position nicht auslesen. unverwundbaristprotected, ohne dass es eine Unterklasse gäbe. Ohne konkreten Grund gehört es aufprivate.- Es gibt keine Möglichkeit, Leben zurückzubekommen.
erleideSchadenprüft nicht auf negative Mengen – damit könnte man sich heilen.
Zusatzaufgabe
Nimm dir das Spiel vor, das du am Ende der Einführungsphase gebaut hast.
a) Zeichne nachträglich das vollständige Implementationsdiagramm – mit allen privaten Methoden und allen Datentypen.
b) Vergleiche es mit dem Entwurfsdiagramm von damals. Was ist beim Programmieren dazugekommen, was hast du weggelassen?
c) Beurteile: Hätte ein genaueres Diagramm dir Arbeit erspart – oder wärst du nur langsamer losgekommen? Begründe.