Zustände

Aus ComeniusWiki
Version vom 15. Mai 2019, 10:38 Uhr von B.Schiller (Diskussion | Beiträge)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Durch die Veränderung des Wertes eines Attributes wird der Zustand eines Objektes verändert. Diese Zustandsänderung kannn durch unmittelbare Zuweisung eines neuen Wertes an das Attribut oder durch indirekte Wertzuweisung durch eine andere Methode ausgelöst werden. Der Zustand eines Objektes wird durch die Gesamtheit der Werte aller seiner Attribute definiert. Ein Objekt ändert seinen Zustand, wenn sich der Wert mindestens eines seiner Attribute ändert (Zustandsübergang). Zwei Objekte der gleichen Klasse sind im gleichen Zustand, wenn sie in den Werten aller ihrer Attribute übereinstimmen. Um Abläufe aus der realen Welt zu simulieren, ist es notwendig, dass man sich im Vorfeld darüber klar wird, welche verschiedenen Zustände die beteiligten Objekte einnehmen können und wie diese Zustände gegebenenfalls voneinander abhängen.

Zustaende.JPG


Aufgabe 1

Welche Zustände kann eine einfache Verkehrsampel annehmen? Welche Zustandsübergänge treten dabei auf?


Aufgabe 2

Roboter Karol bewegt sich in seiner kleinen Welt. Sein Zustand lässt sich durch die drei Angaben x-Position, y-Position und Blickrichtung angeben. Die Startposition lautet:[(1,1);S]. Zustandsübergänge werden durch den Aufruf der Methoden schritt(), linksDrehen() und rechtsDrehen() ausgelöst. Notiere die verschiedenen Zustände von Karol, die sich im Verlauf des folgenden Programms ergeben:

schritt
schritt
linksDrehen
wiederhole 4 mal
    Schritt
*wiederhole
rechtsDrehen
Schritt
linksDrehen
Schritt
Schritt

Wie lässt sich der Zustandsübergang bei einem Schritt (genauer beim Aufruf der Methode schritt()) mathematisch fassen?


Zustandsdiagramme

Mithilfe von Zustandsdiagrammen lässt sich das Verhalten von Objekten beschreiben. (Solche Objekte bezeichnet man auch als Zustandsautomaten.) Ein Zustandsübergangsdiagramm oder kurz Zustandsdiagramm besteht aus Zuständen und deren Übergängen. Es zeigt alle Abläufe, die für ein System möglich sind. Ein Zustandsübergang verbindet zwei Zustände. Der Übergang wird durch einen Pfeil dargestellt und wird stets durch ein Ereignis ausgelöst. Dieses Ereignis notiert man direkt am zugehörigen Pfeil. In einem Zustandsdiagramm gibt es genau einen Anfangszustand, der in einem Diagramm durch einen Pfeil mit rundem, ausgefülltem Pfeilfuß gekennzeichnet wird. Es sind kein, ein oder mehrere Endzustände möglich. Ein Endzustand wird durch einen kleinen ausgefüllten Kreis mit einem umschließenden Ring gekennzeichnet.


Beispiel:

Bei einer Mikrowelle führt das Öffnen der Tür zum Abbruch, das anschließende Schließen der Tür zur Wiederaufnahme des laufenden Programms. Dies lässt sich in folgendem Zustandsdiagramm darstellen. Dabei sind sowohl das auslösende Ereignis (Öffnen bzw. Schließen der Tür) als auch die ausgelöste Aktion (Abbruch bzw. Wiederaufnahme des Programms) angegeben.

Mikrowelle.JPG


Hinweise:

  • Ein Übergang kann nicht unterbrochen werden. Einmal ausgelöst wird die Aktion durchgeführt.
  • Ein Zustandsübergang muss den Zustand des Systems nicht unbedingt ändern.
  • Ein Ereignis löst einen (oder keinen) Übergang aus, aber niemals mehrere. Man spricht in diesem Zusammenhang auch von einem deterministischen Zustandsdiagramm.
  • Zustandsnamen sind frei wählbar, sollten aber selbsterklärend sein. Es ist üblich, bei der Bezeichnung von Zuständen Substantive, Partizipien oder Adjektive zu verwenden.
  • Ereignisnamen enthalten hingegen immer Verben. Verben weisen darauf hin, dass ein Ereignis häufig eine Veränderung auslöst.


