2. Motivation:
Ermögliche es einem Objekt, sein Verhalten zu
ändern, wenn sein interner Zustand sich
ändert
3. Beispiel
• Wir modellieren eine Freundin/Freund:
– Laune: Fröhlich, Neutral, Bockig
– Wir können mit ihr interagieren: ihr einen Kuss geben, uns mit ihr
unterhalten, oder sie ärgern
– Je nach ausgeführte Aktion, ändert sich ihren Zustand (Laune), wie
unten illustriert:
4. • Je nach Zustand wird die Freundin unterschiedlich auf Interaktionen (unterhalten(),
veraergern(), kussGeben()) reagieren
• Das äußere Verhalten der Freundin ändert sich also in Abhängigkeit von ihrem inneren
Zustand.
Wie löst man das Problem?
5. Eine Möglichkeit:
• Aktuelle Zustand durch Integer repräsentieren. NEUTRAL = 0; BOCKIG =1; FRÖHLICH = 2;
• In jede Operation – kussGeben(), unterhalten(), verärgern() – wird geprüft, welchen Wert
diese Integer Variable hat, und danach verhandelt, also: if(state == 1){ //Reagiere auf bockige
weise} elseif(state == 0){ //Reagiere neutral}
Beispiel:
publicvoid unterhalten() {
if (aktuellerZustand == NEUTRAL) {
System.out.println(„blablabla!”);
else if (aktuellerZustant == BOCKIG) {
System.out.println(“Ich bin sauer, Ich will nichtmitdirreden!”);
}else if (aktuellerZustant == FRÖHLICH) {
System.out.println(“Hihi, blablabla!”); }
}
6. Allerdings… Naiver Ansatz
Probleme:
• Unleserlich/Unübersichtlich
• Nicht wirklich Objektorientiert
• Schlechte Wartbarkeitund Erweiterbarkeit=>if/else Konstrukt muss bei jede kleine Änderung
erweitert werden. Schnell Fehleranfällig.
Und was ist, wenn wir als neue Anforderung möchten, dass bei einen Aufruf von unterhalten()
beim Zustand Neutral, es eine 10% Wahrscheinlichkeit gibt, dass dies in den Zustand Bockig
führt, weil man was falsches gesagt hat?
Reine strukturierte Programmierung also doch keine Gute Idee.
=>Bessere Lösung: Statusobjekte anlegen, die je eine Laune/Zustand
darstellen sollten, und die sich einen Interface teilen.
– Somit nutzen sie alle die selben Methoden, Implementieren sie jedoch
unterschiedlich
7. OO-Entwurfsprinzipe:
• Identifiziere jene Aspekte, die sich ändern und trenne sie von jenen, die konstant
bleiben.
In Zukunft müssen wahrscheinlich neue Zustände integrieren. Die Freundin ändert sich aber nicht.
Jedoch wird bei dem jetzigen Entwurf bei jede Änderung beeinträchtigt.
• Offen/Geschlossen–Prinzip (Open/Closed): Entwürfe sollen für Erweiterungen
offen, aber für Veränderungen geschlossen sein.
Wie oben: Freundin-Code soll für Veränderungen geschlossen sein, sie soll um neue Zustände
jederzeit erweitert werden können
• Kohäsion (= Grad, inwiefern eine Klasse einen einzigen konzentrierten Zweck hat)
und Delegation: Kapsele Verantwortlichkeiten in eigenen Objekten und delegiere
Aufrufe an diese Objekte. Es gilt: eine Klasse, eine Verantwortlichkeit.
Hier ist es Deutlich: Zustände müssen in eigenen Objekten gekapselt werden
9. Implementierung:
• Interface IZustand(){
Public void unterhalten();
Public voidveraergern();
Public voidkussGeben();
}
Class Neutral implementsIZustand
Class Bockig implementsIZustand
Class FroehlichimplementsIZustand
10. Die Klasse Freundin:
class Freundin{
publicIZustandaktuellerZustand;
//Setter zum Setzen des Aktuellen Zustands
publicvoidsetAktuellerZustand(IZustandpAktuellerZustand){
aktuellerZustand= pAktuellerZustand;
}
public Freundin(){
setAktuellerZustand(newNeutral(this));
}
publicvoidunterhalten(){
aktuellerZustand.unterhalten();
}
publicvoidkussGeben(){
aktuellerZustand.kussGeben();
}
publicvoidveraergern(){
aktuellerZustand.veraergern();
}
}
11. Der neutrale Zustand
class Neutral implements IZustand{
//Konstruktur. MitFreundinparametrisiert
public Neutral (FreundinpFreundin){
_freundin = pFreundin;
}
//Referenz auf die Freundin
private Freundin _freundin;
public void unterhalten(){
System.out.println(“blablabla!“);
}
public void kussGeben(){
System.out.println(“Hihi, dankeschön!“);
_freundin.setAktuellerZustand(newFröhlich(_freundin));//Zustandsübergang
}
public void veraergern(){
System.out.println(“Sag mal gehtsnoch?”);
_freundin.setAktuellerZustand(newBockig(_freundin)); //Zustandsübergang
}
}
12. Der bockige Zustand
classBockigimplements IZustand{
//Konstruktur. MitFreundinparametrisiert
public neutral (FreundinpFreundin){
_freundin=pFreundin;
}
//Referenz auf die Freundin
private Freundin _freundin;
public void unterhalten(){
System.out.println(“Ich bin sauer, Ich will nichtmit dir reden!“);
}
public void kussGeben(){
System.out.println(“Na gut! IchHabdichwiederlieb…“);
_freundin.setAktuellerZustand(new Neutral(_freundin)); // Zustandsübergang
}
public void veraergern(){
System.out.println(“Du machstallesbloßnochschlimmer!”);
}
}
13. Der fröhliche Zustand
class Froehlich implements IZustand{
//Konstruktur. MitFreundinparametrisiert
public neutral (FreundinpFreundin){
_freundin=pFreundin;
}
//Referenz auf die Freundin
private Freundin _freundin;
public void unterhalten(){
System.out.println(“Hihi, blablabla!“);
}
public void kussGeben(){
System.out.println(“Oh la la…“);
_freundin.setAktuellerZustand(newFroehlich(_freundin)); //Zustandsübergang
}
public void veraergern(){
System.out.println(“Hey! Dabei war ich so gut drauf!”);
_freundin.setAktuellerZustand(newNeutral(_freundin));
}
}
14. Testrun:
Freundin freundin=new Freundin();
//Defaultzustand: Neutral -_-
freundin.unterhalten(); // blablabla!
freundin.veraergern(); // Sag mal gehtsnoch?
// => Ab jetzt: Bockig
freundin.unterhalten(); // Ich bin sauer, Ich will nichtmit dir reden!
Freundin.kussGeben(); // Na gut!IchHabdichwiederlieb
// => Ab jetzt. Neutral -_-
freundin.kussGeben(); // Hihi. :-)
// => Ab jetzt: Fröhlich
freundin.unterhalten(); // Hihi,blablabla!
Freundin.kussGeben(); // Oh la la…
15. Variationen
Definition der Zustandsübergänge
Zustände bestimmen Folgezustand
Kontext bestimmt Folgezustände
Folgezustand als Rückgabewert von operate()
16. Lebenszeit von Zustandsobjekten:
Erzeugung bei Bedarf Erzeugung im Voraus
Bei seltener Änderung der Zustände Bei häufigen Zustandswechseln
Referenz vom Context auf Einmalige Kosten zur Instanziierung der
Zustandsobjekte nicht notwendig. Zustandsobjekte
Kein Kosten für das Zerstören von
Zustandsobjekten
Allerdings muss das Kontextobjekt zu
jedem möglichen Zustandsobjekt eine
Referenz halten.
Gemeinsame Nutzung von Zustandsobjekten:
Wenn die Zustände keinen internen Zustand haben, so können zur Einsparung von
Ressourcen verschiedene Kontextobjekte die selben Zustandsobjekte nutzen
17. Anwendungsfälle
• Ein Objekt soll sein äußeres Verhalten zur Laufzeit in Abhängigkeit von
seinem Zustand ändern.
• Ein Objekt besitzt eine Reihe von Methoden ähnlicher Struktur, die sich
aus immer gleichen Bedingungsanweisungen zusammensetzen.
18. Vorteile Nachteile
• Intuitiv und Verständlich • Mehr Klassen
• Hohe Kohäsion/klare • Weniger Kompakt
Trennung der
Verantwortlichkeiten
• Änderungsstabil
• Erweiterbar ohne den Kontext
zu ändern
• Wiederverwendbarkeit
• Weniger fehlerträchtig