Klassendiagramme—
Modellierung und Implementation
OOPM, Ralf Lämmel
Eigentlich ein
Verkehrsschild -- schaut
aber wie UML aus!?
Motivation: Wir wollen die Struktur für eine Anwendung im Personalwesen modellieren.
MSFT
VB C#
HR Dev
Anders Erik
Paul
Firma
Abteilungen Unter-
abteilungen
Angestellte
Ein Klassendiagramm
für eine Anwendung im Personalwesen
Beziehungen im OO Paradigma
Auf Objektebene
Verknüpfungen zwischen Objekten Auf Typebene (Klassen / Schnittstellen)
Generalisierung
Eine Klasse spezialisiert eine andere Klasse.
Ein Schnittstelle spezialisiert eine andere Schnittstelle.
Eine Klasse realisiert eine Schnittstelle.
Mehrfachvererbung Assoziationen
Modellierung von Verknüpfungen auf Typebene
Fahrplan
Relevante Sprachmittel für Klassenbeziehungen in Java UML-Notation für Beziehungen in Klassendiagrammen Abwägung von Vererbung vs. Komposition
Abbildung von UML’s Beziehungen auf Java
Ein Beispiel
Eine Klasse für Zähler (Counter) Zustand
Zählerstand Verhalten
Zähler erhöhen (“step”) Zählen ablesen (“read”)
Zähler zurücksetzen (“reset”)
Siehe Counter Chrestomathie
https://github.com/rlaemmel/nopetal
* A simple class of counters */
public class BasicCounter {
/** Private state of the counter */
private int count = 0;
/** Increment the counter */
public void step() { count++; }
/** Return the value of the counter */
public int read() { return count; } /** Reset the counter to zero */
public void reset() { count = 0; }
Objektverwendung
BasicCounter mycounter = new BasicCounter();
mycounter.step();
mycounter.step();
mycounter.step();
System.out.println(mycounter.read());
Ausgabe: 3
Veränderte Anforderung
(Realisierung mit Klassenvererbung)
Zähler mit Grenze (“limit”) Zustand
Limit
Modifiziertes Verhalten
Zähler erhöhen (“step”)
/**
* A counter whose count is limited */
public class LimitedCounter extends BasicCounter { private int limit;
/** Construct a limited counter from the limit */
public LimitedCounter(int i) { limit = i;
}
/** Stop stepping when the limit is reached */
public void step() { if (read() < limit) super.step();
}
}
Aufruf der Implementation
der Oberklasse
Veränderte Anforderung
(Realisierung mit Klassenvererbung)
Zähler mit Erinnerung (“recall”) Zustand
Geretteter Wert (“snap”) Zusätzliches Verhalten
Retten des Wertes (“mark”)
/**
* A counter with recall */
public class RecallCounter extends BasicCounter { private int snap = read();
/** Store the counter state until later */
public void mark() { snap = read();
}
/** Reset the counter for recall */
public void recall() { count = snap;
} }
Oops!
Unzulässiger Zugriff auf “count”!
* A simple class of counters */
public class BasicCounter {
/** Protected state of the counter */
protected int count = 0;
/** Increment the counter */
public void step() { count++; }
/** Return the value of the counter */
public int read() { return count; } /** Reset the counter to zero */
public void reset() { count = 0; }
Gewährung von Zugriff für
Unterklassen
Beschränkung der Veerbung
Finale Klasse
Kann nicht spezialisiert werden Beschränkt Typsubstitution
Finale Methode
Kann nicht überschrieben werden
Beschränkt Verhaltensänderungen
* A counter that cannot be extended any further */
public final class FinalCounter extends BasicCounter { }
Variation im OO Entwurf
Identifikation einer Schnittstelle für Zähler Abstraktion von jeglicher Implementation Warum?
Lässt die Oberklasse offen
Nicht so wichtig für triviale Zähler!
Interessant für komplexere Datenstrukturen
* The shared interface of counters */
public interface Counter {
/** Increment the counter */
void step();
/** Return the value of the counter */
int read();
/** Reset the counter to zero */
void reset();
}
/**
* A simple class of counters */
public class BasicCounter implements Counter { // Private state of the counter
protected int count = 0;
/** Increment the counter */
public void step() { count++; }
/** Return the value of the counter */
public int read() { return count; } /** Reset the counter to zero */
public void reset() { count = 0; } }
Wieder Zähler mit Grenze
mit Wiederverwendung aber ohne Vererbung
Benutzung von Assoziation
Verknüpfung zu Zähler ohne Grenze Delegation von Nachrichten
Warum?
Variation
im OO Entwurf
/**
* A counter whose count is limited */
public class LimitedCounter implements Counter { private BasicCounter inner = new BasicCounter();
private int limit;
/** Construct a limited counter from the limit */
public LimitedCounter(int i) { limit = i; } /** Return the value of the counter */
public int read() { return inner.read(); } /** Reset the counter to zero */
public void reset() { inner.reset(); }
/** Stop stepping when the limit is reached */
public void step() {
if (read() < limit) inner.step();
}
Delegation
Delegation
Wieder Zähler mit Erinnerung mit Wiederverwendung aber ohne Vererbung
Benutzung von Assoziation
Variation
im OO Entwurf
public class RecallCounter implements Counter {
private BasicCounter inner = new BasicCounter();
private int snap = inner.read();
/** Increment the counter */
public void step() { inner.step(); } /** Return the value of the counter */
public int read() { return inner.read(); } /** Reset the counter to zero */
public void reset() { inner.reset(); }
/** Store the counter state until later */
public void mark() { snap = read(); } /** Reset the counter for recall */
public void recall() { inner.count = snap; } }
Oops!
Unzulässiger Zugriff auf “count”! Oops!
* A simple class of counters */
public class BasicCounter implements Counter { // Package-private state of the counter
/* default */ int count = 0;
/** Increment the counter */
public void step() { count++; }
/** Return the value of the counter */
public int read() { return count; } /** Reset the counter to zero */
public void reset() { count = 0; }
Gewährung von Zugriff für
Klassen im
Package
Zugriffskontrollen
public
Zugriff darf von überall erfolgen private
Zugriff nur innerhalb der Klasse protected
Zugriff in der Klasse und Unterklassen
“default”
Zugriff überall innerhalb des Packages
Synonyme:
“friendly” und
“package
private”
Beziehungen im OO Paradigma
Auf Objektebene
Verknüpfungen zwischen Objekten Auf Typebene (Klassen / Schnittstellen)
Generalisierung
Eine Klasse spezialisiert eine andere Klasse.
Ein Schnittstelle spezialisiert eine andere Schnittstelle.
Eine Klasse realisiert eine Schnittstelle.
Mehrfachvererbung
Einfach- & Mehrfachvererbung
Erbt eine Klasse nur von einer anderen Klasse, so spricht man von Einfachvererbung. Sind es
mehrere Klassen, von denen geerbt wird, so
bezeichnet man dies mit Mehrfachvererbung.
Mehrfachvererbung
Zulässig in UML und C++ u.a.
Unklarheiten / Probleme / Risiken Direkte Konflikte (siehe payMeal) Indirekte Konflikte (siehe name) Mehrfachvererbung in Java
Nicht zulässig für Klassen
Zulässig für Schnittstellen
Mehrfachvererbung in UML
(ein weiteres Beispiel)
Eine Klasse kann mehrere
Schnittstellen implementieren.
Beziehungen im OO Paradigma
Auf Objektebene
Verknüpfungen zwischen Objekten Auf Typebene (Klassen / Schnittstellen)
Generalisierung
Eine Klasse spezialisiert eine andere Klasse.
Ein Schnittstelle spezialisiert eine andere Schnittstelle.
Eine Klasse realisiert eine Schnittstelle.
Mehrfachvererbung Assoziationen
Modellierung von Verknüpfungen auf Typebene
Assoziationen
Allgemeine Assoziation Teil-Ganzes Beziehungen
Aggregation
Komposition
Allgemeine Assoziationen
Zwei Klassen gehen eine Beziehung ein.
Modelliert Verbindungen zwischen Instanzen Multiplizität der Verbindung ist festzulegen
Gerichtete Verbindungen sind nur einseitig navigierbar Beispiel
Ein Konto hat einen (1) zugeordneten Kunden.
Ein Kunde besitzt beliebig viele (0..*) Konten.
Teile-Ganzes-Beziehungen
Aggregation
Alle Multiplizitäten sind zulässig
Unausgefüllte Raute am Ende des Ganzen Komposition
Stärkere Form der Aggegration
Multiplizitäten 1 und 0..1 zulässig für das Ganze Ausgefüllte Raute am Ende des Ganzen
Beispiel
Eine Abbildung besteht aus beliebig vielen Formen.
Siehe Shapes Chrestomathie
https://github.com/rlaemmel/nopetal
Unterscheidung von
Aggregation und Komposition
Betrachte die Beziehung zwischen Klassen für Fahrzeuge und Motoren. Ein Motor ist definitiv ein Teil des Fahrzeugs.
Nehmen wir weiterhin an, dass die Klassen technische Entwürfe modellieren. Dann kann ein Motor Teil mehrerer Fahrzeuge sein.
Aggregation ist adäquat.
Komposition wäre stattdessen adäquat, wenn die Klassen
physische Fahrzeuge (und deren Komposition aus echten
Teilen) modellieren. Jedes Teil kann dann nur einmal
Abwägung zwischen
Generalisierung und Assoziation
Kombination aus
Generalisierung und Assoziation
Java-Implementation von UML-Assoziationen
Variationen
Gerichtet und ungerichtet
Multiplizitäten 0..1, 1, 0..*, 1..*
Allgemein vs. Aggregation (Komposition)
(Assoziationsklassen (Assoziationen mit Attributen)) (Mehrfachgeneralisierungen (für Klassen))
Wir werden die (wenigen) speziellen Herausforderungen von
https://svn.code.sf.net/p/developers/code/repository/
oopm/eclipse/chrestomathy/oo/assoc/
Modell
Jede Person hat möglicherweise eine Residenz.
Jede Residenz beherbergt beliebig viele Personen.
Navigation ist nur von Person nach Residenz vorgesehen.
Implementation
Klasse Person hat Feld vom Typ Residence.
Das Feld darf legal den Wert “null” annehmen.
“0..1”
/**
* A person with an optional residence */
public class Person {
private Residence residence;
public Residence getResidence() { return residence;
}
public void setResidence(Residence residence) { this.residence = residence;
} }
Auslassungen der
Einfachheit halber: Attribute für Name, Straße, o.ä. sowie
Konstruktoren zur
Initialisierung sowie Getter und Setter für den Zugriff
auf die ausgelassenen
Attribute.
Objektverwendung
Residence r1 = new Residence("Bismarckstr. 37");
Residence r2 = new Residence("Bahnhofplatz 42");
Person p1 = new Person("Hans");
p1.setResidence(r1);
p1.setResidence(r2);
System.out.println(p1.getResidence().getStreet());
Ausgabe:
Bahnhofplatz 42
Modell
Jede Person hat genau eine Residenz.
Sonst wie vorher.
Implementation
Konstruktion einer Person benötigt eine Residenz.
Parameter des Konstruktors
Erzeugen des Residenzobjektes
“1”
Jede Person hat genau eine Residenz.
Überprüfung des Parameterkonstruktors
public class Person {
private Residence residence;
/** Construction requires a residence */
public Person(Residence residence) { if (residence==null)
throw new IllegalArgumentException();
setResidence(residence);
} ...
}
Vordefinierte
Ausnahmeklasse
Ableitungsposition von IllegalArgumentException
Heute: Erwähnung von Ausnahmen
Modell
Jede Person hat beliebig viele Residenzen.
Sonst wie vorher.
Implementation
Das Feld für die Residenzen ist von einem Container-Typ.
“0..*”
Jede Person hat beliebig viele Residenzen.
Verwendung eines Containers
public class Person {
private List<Residence> residences =
new LinkedList<Residence>();
...
}
Objektverwendung
Residence r1 = new Residence("Bismarckstr. 37");
Residence r2 = new Residence("Bahnhofplatz 42");
Person p1 = new Person("Hans");
p1.getResidences().add(r1);
p1.getResidences().add(r2);
for (Residence r : p1.getResidences()) System.out.println(r.getStreet());
Ausgabe:
Bismarckstr. 37
Bahnhofplatz 42
Modell
Jede Person hat beliebig viele Residenzen.
Die Navigation kann in beide Richtungen erfolgen.
Implementation
Wir brauchen Attribute für Verknüpfungen auf beiden Seiten.
Die Attribute sind von einem Container-Typ.
add/remove-Operation werden beidseitig angewandt.
“0..*”
“ungerichtet” und
Jede Person hat beliebig viele Residenzen.
Verwendung eines Containertyps.
public class Person {
??? List<Residence> residences;
...
}
public class Residence {
??? List< Person> persons;
...
}