Folgende Fragen sind beim Entwurf eines Zustandsmodells für einen Problembereich zu klären:

  • Welche Zustände sind relevant?
  • Welcher Zustand ist ein Anfangszustand?
  • Welche Ereignisse sind relevant?
  • Welche Zustandsübergänge können ausgelöst werden?
  • Gibt es Endzustände?


Beispiel für die Erstellung eines Zustandsdiagramms:

Gegeben sei ein einfacher CD-Player. Nach dem Einschalten ist das CD-Fach geschlossen. Nach dem Öffnen kann eine CD eingelegt werden und der CD-Player ist bereit zum Abspielen der CD. Mit PLAY wird die CD abgespielt, mit STOP wird die Wiedergabe abgebrochen. Der CD-Player kann entweder mit eingelegter CD oder ohne CD im Fach wieder ausgeschaltet werden.

Vorgehensweise bei der Erstellung des Zustandsdiagramms:


Aufgabe 3

Erstelle unter Rückgriff auf die Ergebnisse von Aufgabe 1 ein Zustandsdiagramm für eine Verkehrsampel. Gibt es bei einer Ampel einen Endzustand?

Zustandsübergänge mit Bedingung

Häufig ist ein Zustandsübergang mit einer Bedingung verbunden, d.h. die auszulösende Aktion wird nur dann durchgeführt, wenn eine bestimmte Bedingung erfüllt ist.

Allgemeine Darstellung eines Zustandsübergangs mit Bedingung:


UebergangMitBedingung.JPG


Beispiel

Bei einem Getränkeautomaten kann ein Getränk erst geordert werden, wenn genug Geld eingeworfen worden ist.


Getraenkeautomat.JPG


Aufgabe 4

Es soll ein vereinfachter Bankautomat mit Hilfe eines Zustandsübergangsdiagramms modelliert werden. Folgende Abläufe sollen umgesetzt werden:

  • Führt der Kunde seine Karte ein, so wird er bei gültiger Karte aufgefordert, seine PIN einzugeben. Wenn die Karte ungültig ist, wird sie sofort wieder ausgegeben. Der Kunde wird aufgefordert, die Karte zu entnehmen und nach der Entnahme ist der Automat wieder bereit.
  • Auch bei falscher PIN-Eingabe gibt der Bankautomat die Karte zurück und der Kunde muss diese entnehmen. Mit der richtigen PIN kann sich der Kunde authentifizieren und sodann einen Geldbetrag auswählen.
  • Ist der ausgewählte Betrag zu groß, so muss solange erneut ein Betrag gewählt werden, bis die Auswahl passt. Wurde ein erlaubter Betrag ausgewählt, dann gibt der Automat die Karte aus und bucht den entsprechenden Geldbetrag vom Konto ab. Erst wenn die Karte entnommen ist, wird das Geld ausgegeben.

Implementierung von Zustandsdiagrammen in Java

Zustandsdiagramme sind natürlich auf eine Implementierung als Programm ausgerichtet. Wir wollen uns die Vorgangsweise am Beispiel unserer Verkehrsampel aus Aufgabe 8.2.1. ansehen. Dabei sind folgende Schritte nötig:

  • Für die Repräsentation der möglichen Zustände wäre zwar z. B. auch der Datentyp int geeignet, näher am Modell ist aber der Aufzählungstyp enum. Die Zustände werden so durch ihre Namen angesprochen.
  • Auslösende und ausgelöste Aktionen realisiert man durch Methoden. Nur die auslösenden erhalten öffentliches Zugriffsrecht.
  • Durch bedingte Anweisungen unterscheidet man, in welchem Zustand welches Ereignis ausgelöst wird.


Lösungsvorschlag für die Ampelschaltung:

