Programmieren in Java - Vorlesung 02: Einfache Klassen

Werbung
Programmieren in Java
Vorlesung 02: Einfache Klassen (Fortsetzung)
Prof. Dr. Peter Thiemann
Albert-Ludwigs-Universität Freiburg, Germany
SS 2015
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
1 / 51
Inhalt
Einfache Klassen
Testen für einfache Klassen
Vermeiden von sinnlosen Objekten
Benennen von Konstanten
Vermeiden von magischen Werten
Zusammengesetzte Klassen
Merkmale
Fahrkarten mischen
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
2 / 51
Einfache Klassen
Zusammenfassung: Arbeiten mit einfachen Klassen
Executive Summary
1. Testen
I
I
Synthetische Tests aus Spezifikation
Analytische Tests aus Fehlerberichten
2. Es sollte nicht möglich sein, sinnlose Objekte zu erzeugen.
I
I
Factory-Methoden
Aufzählungstypen
3. Zur Vermeidung von Missverständnissen und zur Dokumentation
sollte jede Konstante sinnvoll benannt werden.
4. Der Wertebereich eines Felds sollte nicht künstlich beschränkt
werden. (Keine magischen Werte.)
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
3 / 51
Einfache Klassen
Testen für einfache Klassen
Testen für einfache Klassen
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
4 / 51
Einfache Klassen
Testen für einfache Klassen
Testen
I
Ansatz: Unit Testing
I
separate Testsuite für jede Programmkomponente (z.B. Klasse)
I
separate Tests für jede Methode
zwei Arten von Tests
I
I
synthetische Tests abgeleitet von der Spezifikation
I
I
I
Demonstration der normalen Funktion
Randfälle
Tests, die Fehler dokumentieren
I Bug report
→ fehlschlagender Testfall
→ Bug fix
→ funktionierender Testfall
I
Tests werden nicht wieder entfernt
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
5 / 51
Einfache Klassen
Testen für einfache Klassen
Testen mit Werkzeugen
JUnit
I
Werkzeug zum Unit Testing
I
Eclipse-Integration
I
Rahmen zum automatischen Testen auf Knopfdruck
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
6 / 51
Einfache Klassen
Testen für einfache Klassen
Testen mit Werkzeugen
JUnit
I
Werkzeug zum Unit Testing
I
Eclipse-Integration
I
Rahmen zum automatischen Testen auf Knopfdruck
Vorgehensweise
I
Zu jeder “normalen” Klasse erstelle eine Testklasse
I
Für jede signifikante Methode erstelle mindestens eine synthetische
Testmethode
I
Für jeden nachvollziehbaren Bug erstelle eine Testmethode
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
6 / 51
Einfache Klassen
Testen für einfache Klassen
Testklasse zu SimpleTicket
1
package lesson 01;
2
3
4
import org.junit.Test;
import static org.junit.Assert.∗;
5
6
7
8
public class SimpleTicketTest {
// Testmethoden
}
I
I
Testklasse im gleichen Paket
Die beiden imports machen public Klassen und Methoden des
Testframeworks JUnit sichtbar
I
org.junit ist ein Paket, das zu JUnit gehört
org.junit.Test und org.junit.Assert sind die Klassen Test und Assert in
I
diesem Paket
Erklärung von static folgt . . .
I
I
Namenskonvention für Testklasse: <IhreKlasse>Test
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
7 / 51
Einfache Klassen
Testen für einfache Klassen
Testen von Methoden: Beispiel
Spezifikation
isUsable()
isUsable()
Fahrschein ist verwendbar, wenn er noch nicht abgestempelt worden ist.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
8 / 51
Einfache Klassen
Testen für einfache Klassen
Testen von Methoden: Beispiel
Spezifikation
isUsable()
isUsable()
Fahrschein ist verwendbar, wenn er noch nicht abgestempelt worden ist.
Synthetischer Test
1
2
3
4
5
@Test
public void testIsUsable() {
SimpleTicket st = new SimpleTicket(1, 1);
assertTrue(”Ticket should be usable”, st.isUsable());
}
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
8 / 51
Einfache Klassen
Testen für einfache Klassen
Testen von Methoden: Beispiel
Spezifikation
isUsable()
isUsable()
Fahrschein ist verwendbar, wenn er noch nicht abgestempelt worden ist.
Synthetischer Test
1
2
3
4
5
@Test
public void testIsUsable() {
SimpleTicket st = new SimpleTicket(1, 1);
assertTrue(”Ticket should be usable”, st.isUsable());
}
I @Test:
Markierung (Annotation) für eine Testmethode
Deklaration einer lokalen Variable mit Typ
new SimpleTicket (1, 1): Erzeuge ein neues Objekt der Klasse
SimpleTicket und rufe den Konstruktor mit den Parameter (1, 1) auf
st.isUsable(): Aufruf der Method auf dem Objekt st
assertTrue(message, condition):
Prüft, ob condition erfüllt ist. Sonst Exception.
I SimpleTicket st:
I
I
I
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
8 / 51
Einfache Klassen
Testen von Methoden:
Spezifikation
isUsable(),
Testen für einfache Klassen
Teil 2
isUsable()
Fahrschein ist verwendbar, wenn er noch nicht abgestempelt worden ist.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
9 / 51
Einfache Klassen
Testen von Methoden:
Spezifikation
isUsable(),
Testen für einfache Klassen
Teil 2
isUsable()
Fahrschein ist verwendbar, wenn er noch nicht abgestempelt worden ist.
Synthetischer Test
1
2
3
4
5
6
7
8
@Test
public void testIsUsable2() {
SimpleTicket st = new SimpleTicket(1, 1);
st.stamp(4711, 1);
assertFalse(”Ticket should not be usable”, st.isUsable());
st.stamp(5000, 2);
assertFalse(”Ticket should still be unusable”, st.isUsable());
}
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
9 / 51
Einfache Klassen
Testen von Methoden:
Spezifikation
isUsable(),
Testen für einfache Klassen
Teil 2
isUsable()
Fahrschein ist verwendbar, wenn er noch nicht abgestempelt worden ist.
Synthetischer Test
1
2
3
4
5
6
7
8
@Test
public void testIsUsable2() {
SimpleTicket st = new SimpleTicket(1, 1);
st.stamp(4711, 1);
assertFalse(”Ticket should not be usable”, st.isUsable());
st.stamp(5000, 2);
assertFalse(”Ticket should still be unusable”, st.isUsable());
}
I assertFalse(message, condition):
Prüft, ob condition nicht erfüllt ist. Sonst Exception.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
9 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Vermeiden von sinnlosen Objekten
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
10 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Vermeiden von sinnlosen Objekten
Erinnerung (Felder und Konstruktor von
1
2
3
4
5
6
7
8
9
SimpleTicket)
// Preisstufe 1, 2, 3
private int level;
// Kind = 0, Erwachsener = 1
private int category;
// ...
public SimpleTicket(int level, int category) {
this.level = level;
this.category = category;
}
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
11 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Vermeiden von sinnlosen Objekten
Erinnerung (Felder und Konstruktor von
1
2
3
4
5
6
7
8
9
SimpleTicket)
// Preisstufe 1, 2, 3
private int level;
// Kind = 0, Erwachsener = 1
private int category;
// ...
public SimpleTicket(int level, int category) {
this.level = level;
this.category = category;
}
Sinnvolle und sinnlose Objekte
I
Keine Restriktion der Argumente beim Aufruf des Konstruktors
I
Sinnvoll: new SimpleTicket(1, 1); new SimpleTicket(1, 0)
I
Sinnlos: new SimpleTicket(0, 3); new SimpleTicket(−1, 42);
new SimpleTicket(−999, −999)
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
11 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Vermeiden der Konstruktion von sinnlosen Objekten
Wanted
Sinnlose Objekte können nicht erzeugt werden, d.h.,
alle Objekte sind sinnvoll.
Zwei Strategien
1. Fehlermeldung beim Erzeugen (Factory Method)
+ geht immer, auch bei komplizierten Bedingungen an das Objekt
− potentieller Fehler zur Laufzeit
2. Einschränkung des Wertebereichs
− nur Bedingungen an einzelne Konstruktorargumente
+ keine Laufzeitfehler
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
12 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Strategie #1: Absicherung durch Factory Method
I
Eine Java Methode kann einen Fehlerzustand durch eine Exception
melden.
I
Eine Exception wird durch eine throw-Anweisung ausgelöst und kann
durch eine catch-Anweisung abgefangen werden.
I
Jede Exception wird durch ein geeignetes Exception-Objekt
repräsentiert
⇒ Die throw-Anweisung nimmt ein Exception-Objekt als Parameter
I
Konvention: vermeide Exceptions in Konstruktoren
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
13 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Strategie #1: Absicherung durch Factory Method
I
Eine Java Methode kann einen Fehlerzustand durch eine Exception
melden.
I
Eine Exception wird durch eine throw-Anweisung ausgelöst und kann
durch eine catch-Anweisung abgefangen werden.
I
Jede Exception wird durch ein geeignetes Exception-Objekt
repräsentiert
⇒ Die throw-Anweisung nimmt ein Exception-Objekt als Parameter
I
Konvention: vermeide Exceptions in Konstruktoren
Lösung: Das Pattern factory method
Verstecke den Konstruktor und definiere (eine) Methode(n) zur
Konstruktion von Objekten. Diese wirft eine Exception, falls die
Argumente fehlerhaft sind.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
13 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Strategie #1: Absicherung durch Factory Method
Implementierung Teil 1
1
2
3
4
5
private SimpleTicket(int level, int category) {
this.level = level;
this.category = category;
}
// next up: constructor method create()
I
Sichtbarkeit des Konstruktors wird private.
I
Anstelle von new SimpleTicket(l, c) verwende Aufrufe der
factory-Methode create (nächste Folie):
SimpleTicket.create (l, c)
I
Die factory-Methode hat die gleichen Parameter wie der Konstruktor,
aber sie prüft ihre Legalität bevor das Objekt erzeugt wird.
I
Die factory-Methode ist eine static Methode: Sie hängt nur von der
Klasse ab, nicht von einem spezifischen Objekt.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
14 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Strategie #1: Absicherung durch Factory Method
Implementierung Teil 2
1
2
3
4
5
6
7
8
9
10
11
12
/∗∗
∗ Safe constructor method for simple tickets.
∗ @param level must be 1,2,3
∗ @param category must be 0,1
∗ @return a new legal SimpleTicket
∗/
public static SimpleTicket create(int level, int category) {
if (level < 1 || level > 3 || category < 0 || category > 1 ) {
throw new IllegalArgumentException(”Illegal level or category”);
}
return new SimpleTicket(level, category);
}
I
I
Eine static Methode hängt nicht an einem Objekt, sondern kann direkt
über die Klasse als SimpleTicket.create() aufgerufen werden.
IllegalArgumentException ist ein vordefinierter Konstruktor für ein
Exception Objekt. Das String-Argument ist Teil der Fehlermeldung.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
15 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Strategie #2: Einschränkung des Wertebereichs
Aufzählungstyp
I
I
Anstatt die Kategorie “Kind” oder “Erwachsener” durch Zahlen zu
kodieren, verwende einen Aufzählungstyp (enum).
Ein Aufzählungstyp
I
I
I
ist definiert durch die Liste seiner benannten Werte, Elemente genannt
dient der Modellierung einer endlichen Zustandsmenge
Definierbar im Klassendiagramm und im Java-Code.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
16 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Klassendiagramm (Revision mit Aufzählungstyp)
Enumeration
TicketCategory
CHILD
ADULT
SimpleTicket
level : PriceLevel
category : TicketCategory
timestamp : long
zone : FareZone
Enumeration
FareZone
ZONE A
ZONE B
ZONE C
Enumeration
PriceLevel
LEVEL 1
LEVEL 2
LEVEL 3
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
17 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Aufzählungstypen TicketCategory und FareZone
1
package lesson 02;
2
5
public enum TicketCategory {
CHILD, ADULT;
}
1
package lesson 02;
3
4
2
3
4
5
public enum FareZone {
ZONE A, ZONE B, ZONE C;
}
I
Syntax: Sichtbarkeit, Schlüsselwort enum, Name (groß)
I
Elemente des Aufzählungstyps: Bezeichner, nur Großbuchstaben oder
Unterstriche
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
18 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Änderungen in SimpleTicket
Vorher
9
10
11
12
13
14
15
16
17
// Preisstufe 1, 2, 3
private int level;
// Kind = 0, Erwachsener = 1
private int category;
// Zeitstempel der Entwertung (in Millisekunden seit 1.1.1970)
// nicht entwertet=0, ungültig=1
private long timestamp;
// Ort der Entwertung: Zone A=1, B=2, C=3
private int zone;
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
19 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Änderungen in SimpleTicket
Nachher
4
5
6
7
8
9
10
11
12
// new: Preisstufe: LEVEL 1, LEVEL 2, LEVEL 3
private PriceLevel level;
// new: Kind = CHILD, Erwachsener = ADULT
private TicketCategory category;
// Zeitstempel der Entwertung (in Millisekunden seit 1.1.1970)
// nicht entwertet=0, ungültig=1
private long timestamp;
// new: Ort der Entwertung: ZONE A, ZONE B, ZONE C
private FareZone zone;
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
20 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Änderungen in SimpleTicket.validate()
Vorher
1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean validate(int c, long t, int z) {
// 1. stamped exactly once?
boolean result = (this.timestamp != 0) && (this.timestamp != 1);
// 2. passenger category less than ticket category?
result = result && (c <= category);
// 3. ticket expired?
long timediff = t − timestamp;
result = result && (timediff <= level ∗ 60 ∗ 60 ∗ 1000);
// 4. ticket used in valid zone?
int leveldiff = Math.abs(zone − z);
result = result && (leveldiff < level);
return result;
}
I
Operationen in Zeile 5 (numerischer Vergleich), 8 (Rechnung), 11
(numerischer Vergleich) sind auf Aufzählungstypen nicht definiert.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
21 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Änderungen in SimpleTicket.validate()
Nachher
1
2
3
4
5
6
7
8
9
10
public boolean validate(TicketCategory c, long t, FareZone z) {
...
// 2. passenger category less than ticket category?
result = result && (c.ordinal() <= category.ordinal());
...
// 4. ticket used in valid zone?
int leveldiff = Math.abs(zone.ordinal() − z.ordinal());
...
return result;
}
I
Nachher: geänderte Signatur
I
Abhilfe: auf Aufzählungstypen ist Methode int ordinal() definiert.
I ordinal()
bildet auf 0, 1, 2, 3, . . . ab
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
22 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Änderungen in SimpleTicket.validate()
Vorher
1
2
3
4
5
6
7
8
public boolean validate(int c, long t, int z) {
...
// 3. ticket expired?
long timediff = t − timestamp;
result = result && (timediff <= level ∗ 60 ∗ 60 ∗ 1000);
...
return result;
}
I
Fürs Rechnen mit level ist ordinal() nicht passend.
I
Besser wäre LEVEL 1 7→ 1, LEVEL 2 7→ 2 etc
I
Das kann durch einen Aufzählungstyp mit Attributen erreicht werden.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
23 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Einschub: Aufzählungstyp mit Attribut
2
3
4
5
6
7
8
9
10
11
12
13
public enum PriceLevel {
LEVEL 1(1), LEVEL 2(2), LEVEL 3(3);
// corresponding numeric level
private final int level;
private PriceLevel(int level) {
this.level = level;
}
// getter method
public int getLevel() {
return this.level;
}
}
I
Für jedes Attribut wird ein private final Feld definiert.
bedeutet, dass sich der Wert des Feldes nach der Initialisierung
durch den Konstruktor nicht mehr ändern kann.
Konstruktoraufruf mit Parametern in Klammern (Zeile 4)
public Getter-Methode nur zum Lesen des Feldes
I final
I
I
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
24 / 51
Einfache Klassen
Vermeiden von sinnlosen Objekten
Änderungen in SimpleTicket.validate()
Nachher
1
2
3
4
5
6
7
8
public boolean validate(TicketCategory c, long t, FareZone z) {
...
// 3. ticket expired?
long timediff = t − timestamp;
result = result && (timediff <= level.getLevel() ∗ 60 ∗ 60 ∗ 1000);
...
return result;
}
I
Erstes Ziel erreicht: keine sinnlosen SimpleTicket Objekte mehr
möglich!
I
Aber die Klasse enthält weitere Anti-Pattern, die wir eliminieren
wollen . . .
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
25 / 51
Einfache Klassen
Benennen von Konstanten
Benennen von Konstanten
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
26 / 51
Einfache Klassen
Benennen von Konstanten
Benennen von Konstanten
Betrachte
1
2
3
// 3. ticket expired?
long timediff = t − timestamp;
result = result && (timediff <= level.getLevel() ∗ 60 ∗ 60 ∗ 1000);
I
Was ist die Bedeutung der Konstante 60 ∗ 60 ∗ 1000?
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
27 / 51
Einfache Klassen
Benennen von Konstanten
Benennen von Konstanten
Betrachte
1
2
3
// 3. ticket expired?
long timediff = t − timestamp;
result = result && (timediff <= level.getLevel() ∗ 60 ∗ 60 ∗ 1000);
I
Was ist die Bedeutung der Konstante 60 ∗ 60 ∗ 1000?
I
Natürlich ist es die Anzahl der Millisekunden pro Stunde!
I
Zur Vermeidung von Missverständnissen und zur Dokumentation
sollte jede Konstante sinnvoll benannt werden.
I
Ausnahmen: 0, 1, true, false etc
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
27 / 51
Einfache Klassen
Benennen von Konstanten
Benennen von Konstanten
Betrachte
1
2
3
// 3. ticket expired?
long timediff = t − timestamp;
result = result && (timediff <= level.getLevel() ∗ 60 ∗ 60 ∗ 1000);
I
Was ist die Bedeutung der Konstante 60 ∗ 60 ∗ 1000?
I
Natürlich ist es die Anzahl der Millisekunden pro Stunde!
I
Zur Vermeidung von Missverständnissen und zur Dokumentation
sollte jede Konstante sinnvoll benannt werden.
I
Ausnahmen: 0, 1, true, false etc
I
Sinnvolles Muster: Lege separate Klasse zur Aufnahme der
Konstantendefinitionen an.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
27 / 51
Einfache Klassen
Benennen von Konstanten
Klasse für Konstantendefinitionen
1
2
3
public class Tickets {
public static final int MILLISECONDS PER HOUR = 60 ∗ 60 ∗ 1000;
}
I static
I final
— gehört zu keinem Objekt
— Konstante, die nach Initialisierung nicht mehr verändert wird
I
Name in Großbuchstaben oder Unterstrichen
I
Initialisiert durch konstanten Ausdruck
I
Verwendung durch Tickets.MILLISECONDS PER HOUR
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
28 / 51
Einfache Klassen
Vermeiden von magischen Werten
Vermeiden von magischen Werten
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
29 / 51
Einfache Klassen
Vermeiden von magischen Werten
Vermeiden von magischen Werten
// Zeitstempel der Entwertung (in Millisekunden seit 1.1.1970)
// nicht entwertet=0, ungültig=1
private long timestamp;
1
2
3
I
Die Werte 0 und 1 sind magisch für timestamp.
I
Sie sind prinzipiell gültige Zeitstempel, bei denen es höchst
unwahrscheinlich ist, dass sie auftreten.
I
Besser und sicherer: verfahre nach dem Prinzip Nichts ist unmöglich“
”
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
30 / 51
Einfache Klassen
Vermeiden von magischen Werten
Vermeiden von magischen Werten
Lösung
// Zeitstempel der Entwertung (in Millisekunden seit 1.1.1970)
private long timestamp;
1
2
3
private static final int MAX RIDES = 1;
private int nrOfStamps;
4
5
I
Neues Feld nrOfStamps, das die Anzahl der Stempel zählt.
ist die maximale Anzahl von Stempeln, bevor der
Fahrschein nicht mehr entwertet werden kann.
I MAX RIDES
I timestamp
unterliegt keinerlei Einschränkungen mehr.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
31 / 51
Einfache Klassen
Vermeiden von magischen Werten
Vermeiden von magischen Werten
Anpassung der Methoden
1
2
3
1
2
3
4
5
6
7
public boolean isUsable() {
return nrOfStamps < MAX RIDES;
}
public void stamp(long t, FareZone z) {
if (isUsable()) {
zone = z;
timestamp = t;
}
nrOfStamps++;
}
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
32 / 51
Zusammengesetzte Klassen
Zusammengesetzte Klassen
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
33 / 51
Zusammengesetzte Klassen
Erweiterung: Mehrfahrtenkarte
Spezifikation
Das Verkehrsunternehmen möchte nun 2x4-Fahrtenkarten ausgeben. Die
2x4-Fahrtenkarte hat eine Preisstufe (1, 2, 3), sie ist für Erwachsene oder
Kinder und sie kann acht Mal entwertet werden. Der Entwerterstempel
enthält Uhrzeit, Datum und Ort der Entwertung. (usw. wie gehabt)
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
34 / 51
Zusammengesetzte Klassen
Klassendiagramm (Versuch)
SimpleTicket
level : PriceLevel
category : TicketCategory
timestamp : long[]
zone : FareZone[]
nrOfStamps : int
I
Erste Idee
I
I
I
Erweitere timestamp und zone zu Arrays
Korrespondierende Einträge bilden einen Stempel ab.
Nachteil: Korrespondenz ist implizit
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
35 / 51
Zusammengesetzte Klassen
Validation wird Klasse
SimpleTicket
level : PriceLevel
1
category : TicketCategory
nrOfStamps : int
Validation
timestamp : long
0,1 zone : FareZone
timeSinceCreated(long t) : long
levelDifference(FareZone z) : int
Besser
I
Eigene Klasse Validation
I
Assoziation zwischen SimpleTicket und Validation
I
Jedes SimpleTicket besitzt null oder eine Validation
I
Ein Validation Objekt kapselt einen Stempel
I
Operationen auf Zeitstempeln müssen in die Klasse Validation
verlagert werden!
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
36 / 51
Zusammengesetzte Klassen
Validation in Java
1
2
3
4
5
public class Validation {
// Zeitstempel der Entwertung (in Millisekunden seit 1.1.1970)
private final long timestamp;
// Ort der Entwertung
private final FareZone zone;
6
public Validation(long timestamp, FareZone zone) {
this.timestamp = timestamp;
this.zone = zone;
}
/∗∗ ... ∗/
public long timeSinceCreated(long t) {
return t − timestamp;
}
/∗∗ ... ∗/
public int levelDifference(FareZone z) {
return Math.abs(zone.ordinal() − z.ordinal());
}
7
8
9
10
11
12
13
14
15
16
17
18
19
}
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
37 / 51
Zusammengesetzte Klassen
Anpassung in SimpleTicket
1
2
3
4
5
6
7
8
9
10
11
/∗∗
∗ Stamp the ticket.
∗ @param t time of validation (in millisec)
∗ @param z location of validation (zone)
∗/
public void stamp(long t, FareZone z) {
if (this.isUsable()) {
validation = new Validation(t, z);
}
nrOfStamps++;
}
I
Die stamp() Methode erzeugt ein neues Validation Objekt.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
38 / 51
Zusammengesetzte Klassen
Anpassung in SimpleTicket
1
2
3
4
5
6
7
8
9
10
11
/∗∗
∗ Check if a ticket is valid given the passenger’s category, ...
∗/
public boolean validate(TicketCategory c, long t, FareZone z) {
boolean result = nrOfStamps == 1;
result = result && (c.ordinal() <= category.ordinal());
result = result && (validation.timeSinceCreated(t) <=
level.getLevel() ∗ Tickets.MILLISECONDS PER HOUR);
result = result && (validation.levelDifference(z) < level.getLevel());
return result;
}
I validate()
I
bearbeitet nur eigene Felder
Alle Berechnungen an Zeitstempeln sind in die Klasse Validation
verschoben
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
39 / 51
Zusammengesetzte Klassen
Mehrfahrtenkarte mit Validation
TwoTimesFourTicket
level : PriceLevel
1
category : TicketCategory
nrOfStamps : int
Validation
timestamp : long
0..8 zone : FareZone
timeSinceCreated(long t) : long
levelDifference(FareZone z) : int
Änderung
I
Assoziation zwischen TwoTimesFourTicket und Validation
I
Jedes TwoTimesFourTicket besitzt null bis acht Validation Objekte
I
(Vielfachheit / Multiplicity einer Assoziation)
I
Mögliche Implementierung: mit Array
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
40 / 51
Zusammengesetzte Klassen
Implementierung der Mehrfahrtenkarte (Auszug)
1
2
3
public class TwoTimesFourTicket {
// ...
private Validation[] validation;
4
public TwoTimesFourTicket(PriceLevel level, TicketCategory category) {
this.level = level;
this.category = category;
this.validation = new Validation[MAX RIDES];
this.nrOfStamps = 0;
}
5
6
7
8
9
10
11
public void stamp(long t, FareZone z) {
if (isUsable()) {
validation[nrOfStamps] = new Validation(t, z);
}
nrOfStamps++;
}
12
13
14
15
16
17
18
}
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
41 / 51
Zusammengesetzte Klassen
Merkmale
Merkmale zusammengesetzter
Klassen
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
42 / 51
Zusammengesetzte Klassen
Merkmale
Merkmale
I
SimpleTicket und TwoTimesFourTicket sind Beispiele für
zusammengesetzte Klassen.
I
Zusammengesetzte Klassen sind mit anderen Klassen assoziiert oder
besitzen Attribute, die selbst Objekte sind
I
Methodenentwurf:
Eine Operation auf einer zusammengesetzten Klasse verwendet
I
I
I
Attribute und Operationen der Klasse
Operationen auf Objekten von assoziierten Klassen
Operationen auf Objekten in Attributen
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
43 / 51
Zusammengesetzte Klassen
Merkmale
Testen für zusammengesetzte Klassen
I
Entwurf von Operationen geschieht top-down
1. Entwerfe Operation auf zusammengesetzter Klasse unter Annahme von
Operationen auf assoziierten Klassen
2. Entwerfe geforderte Operationen auf den assoziierten Klassen
I
Testen geschieht bottom-up
1. Erstelle Tests für untergeordnete und assoziierte Klassen
2. Erstelle Tests für zusammengesetzte Klasse
I
Im Beispiel
1. Erstelle Tests für Validation
2. Erstelle Tests für SimpleTicket und TwoTimesFourTicket
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
44 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Fahrkarten mischen
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
45 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Erweiterung: Fahrkarten mischen
Spezifikation
Das Verkehrsunternehmen möchte Einzelfahrscheine und auch
2x4-Fahrtenkarten ausgeben. (usw. wie gehabt)
Ein Fahrgast möchte mit Einzelfahrscheinen und 2x4-Fahrtenkarten
gleichermaßen umgehen.
I
D.h., ein Einzelfahrschein und eine 2x4-Fahrtenkarte muss sich auf
eine gewisse Art gleich verhalten.
I
Sie müssen beide das gleiche Interface implementieren!
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
46 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Klassendiagramm: Interface für Fahrscheine
Interface
ITicket
isUsable() : boolean
stamp(when : long, where : int) : void
validate(int category, when : long, where : int) : boolean
I
Wie Klasse, aber Stereotyp Interface“
”
Keine Attribute (Konstanten möglich)
I
Operationen wie Klasse
I
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
47 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Interface in Java
1
package lesson 02;
2
3
4
5
6
7
1
2
public interface ITicket {
public boolean isUsable();
public void stamp(long t, FareZone z);
public boolean validate(TicketCategory c, long t, FareZone z);
}
I
Jede Klasse, die diese Methoden public implementiert, kann das
Interface implementieren
I
Intention durch die implements-Klausel im Klassenheader deklariert.
I
Dann müssen diese Methoden vorhanden sein.
public class SimpleTicket implements ITicket { ... }
public class TwoTimesFourTicket implements ITicket { ... }
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
48 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Implementierungsbeziehung im Klassendiagramm
Interface
ITicket
isUsable()
stamp()
validate()
∆
SimpleTicket
I
TwoTimesFourTicket
Gestrichelte Linie mit offenem Pfeilkopf von Klasse zu
implementiertem Interface
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
49 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Interfacetypen
I
Ein Interface definiert einen Referenztypen, genau wie eine
Klassendeklaration.
I
Ein Interface (-typ) kann überall benutzt werden, wo ein Typ erwartet
wird: lokale Variable, Felder, Parameter, usw.
I
Wenn ein Interfacetyp als Parameter erwartet wird, dann darf ein
Objekt einer beliebigen Klasse, die das Interface implementiert,
übergeben werden.
I
Gleiches gilt für Zuweisungen an Variable oder Felder vom
Interfacetyp.
Jede implementierende Klasse ist ein Subtyp des Interfacetyps. Also
I
I
I
SimpleTicket ist Subtyp von ITicket und
TwoTimesFourTicket ist Subtyp von ITicket.
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
50 / 51
Zusammengesetzte Klassen
Fahrkarten mischen
Verwendung von Interfacetypen
Vereinigung von Klassen
I
Ein Typ I ist eine Vereinigung von Klassen C1 , . . . , Cn , falls jedes
Objekt vom Typ I genau eine der Klassen C1 , . . . , Cn besitzt.
I
Die Operationen auf I sind die gemeinsamen Operationen von
C1 , . . . , Cn .
I
Implementierung durch Composite Pattern.
I
Der Vereinigungstyp I wird durch ein Interface implementiert.
Beispiel für Vereinigung
ITicket ist Vereinigung von SimpleTicket und TwoTimesFourTicket
Peter Thiemann (Univ. Freiburg)
Programmieren in Java
JAVA
51 / 51
Herunterladen