public class Ampel{
 
//Mögliche Zustände werden als Aufzählungstyp enum vereinbart
   private enum Ampelzustand {rot, gruen, gelb, rotgelb, aus};
 
//Variable zum Aufnehmen des aktuellen Ampelzustands (Variable ist vom Typ Ampelzustand)
   private Ampelzustand z;
 
public Ampel(){
//Der Konstruktor erzeugt eine Ampelinstanz im Zustand "aus"
   z=Ampelzustand.aus;
}
 
public void schalteEin()   //auslösendes Ereignis: Aufruf der Methode schalteEin()
{
   if (z == Ampelzustand.aus)
{
   setzeZustand(Ampelzustand.gelb);   //ausgelöste Aktion: Setzten des Ampelzustands auf "gelb"
}
}
 
//Methode, die das Erscheinungsbild der Ampelzustände festlegt (hier vereinfacht durch Beschreibung auf der Konsole)
private void setzeZustand(Ampelzustand zustandNeu){
 
z=zustandNeu;
 
if(z==Ampelzustand.aus){
   System.out.println("aus");
   System.out.println("aus");
   System.out.println("aus");
   System.out.println();
}
if(z==Ampelzustand.gelb){
   System.out.println("aus");
   System.out.println("gelb");
   System.out.println("aus");
   System.out.println();
}
if(z==Ampelzustand.rot){
   System.out.println("rot");
   System.out.println("aus");
   System.out.println("aus");
   System.out.println();
}
if(z==Ampelzustand.rotgelb){
   System.out.println("rot");
   System.out.println("gelb");
   System.out.println("aus");
   System.out.println();
}
if(z==Ampelzustand.gruen){
   System.out.println("aus");
   System.out.println("aus");
   System.out.println("gruen");
   System.out.println();
}
}
 
//Methode für automatische Schaltung, die jeweils 3 sec (=3000 ms) Pause zwischen den Zuständen lässt
public void schalteAuto(){
setzeZustand(Ampelzustand.gelb);
try{Thread.sleep(3000);}catch(InterruptedException ie){/*Nichts tun*/}
setzeZustand(Ampelzustand.rot);
try{Thread.sleep(3000);}catch(InterruptedException ie){/*Nichts tun*/}
setzeZustand(Ampelzustand.rotgelb);
try{Thread.sleep(3000);}catch(InterruptedException ie){/*Nichts tun*/}
setzeZustand(Ampelzustand.gruen);
}
}


Aufgabe 5

Es soll nun der Bankautomat aus Aufgabe 4 in Java implementiert werden. Auf den Zustand Entnahmeaufforderung anzeigen kann dabei verzichtet werden. Das Einführen bzw. Entnehmen von Karte und Geld wird einfach durch eine Konsoleneingabe simuliert. Aus diesem Grund verwenden wir den Scanner aus Kapitel 2 für die Eingaben. Der Auszahlungsvorgang wird in einer Methode auszahlen() umgesetzt, in der nacheinander die 6 Zustände abgearbeitet werden. Bei einer Falscheingabe von PIN oder Geldbetrag wird der Kunde der Einfachheit halber zur Entnahme der Karte aufgefordert und der Automat geht wieder in den Zustand bereit über. Beim Geldbetrag sollen folgende Beschränkungen gelten: Er darf nicht größer als 1000 Euro sein und es sind nur Beträge in 50iger-Schritten möglich (50 - 100 - 150 - ...). Um den Vorgang zu überprüfen gehen wir von einem Kontostand von 1000 Euro aus und der neue Kontostand wir nach der Entnahme kurz angezeigt. Das folgende Klassendiagramm soll als Grundlage für die Implementierung dienen:

Aufgabe 8-4-1.JPG


Aufgabe 6

Ein Internetportal bietet Kunden die Möglichkeit in Reisebüros, bei denen man registriert ist, eine Reise unter Angabe von Zielort und Hotelname zu buchen. Folgende Klassen seien gegeben:


public class Kunde
{
   private String name;
 
   public Kunde(String name){
       this.name=name;
    }
 
    public String getName(){
        return name;
    }
}


public class Reisebuero {
 
    private String reisebueroName;
 
    //Zu Testzwecken gehen wir von einem einzigen registrierten Kunden aus
    private String hinterlegterBenutzer="traveler";
    private String hinterlegtesKennwort="cook";
 
 
    public Reisebuero(String reisebueroName){
        this.reisebueroName=reisebueroName;
    }
 
 
    public String getReisebueroName(){
        return reisebueroName;
    }
 
    public String getHinterlegterBenutzer(){
        return hinterlegterBenutzer;
    }
 
    public String getHinterlegtesKennwort(){
        return hinterlegtesKennwort;
    }
}


Es soll nun eine Klasse Buchung erstellt werden, die Kunde und Reisebüro zusammenführt. Dazu werden zuerst die Anmeldedaten mittels einer Methode anmeldungPruefen() überprüft und anschließend werden die Buchungsdaten (Zielort und Hotel) aufgenommen. Damit haben wir hier auch zwei Zustände (anmeldung und buchung), die wir in einer Methode buchen() abarbeiten. Als Ergebnis wird von einer Methode buchungVornehmen() folgender Text ausgegeben (Beispiel):

Das Reisebuero PromilleAufMalle bucht auf den Namen Luis Trinker einen Aufenthalt im Hotel Sangriagemetzel in Palma.
Wir wünschen einen schönen Aufenthalt.