Es geht los.
Hallo, ich bin auch da.
Na, was kommt bei dem Programm raus?
Hört mir jemand beim Zwischenhelfen?
Es geht los.
Schon eine Minute drüber gequatscht.
So, was kommt bei dem Programm raus?
Das ist schon einige Zeit auf der Tafel gewesen.
Ich gebe jetzt die Antwort nicht vor, vielleicht kommen Sie ja selber
drauf mal.
Wenn Sie zu spät kommen, bitte hinten reingehen.
Na, hat jemand einen Vorschlag?
Wer es noch nicht eingetippt hat?
Ja?
Genau.
Also, die erste Zeile ist ja wohl klar.
Das wird natürlich das H drücken.
Da sind zwei Zeichen rein, das Plus.
Konkateniert die.
Das ist ziemlich leicht.
Aber was ist in der zweiten Zeile?
Ich glaube, Sie haben schon die richtige Idee.
Was bedeutet das Plus in der zweiten Zeile?
Das ist normale Addition, genau.
Zeichenaddition, genau wie Sie sagten.
Das heißt also, die zweite Zeile ist das Plus, bedeutet hier, es sind
keine Zeichen rein, sondern das sind hier zwei Ziffern, die werden
interpretiert als Ganzzahlen, eigentlich nur als 8-Bit-Zahlen, und die
werden addiert.
Um das genaue Ergebnis zu wissen, müsste man wissen, was der Code für
H ist und der Code für das kleine a ist.
Und das weiß ich natürlich auch nicht auswendig, aber man kann es
nachschauen.
Und wenn man es nachschaut, kommt dann...
Moment, jetzt muss ich noch überlegen, wo ich meinen Zeiger hingetan
habe.
Ja, dann kommt HA 169 heraus.
Das ist einfach die Addition dieser zwei Zeichen.
Ja, das ist überraschend, ja.
Wie könnte man es korrigieren?
Man könnte es korrigieren, indem man eine Konversion erzwingt, eine
Typkonversion, indem man sagt, ich habe erstmal eine leere
Zeichenreihe.
Das ist schonmal ein Zeichen, das sagt dem Plus A, ich muss
Zeichenreihen addieren, also es konkatenieren, und dann wird die
Konversion richtig passieren.
Das ist natürlich blödsinnig, ja.
Das könnte man gleich in die Zeichenreihe hinschreiben.
Oder aber man benutzt mal wieder das Stringbuffer.
Machen Sie sich einen neuen Stringbuffer SB.
Hier ist er.
Und Sie hängen an dem Per Append das H und das A dran.
Ja, natürlich ist das ziemlich viel Schreibaufwand für ziemlich wenig
Effekt, ja.
Aber das ist nur zur Illustration.
Wir wollen natürlich möglichst kleine Beispiele haben.
Wenn es darum geht, Einzelzeichen in eine Zeichenreihe anzufügen,
zusammenzusetzen, dann vorsichtig mit dem Plus, wenn da mehr vorkommt,
benutze den Stringbuffer.
Und diese Typumwandlung funktioniert übrigens nicht, indem man dem
sagt, machen String draus, weil der Übersetzer das nicht will.
Also Zeichenkombination, Zeichenkonkatenation versus Addition
beachten.
Manchmal bedeutet das Plus das eine, manchmal das andere.
Die implizite Typumwandlung beachten und die doppelten Gänsefüßchen,
die einfachen Gänsefüßchen eben vorsichtig einsetzen.
Was können wir daraus lernen?
Wieder ein bisschen was dazugelernt zu Java.
Gibt es vielleicht noch eine Frage oder sowas dazu?
Ne,
gut, dann machen wir gleich weiter
mit den UML-Klassendiagrammen.
Da waren wir letztes Mal stehen geblieben, auf Seite 63 glaube ich, da
hatten wir über Vererbung gesprochen.
Gehe nochmal zurück, das ist eigentlich, eigentlich kennen Sie die
Konzepte schon, wir wiederholen sie nochmal kurz und sehen, wie wir
sie in UML ausdrücken können.
Also wir sagten ja immer, eine Klasse ist eine Menge aus dieser Menge
aller Objekte Omega, also alles, was von irgendjemand identifiziert
werden kann.
Was ist dann eine Unterklasse?
Naja, Klasse ist auch eine Klasse, muss also auch eine Menge sein.
Also ist so eine Unterklasse immer eine Teilmenge ihrer Oberklasse.
Teilmengenbeziehungen besteht zwischen Oberklasse und Unterklasse.
Und in der Regel möchte man auch, dass diese Unterklassen disjunkt
sind, dass sie also paarweise disjunkt sind und keine gemeinsamen
Mitglieder haben.
Allerdings hatte ich Ihnen das Beispiel des Platypus gegeben, dass es
da Ausnahmen geben kann.
Aber normalerweise macht man seine Unterklassen disjunkt.
Okay, dann hätten wir, also das war die Unterklassenbeziehung, also
wichtig zu merken, dieser großgeschriebene Pfeil hier, der hier
bedeutet, die Unterklassenbeziehung zeigt auf die Oberklasse.
Das ist fürs Malen in UML gedacht.
So, dann sagten wir, in der Informatik brauchen wir, also in der
Programmierung wollen wir eine zusätzliche Bedingung erfüllt haben,
neben der Unterklassenbeziehung, dass nämlich das Substitutionsprinzip
gilt.
Das heißt, wenn Sie an einer Stelle eine Klasse einsetzen und mit den
Funktionen oder Methoden aufrufen an einem Objekt, das zu einer
bestimmten Klasse gehört, dann sollte es so sein, dass wenn Sie
stattdessen ein Unterklassenobjekt benutzen, dass das Programm
trotzdem weiter funktioniert.
Das heißt, in allen Kontexten, wo Sie ein Objekt einer Klasse
einsetzen, also eine Instanz, dürfen Sie auch ein Objekt einer
Subklasse benutzen.
Das lässt sich, also das Unterklassenelement genauso wie das
Oberklassenelement verwenden.
Das Unterklassenelement darf sich aber in anderen Punkten
unterscheiden.
Zum Beispiel könnte es mehr Attribute haben, die im Oberklasseninstanz
nicht vorhanden sind, aber das macht nichts, denn ein Programm, das
nur die Oberklasse benutzt, kann die zusätzlichen Eigenschaften der
Unterklasse nicht sehen.
Das ist übrigens eine wichtige Eigenschaft, die wird dann später sehr,
sehr häufig ausgenutzt, wenn wir über Entwurfsmuster sprechen, um
Varianten von Programmen einfach behandeln zu können.
Da ist dieses Substitutionsprinzip ganz wichtig.
Das hatten wir ein Gegenbeispiel gehabt.
Gehen wir gleich weiter.
Wie das mit der Vererbung funktioniert, auch das wissen Sie schon.
Ich habe später noch eine Variante dieses Beispiels, das noch ein
bisschen darauf eingeht.
Dann sagten wir, Unterscheide zwischen Signaturvererbung und
Implementierungsvererbung.
Signaturvererbung heißt, Sie haben eine Klasse, in der steht eine
abstrakte Methode.
Abstrakte Methode bedeutet, sie ist nicht implementiert.
Die Unterklasse erbt diese Signatur, die Signatur dieser Methode.
Signatur ist ja nichts anderes als der Name, Parameterliste mit den
Typen und den Ergebnistypen.
Das ist die Signatur einer Methode.
Das wird dann vererbt und damit muss diese Unterklasse entweder diese
Signatur weitervererben an noch eine Unterklasse.
Irgendwo muss es aber eine Unterklasse geben, die diese Signatur dann
tatsächlich mit einem implementiert, das heißt, den Rumpf, den Code
für die Methode liefert.
Das nennt man Signaturvererbung.
Implementierungsvererbung ist, wenn Sie einfach eine Methode
implementiert haben in einer Oberklasse und eine Unterklasse sie
aufruft, ohne dass Sie sie neu implementieren.
Natürlich brauche ich zur Implementierungsvererbung auch die
Signaturvererbung.
Nun, eigentlich brauchen wir fast nur die Signaturvererbung, aber die
Implementierungsvererbung ist bequem, weil man damit nicht Sachen
zweimal schreiben muss, die identisch sind.
Unterscheiden müssen wir jetzt noch, Sie erinnern sich, es gibt das
Überschreiben einer Methode und das Überladen.
Überschreiben sagen wir, was immer die Oberklassenmethode war, ich
will nur die Signatur haben, aber eine andere Implementierung.
Ich ersetze sie also.
Das nennt man Überschreiben.
Im Gegensatz zu ist das Überladen.
Überladen bezieht sich auf einen Bezeichner einer Methode, das sagen
Sie, ich habe eine neue Methode mit dem gleichen Namen, aber anderer
Signatur.
Sie können also Methode X haben, ohne Parameter und eine Methode X mit
einem ganzzahligen Parameter, eine dritte Methode mit zwei
ganzzahligen Parametern, eine vierte Methode mit zwei Floats.
Das nennt man Überladen.
Der gleiche Bezeichner der Methodenname, unterschiedliche Signatur.
Während beim Überschreiben haben Sie exakt die gleiche Signatur,
liefern aber eine neue Implementierung.
Die alte ist damit verschwunden, verdeckt durch das Überladen der
Methode.
Wie gesagt, das ist eigentlich nur Wiederholung dessen, was Sie aus
Java schon kennen.
Also hier kommt ein Beispiel.
Wir haben hier eine Oberklasse, denen wir geometrische Figur und drei
Unterklassen, Kreis, Oval und Rechteck.
Und wie gesagt, es ist klar, dass eine geometrische Figur eine sehr
große Menge ist.
Und davon gibt es jetzt Untermengen, alle Kreise, alle Ovale und alle
Rechtecke.
Was hat denn die geometrische Figur zu bieten?
Sie hat, sagen wir, eine Position.
Das ist irgendwo ein Punkt auf der geometrischen Funktion.
Sie brauchen eine Zeichenoperation und der Einfachkeit halber nur noch
eine Löschoperation.
Der Kreis biegt hinzu zur Position einen Radius.
Die Position könnten wir jetzt als das Zentrum des Kreises auffassen
und eine eigene Zeichenoperation, nämlich einen Kreis zu zeichnen.
Dazu brauche ich eine spezielle Operation.
Oval hat zwei Radii und eine eigene Zeichenoperation und das Rechteck
natürlich eine Länge und eine Breite und eine Position.
Das ist wahrscheinlich die linke Oberecke.
Wieder für ein Achsenparallelsrechteck reicht das.
Und eine Zeichenoperation.
Hier oben haben wir noch ein bisschen was eingebettet für
Assoziationen, die wir ja letztes Mal ausführlich besprochen hatten.
Eine Assoziation zeige an.
Die Assoziation zeige an.
Sehen Sie den Lesepfeil.
Das sagt also, ich muss in der Richtung lesen.
Bildschirm zeigt an mehrere geometrische Figuren.
Beliebig viele, gar keine, ist auch erlaubt.
Nur möglicherweise eine geometrische Figur kann auch auf mehreren
Bildschirmen sein.
Das wollen wir auch zulassen.
So.
Jetzt sehen Sie, dass wir die Zeichenoperation überladen haben, die
Löschoperation aber nicht.
Die Löschoperation ist wahrscheinlich so, dass ich einfach nur das
Objekt freigeben muss.
In Java brauche ich nicht mal eine Löschoperation, denn die wird von
der Speicherbreinung automatisch entfernt, wenn eine Figur, eine
geometrische Figur nicht mehr erreichbar ist.
Das trifft auch auf jedes Objekt zu.
Und die Frage, kann es überhaupt eine sinnvolle Implementierung für
Zeichnen hier oben geben, ist wahrscheinlich zu verneinen.
Ich weiß nämlich nicht, wie ich allgemeine Figuren zeichne, ohne dass
ich irgendeine Repräsentation dieser Figuren habe.
Also ist diese Funktion wahrscheinlich abstrakt.
Abstrakte Methoden in UML kann man auf zweierlei Arten schreiben.
Die eine ist per Kursiv.
eine Klasse, die wenigstens eine abstrakte Methode hält, ist dann auch
eine abstrakte Klasse.
Dann schreibt man jedenfalls, wenn es gedruckt wird, auch Kursiv.
Und natürlich habe ich jetzt nur noch eine Signaturvererbung, nämlich
die Signatur von Zeichnen, die nicht besonders interessant ist, wird
auf die drei Unterklassen vererbt.
Das hatte ich gesagt.
Von abstrakten Klassen kann es keine Exemplare oder Instanzen geben.
Warum denn wohl nicht?
Weil mindestens eine dieser Methoden nicht funktioniert.
Wenn ich sie aufrufen würde, würde irgendetwas Undefiniertes
passieren.
Um das zu vermeiden, wenn die Klasse gewissermaßen nicht vollständig
implementiert ist, dann lassen wir es nicht zu, dass Instanzen davon
erzeugt werden.
Gibt es noch eine Variation?
Entschuldigung, man könnte auch noch sagen, dass eine abstrakte Klasse
an ihre Unterklassen die Verpflichtung vererbt, die fehlenden,
abstrakten Methoden zu implementieren.
Oder aber sie weiter zu vererben, um sie die Verpflichtung einer
Unterklasse auf der dritten Stufe weiterzugeben und so fort.
Das ist nochmal genau das gleiche.
Wir haben es jetzt nur anders geschrieben und das ist die bevorzugte
Schreibweise für die Klausur.
Ich kann nämlich an Handschriften nie erkennen, ob das jetzt Kursiv
ist oder Normalschrift.
Also nehmen wir diese Notation, die sagt, in UML, wenn eine Methode
abstrakt ist, schreiben Sie dieses Wörtchen abstrakt davor in diesen
spitzen Klammern und wenn eine Klasse abstrakt ist, schreiben Sie
drüber oder davor ebenfalls abstrakt.
Dann kann ich das lesen.
Dann sagen, ah, ihr habt verstanden, hier brauche ich eine abstrakte
Klasse.
Ja?
So, und im UML-Diagramm selbst natürlich können Sie sagen, naja, muss
ich denn, wenn Zeichne schon vererbt wird, muss ich das in der
Unterklasse eigentlich noch angeben?
Bekomme ich ja.
Und da ist die Regel, dass Sie hier diese Methode angeben sollen,
falls sie tatsächlich in dieser Klasse implementiert werden sollte.
Wenn Sie sagen, naja, nehmen wir das Rechteck, ist vielleicht besser,
beim Rechteck sagen Sie, da gibt es noch eine Unterklasse, das ist die
Klasse Quadrat, eine Unterklasse von Rechteck und Sie sagen, will ich
die Zeichneoperation einfach nicht implementieren, dann lassen Sie sie
hier weg.
Und es würde diese Unterklasse Rechteck dann nochmal erscheinen, weil
sie dort implementiert werden soll.
Also nur in der Klasse wiederholen, die vererbte abstrakte
Methodensignatur, wenn tatsächlich implementiert wird.
Okay, das glaube ich ist alles, was dazu jetzt zu sagen ist, zu den
abstrakten Methoden.
Gibt es da noch Fragen dazu?
Nee.
Also, Sie merken schon, Java und UML haben sehr, sehr ähnliche
Konzepte.
Das werden wir jetzt gleich bei den Schnittstellen auch noch sehen.
Ist eigentlich genauso wie in Java und in anderen Programmiersprachen,
das ist einfach ein ausgereiftes Konzept, das sowohl in modernen
Programmiersprachen als auch in UML vorkommt.
So, Schnittstelle kennen Sie?
Schnittstelle ist eine total abstrakte Klasse, könnte man sagen.
Schnittstelle hat nur abstrakte Methoden und eventuell Konstanten.
Also, da sagen Sie, geben Sie sich eine Schnittstelle vor, zum
Beispiel den hier.
Sagen wir, wir haben ein Interface SMTP-Server, der hat Open, Send und
Close.
Open sendet an einen, öffnet einen Kanal, sendet, schickt etwas, sind
halt hier die Parameter, die sind nicht so wichtig.
Und davon gibt es jetzt eine Implementierung, die ist natürlich auch
vererbt.
Ich vererbe also auch die Schnittstelle an eine konkrete Klasse, wie
Sie tatsächlich schreiben, nur ist der Vererbungspfeil hier etwas
anders geschrieben.
Um anzudeuten, dass das eine Schnittstelle ist, nimmt man hier den
gestrichelten Pfeil.
Das ist natürlich nur ein wirklicher Detail.
Sie können noch das Realize dazuschreiben, das muss aber nicht sein.
Gestrichelt ist schon okay.
Das hier ist ein anderer Pfeil übrigens.
Sie kennen den Pfeil schon, der hatte einmal auch geheißen.
Hatten wir bei den Klassenobjekten schon gesehen.
Das ist ja der Uses-Pfeil, da muss allerdings Uses dranstehen.
Wenn Sie das zeigen wollen, ist wahrscheinlich in diesem E-Mail
-Programm ein Aufruf auf ein Objekt, das die SMTP-Server-Schnittstelle
implementiert.
Schreibweise ist wieder so, dass Sie in UML hier drüber Interface
schreiben.
Wieder in diesen Spitzenklammern.
Was ist da noch zu sagen?
Die Schnittstellen, ähnlich wie abstrakte Methoden, übertragen die
Verpflichtung, alle Methoden in dieser Schnittstelle zu
implementieren.
Damit kriegen Sie, wenn Sie so eine Klasse haben, von der Sie wissen,
Sie implementiert die SMTP-Schnittstelle, dann wissen Sie, Sie können
Open, Close und Send aufrufen.
Die Methoden gibt es dann.
Und wo immer so eine Schnittstelle benutzt wurde, können Sie ein
beliebiges Objekt einer beliebigen Klasse diese Schnittstelle
verwirklicht einsetzen.
Die Idee hinter der Schnittstelle ist zu sagen, ich gebe etwas vor,
das mir gewissermaßen Kompatibilität garantiert, dass ich an jeder
Stelle, wo diese Schnittstelle benötigt wird, auch entsprechende
Objekte habe, die die Schnittstelle erfüllen können und nicht
plötzlich eine andere Signatur haben, die einen extra Parameter
erfordert oder anders heißt oder sonst was das alles schwierig macht.
Und die Idee ist eben, dass Sie nur mit dem Wissen über die
Schnittstelle schon programmieren können.
Also denken Sie, Sie bekämen so einen schwarzen, eigentlich weißen
Kasten hier und Sie finden darauf Knöpfe wie Play, Stop, Fast Forward,
Rewind und Skip.
Das muss irgendwie ein Abspielgerät sein.
Ich weiß schon, wie ich das benutzen muss.
Nämlich in Play fängt das Ding zum Abspielen an.
Ob das jetzt ein MP3-Spieler ist oder ein Videorekorder oder ein DVD
-Spieler, das ist schon egal.
Das wären die verschiedenen Implementierungen dieser Schnittstelle.
Noch ein Punkt.
Bei Schnittstellen gibt es keine Vererbung.
Sie kriegen lediglich die Vorschrift, die Schnittstelle zu
implementieren.
Wenn eine Schnittstelle B eine Schnittstelle A erweitert, dann ist die
Menge der abstrakten Methoden von A einfach eine Teilmenge der Menge
der abstrakten Methoden von B.
Das ist eigentlich ganz klar.
Wenn eine Klasse X eine Schnittstelle implementiert, dann ist die
Methode der abstrakten Methoden von A eine Teilmenge der
Methodendefinition von X.
X darf also schon mehr enthalten, muss als Minimum die Schnittstelle
implementieren.
Und bei Schnittstellen ist es möglich, dass Sie eine Klasse haben, die
mehrere Schnittstellen implementiert.
Wenn also Schnittstelle sagen SMTP-Server und Schnittstelle
irgendetwas anderes.
Und eine Klasse, die beide Schnittstellen verwirklicht.
Im Gegensatz dazu gibt es keine, in den meisten Programmiersprachen,
C++ gibt es schon, keine Mehrfachvererbung.
Also Mehrfachvererbung würde heißen, Sie haben eine Klasse, die von
mehreren anderen Klassen erbt.
Ist übrigens in UML nicht verboten, die Mehrfachvererbung.
Aber die wird vermieden, weil sie Probleme verursacht.
Können Sie sich vorstellen, welches Problem das ist?
Also nochmal, Sie haben eine Klasse, die erbt von mehreren anderen
Klassen.
Zwei, sagen wir mal.
Wenn Sie gleichnamige Attribute oder Methoden haben, genau, da ist das
Problem, welche kriege ich jetzt?
Wenn beide mir eine Methode ageben, welche ist es jetzt, die ich
wählen soll, da muss ich irgendwelche Prioritätsregeln beachten und
sowas kann man alles machen, ist aber extrem umständlich und meistens
dann doch nicht richtig.
Kann das Problem bei den Schnittstellen nicht auch auftreten?
Nein, kann es nicht.
Warum nicht?
Genau, da ist noch nichts implementiert.
Ich habe zweimal die gleiche Schnittstelle, die ist identisch.
Also zweimal die gleiche Methode, Entschuldigung, mit den gleichen
Parametern.
Notfalls, wenn sie unterschiedlich sind, ist sowieso kein Problem.
Aber wenn sie gleich wären, naja, das ist ja nur die Signatur.
Und ich muss ja noch die Implementierung liefern, da kann ich nichts
erben davon.
Dadurch gibt es eben diese Zweideutigkeit oder Mehrdeutigkeit da
nicht.
Deswegen geht das gerade bei den Schnittstellen.
Aha, jetzt haben wir das wiederholt.
Ist natürlich klar, hat man wahrscheinlich schon irgendwann mal gesagt
in der Programmiervorlesung.
Und jetzt kommt eine seltsame Aussage.
Wir haben also eine Vererbung und betrachten jetzt einfach nur die
Signaturvererbung.
Und da hatten wir gesagt, die Signaturen müssen gleich sein.
Aber sie müssen gar nicht gleich sein, ich könnte sie ändern.
In einer bestimmten vereinfachten Form.
Und das ist jetzt ein bisschen umständlich.
Dazu brauchen wir jetzt vier einfache Begriffe.
Nämlich die sogenannte Parametervarianz.
Die Varianz bedeutet lediglich Modifikation der Typen der Parameter
einer überschriebenen Methode.
Also Sie haben eine Oberklasse, Unterklasse, eine Methode zeichnen in
der oberen, in der unteren implementieren Sie erneut, also
überschreiben.
Und ich hatte vorhin immer angenommen, wenn Sie das vererben, bleibt
die Signatur gleich.
Das ist aber nicht immer der Fall.
Und da spricht man von Varianz, wenn diese Parametertypen, auch der
Rückgabeparametertyp sich ändern kann in der erbenden Klasse.
Dann spricht man von Kovarianz.
Kovarianz heißt, ich nehme eine Spezialisierung des Parameters.
Also, wenn zum Beispiel in der Oberklasse in einem Parameter die
Klasse Person benutzt würde, würde Kovarianz bedeuten, dass ich in der
Unterklasse anstelle des gleichen Parameters zum Beispiel die Klasse
Student benutzen würde.
Wenn das eine Unterklasse von Person ist.
Das nennt man Kovarianz.
Kovarianz, weil dieser Parameter spezialisiert, genauso wie die
Subklasse selbst ist spezialisiert.
Deswegen Ko, also gleichzeitig spezialisiert.
Das ist die eine.
Das Gegenteil ist auch möglich, das nennt man die Kontravarianz.
Da wird in der vererbten Klasse eine allgemeinere, ein allgemeiner Typ
in den Parametern oder Rückgabewerten benutzt.
Und Invarianz ist dann natürlich exakt gleich.
Was ist dabei jetzt sinnvoll?
Das illustriere ich Ihnen jetzt mal.
Also, Beispiel von Ko- und Kontravarianz.
Hier ist eine Oberklasse P, eine Unterklasse Q.
Hier kommt eine Methode M vor und hier eine Methode M.
Hier wird M überschrieben.
Sie sehen, es hat zwar den gleichen Parameternamen, aber einen
unterschiedlichen Typ A und B.
Aber es ist so, dass A eine Oberklasse von B ist.
Das heißt, ich habe hier Ko-Varianz.
Sie können sich so merken, wenn Sie die Pfeile eintragen würden, gehen
Sie in die gleiche Richtung.
Hier der Vererbungspfeil zeigt nach oben und der hier zeigt auch nach
oben.
Das nennt man die Ko-Varianz.
Kontravarianz ist, wenn es gerade umgekehrt wäre, so wie hier.
Hier ist ein Parameter T benutzt.
Und was soll ich jetzt damit machen?
Naja, jetzt müssen wir überlegen, was wir davon erlauben könnten.
Stellen Sie sich vor, Sie müssten jetzt eine Programmiersprache
entwickeln, Java++ oder so etwas, und Sie überlegen sich, will ich Ko
-Varianz und Kontravarianz erlauben?
Und ein wichtiger Gedanke dabei ist wieder das Substitutionsprinzip.
Wenn ich Ko- oder Kontravarianz erlaube, ist dann das
Substitutionsprinzip gewährleistet.
Dazu ein Beispiel.
Sagen wir mal, da gibt es die Oberklasse Europäer und eine Unterklasse
Kroate.
Es gibt eine Oberklasse Währung und eine Unterklasse Euro.
Zur Verwirrung habe ich es gleich mal anders hingeschrieben, damit Sie
genau hinschauen.
So, und da haben wir eine Methode Nehmen bei beiden.
Bei dem einen steht Euro, bei dem anderen steht Währung.
Jetzt schauen wir mal.
Der hier hat das ist die Oberklasse, aber in der Unterklasse steht ein
allgemeinerer Typ.
Würde das klappen?
Ja?
Ja, natürlich ist das erlaubt.
Sie müssen sich jetzt Folgendes vorstellen.
Sie haben eine Variable E, die ist ein Europäer.
Sie programmieren mit dieser Variable und jedes Mal, wenn Sie die
Methode Nehmen aufrufen, wird der Übersetzer überprüfen, ob Sie auch
wirklich einen Euro liefern.
An der Parameterposition 1 hier.
Ja?
Und jetzt haben Sie diese Variable E, haben Sie immer gedacht, Sie
programmieren für Europäer allgemein.
Und jetzt kommt irgendein Programmierer auf die Idee, in diese
Variable E ein anderes Objekt reinzustecken, nämlich das vom Typ
Proate.
Das wussten Sie aber nicht.
Die Anwendungsprogramm ruft eben Nehmen auf.
Ist jetzt, wenn ich jetzt tatsächlich ein Objekt vom Typ Proate habe,
funktioniert das jetzt?
Ja, es funktioniert, denn Sie liefern einen Euro, also Euro-Typ als
Parameter.
Die nehmen vom Proaten sagt, ich nehme aber nicht nur Euro, ich nehme
eigentlich alles.
In beliebige Währung.
Also funktioniert das in diesem Falle.
Umgekehrt würde es nicht funktionieren.
Also, wir merken uns, Kontravarianz bei den Parametern läuft
problemlos.
Jetzt nehmen wir ein anderes Beispiel, Substitutionsprinzip.
Damit ich eben alle beleidige.
Wir haben einen Baden-Württemberger und einen Schwabe als Unterklasse.
Der Baden-Württemberger ist Nudel, der Schwabe ist Spätzle.
Als den Parameter.
Jetzt ist es so, dass die Nudel die Oberklasse ist und Spätzle die
Unterklasse.
Und jetzt machen wir das gleiche Spielchen.
Sie haben einen Baden-Württemberger angelegt,
programmiert, indem Sie Essen aufrufen und ihm Nudeln liefern.
Jetzt kommt jemand anderer auf die Idee und speichert in dem Baden
-Württemberger einen Schwaben.
Ist erlaubt, ist ja eine Unterklasse.
Darf ich ja.
Jetzt rufen Sie Essen auf und dann würde die Essenfunktion plötzlich
sagen, Moment, Moment, ich esse nur Spätzle.
Das Nudeln, das ist...
Und Sie hatten einen Fehler.
Das Substitutionsprinzip ist nicht erfüllt, im Gegensatz zu vorhin, wo
Sie sagten, bei der Kontravarianz geht das.
Jetzt haben wir aber nur die Hälfte davon gesehen.
Wir müssen nämlich auch noch den Rückgabeparameter berücksichtigen.
Rückgabeparameter ist blöd, weil der funktioniert in der umgekehrten
Richtung.
Vorher hatten wir beim Aufrufen, geben wir ein Parameter rein und der
wird dann in der Methode verarbeitet.
Beim Rückgabeparameter ist es umgekehrt.
Da wird von dem Unterklassenobjekt plötzlich was zurückgegeben.
Wenn ich da jetzt was Spezielleres zurückgebe, dann muss ich mir
überlegen, was ich da erlaube.
Und zwar beim Ausgabeparameter, aber auch beim Rückgabewert und bei
Ausnahmen, wenn Sie also Ausnahmen, Exceptions angeben können, da
brauchen Sie die Kovarianz.
Das heißt, was Sie zurückgeben, darf was Spezielleres sein, aber nicht
was Allgemeineres.
Wenn es nämlich was Allgemeineres wäre, was der Aufrufende erwartet,
was zurückkommt, dann fehlen möglicherweise Attribute oder Methoden,
die jetzt im Folgenden aufgerufen werden, an dem Spezielleren.
Also, merken, Kovarianzsorum, Kontravarianzsorum, das ist mal wieder
so ein Ding, das man gerne verwechselt.
Einmal genau anschauen, dann hat man das.
Und natürlich für Parameter, die gleichzeitig Ein- und
Ausgabeparameter sind, muss ich Invarianz verlangen.
Da kann ich keine Unterklassen oder Oberklassen bilden, in jedem Fall
würde es mir beim Ein- und Ausgabeparameter Schwierigkeiten bereiten.
Ein Ausgabeparameter gibt es jetzt in Java nicht, aber es gibt es zum
Beispiel in C-Sharp, da können Sie Parameter als In-Out bezeichnen und
kriegen Sie sowohl einen Wert rein, als auch einen Wert zurück.
Und dann müssen Sie dort Invarianz verlangen, denn sonst funktioniert
es mit dem Substitutionsprinzip nicht mehr.
So, Sie dürfen jetzt nachschauen, was davon in Java eigentlich erlaubt
ist.
Ist in Java Kontravarianz bei den Eingabeparametern erlaubt?
Ist Invarianz bei den Rückgabewerten erlaubt?
Und was ist mit den Ausnahmen?
Ist da eine Spezialisierung erlaubt oder Generalisierung erlaubt?
Okay, so.
Das war also der Punkt.
Kovarianz, Kontravarianz.
Die Vererbungshierarchie für Java Programmierer sollte man auch in
etwa wissen.
Double ist eine Oberklasse von Float, ist eine Oberklasse von Long,
ist eine Oberklasse von Int, ist eine Oberklasse von Short und Int ist
auch noch eine Oberklasse von Char und eine Oberklasse von Short, eine
Oberklasse von Byte.
Int ist also 32-Bit, Long ist 64-Bit, Float ist 64-Bit, Double ist 64
-Bit.
Das heißt, die Unterklassenbildung hat jetzt mit Co- und Kontravarianz
nichts zu tun.
Aber in allen Plätzen, wo ein Double erlaubt ist, dürfen Sie auch ein
Float einsetzen.
Das wird dann konvertiert.
Überall wo Sie ein Int haben, dürfen Sie ein Long haben, überall wo
Sie ein Float haben, dürfen Sie ein Long einsetzen, das wird auch
konvertiert, unter Umständen unter Verlust von Genauigkeit.
Nächster Punkt, Polymorphie.
Das hat natürlich mit der Vererbung zu tun.
Polymorphie, es kann mehrere Namen, Methoden mit dem gleichen Namen
geben, Signatur muss unterschiedlich sein, das ist das Überladen.
Das ist eine Form von Polymorphie, das hatten wir vorhin schon
gesprochen.
Oder, das hat jetzt nichts mit Vererbung zu tun.
Dynamisch ist Verwendung der Vererbung.
Da hat es jetzt damit zu tun, dass wenn Sie Methoden mehrfach
implementieren in Unterklasse 1 und der weiteren Unterklasse auf
nächster Ebene, da ist die Frage immer, welche Methode wird jetzt
eigentlich aufgerufen und das nennt man die Polymorphie.
Wenn mehrere Methoden eigentlich zur Verfügung stehen, Entschuldigung,
mehrere Implementierung derselben Methode mit der gleichen oder Kontra
-Kovarianz -Signatur zur Verfügung stehen, welche wird aufgerufen?
Und da ist die Antwort, man gehe in die Vererbungs-Hierarchie des
jeweiligen Objektes, das man besitzt, in der Hand hat, was immer das
für Variable ist, und suche nach dem nächsten gelegenen
Implementierung in dieser Vererbungs-Hierarchie, nach oben.
Bild...
Viele Leute wollen dieses Bild nachher haben und sagen, ah, endlich
verstehe ich das.
Also, betrachten Sie mal Folgendes, wir haben wie vorhin Hund, Wolf
und Polarwolf als Subklassen-Hierarchie.
Das hatten wir letztes Mal schon.
Wir haben aber jetzt überall eine Heulen-Implementierung.
Wir haben es einfach gemacht, weil, sagen wir, der Wolf anders heult
als der Polarwolf, weiß man sich.
Also haben wir es dreifach implementiert.
Wir haben eine Variable H, das ist Hund, das ist die oberste
Oberklasse, ist klar.
Ich habe eine Variable Wolf, der gebe ich einem neuen Wolf, das ist
also ein Objekt von der Klasse Wolf, hier ist das zugehörige Klasse.
So sieht das aus.
Dann mache ich Folgendes, ich weise dem Hund ein Objekt des Typs Wolf
zu, also H gleich W, das ist genau das, was ich immer gesagt habe, man
habe ein Oberklassenelement und das bekommt eigentlich zugewiesen ein
Unterklassenelement.
Das ist ja legal.
Ja?
Und jetzt sage ich H Punkt heulen.
Welche Methode wird jetzt aufgerufen?
Die hier, die hier oder die hier?
Wie finde ich das raus?
Und es geht so, die Variable Wolf, Entschuldige, Variable W zeigt
natürlich auf das Objekt Wolf, ist klar.
Hatten wir vorhin schon.
H, durch diese Zuweisung, ist ebenfalls ein Verweis auf das Objekt
Wolf.
Jetzt erfolgt der Aufruf heulen an H.
Was ich jetzt mache, ich verfolge den Verweis, habe jetzt das Objekt
Wolf und viele Leute nehmen jetzt an, jetzt müsste ich plötzlich
dieses Tier aufzurufen, denn es ist ein H.
Das ist nicht der Fall.
Ja?
Es ist ein Hund, sondern was passiert ist, ich verfolge diesen Pfeil
hier hin, bekomme einen Wolf, schaue in der Klasse der zugehörigen
Klasse nach und rufe diese Methode auf.
Wenn dort keine stehen würde, denken Sie sich die mal einen Augenblick
weg, dann würde ich die Vererbungshierarchie hochgehen und nach dem
nächsten Heulen suchen.
Das wäre in diesem Fall natürlich der hier.
Jetzt denken Sie sich, da gäbe es noch eine Oberklasse drüber, da wäre
Heulen ebenfalls implementiert, aber hier nicht und hier nicht.
Ja?
Dann würde die Suchstrategie so gehen, von H zum Objekt Wolf, zur
Klasse Wolf, die hat kein Heulen, also schaue ich in der nächsten
Oberklasse nach, die hat auch kein Heulen und so weiter.
Ja?
So läuft das.
Aber ich gehe nicht nach unten.
Das ist die Mimik, die zum Beispiel auch in Java oder C++ und so
weiter eingebaut ist.
Gibt es da Fragen dazu?
Das ist jetzt sonnenklar.
Nie wieder ein Problem mit Polymorphie von heute an.
Man sähe einfach auf das Objekt, die zugehörige Klasse und dann sucht
man.
Ja?
So macht das Laufzeitsystem das tatsächlich.
Ein spezieller Name in der Compilertechnik, das nennt man Method
Dispatch.
Was gibt es beim Klassen... dann hätten wir also das Vererbungskapitel
jetzt abgeschlossen, auch die Polymorphie abgehakt, Schnittstellen
hinter uns, Kovarianz und Kontravarianz ist vielleicht ein neues
Konzept für Sie.
Was gibt es denn beim Klassendiagramm noch, außer Klassen und
Assoziationen und Vererbung?
Sie können Anmerkungen oder Quelltext angeben, das zeichnet man mit so
einem Kästchen mit so einer abgeknickten Eselsohr.
Können Sie zum Beispiel für diese Operation reinschreiben, was der
grobe Programmiertext ist?
Wäre es zum Beispiel möglich, manchmal ist es eben nur so kurz, dass
man es einfach hinschreibt und dann sind alle Unklarheiten beseitigt
oder man kann Notizen oder Kommentare eingeben.
Sie können auch Abhängigkeiten angeben, das hatten wir vorhin schon
gesehen, an einem Beispiel Uses, wenn die Klasse 1 entweder Objekte
der Klasse 2 aufruft oder Parameter hat, die vom Typ Klasse 2 sind und
das wollen Sie vermerken.
Dann kann man noch das Uses hinzufügen, ist aber nicht Vorschrift.
Nur wenn Sie das wirklich vermerken wollen, das bedeutet dann meistens
auch, dass die beiden Klassen im gleichen Paket sitzen müssen, dass
die entsprechenden Klassen auch bekannt sind.
Das letzte ist der Zugriffsschutz.
Das kennen Sie ebenfalls aus Java, Privat und Protected.
Das ist im UML einfach ganz einfache Notation, da gibt es die Notation
für ein Minus, dann ist es geschützt oder privat oder das Privat, das
Gatterzeichen oder das Fizz oder sowas, wie Sie nennen wollen, für
geschützt oder Protected und Plus für öffentlich Public.
Die Definitionen sind ziemlich gleich.
Wenn Sie etwas mit Plus verziehen, eine Methode mit Plus voranstellen,
dann heißt es, jedes andere Objekt darf diese Methode aufrufen oder
auf dieses Attribut zugreifen.
Privat heißt nur Objekte derselben Klasse.
Das heißt nicht, Privat heißt nicht nur dieses eine Objekt, sondern
wenn Sie zwei Objekte haben, dann dürfen Sie gegenseitig, auf
derselben Klasse, Ihre privaten Attribute und Methoden benutzen.
Also nicht so privat, wie man oftmals annimmt.
Und geschützt, Protected oder eben Doppelkreuz, alle Exemplare
derselben Klasse und ihre Unterklassen, abgeleiteten Klassen, sowie
Exemplare aus dem gleichen Paket.
Eigentlich, das können Sie sagen, ist Paketglobal
aber für andere Pakete nicht sichtbar.
Wenn zum Beispiel eine Unterklasse gebildet wird, eine Klasse, die in
einem anderen Paket sitzt und diese Klassenattribute bezeichnen Sie
als Protected, dann darf das andere Paket auf die nicht zugreifen.
Obwohl es eine Unterklasse ist.
Diese Einschränkungen sind meistens sehr praktisch, aber wohlgemerkt,
sie eignen sich nicht dafür, um Software zu schreiben, die gegen böse
Angriffe gefeit ist, mithilfe dieser Schutzmechanismen.
Denn diese Schutzmechanismen kann ich hinterher im Programm total
umstellen.
Ich kann sie löschen, ändern, neu hinzufügen.
Also diese ganzen Zugriffsrechte gelten nur zur Übersetzungszeit und
zur Laufzeit kann ich sie im Prinzip ändern.
Ich weiß nicht so recht, warum ich das machen würde, aber es ist
jedenfalls möglich und ein Angreifer könnte das ausnutzen.
Also wiegen Sie sich in keine Sicherheit, wenn hier Minus steht, die
anderen Klassen dürfen das eh nicht sehen, das ist gar nicht wahr.
Wenn einer will, wird er einen Weg finden, diesen Schutz zu
durchbrechen.
Also jetzt ein Beispiel zur Sichtbarkeit.
Sagen wir mal, Sie hatten einen Kreis und sagen, den Radius wollen Sie
verdecken.
Das heißt, alle Instanzen von Kreis können natürlich den Radius
anderer Kreise sehen.
Das X und Y ist geschützt, das heißt, ich darf sie nur im Paket sehen
oder an Unterklassen.
Die X und Y des Zentrums.
Und diese Methoden hier, mit Plus verziert, sind alle öffentlich.
So schreibt man das in UML.
Fragen dazu?
Das Plus können Sie auch weglassen, dann ist es öffentlich.
Keine Fragen?
Okay.
Also wenn Sie mehr über UML 2.X wissen wollen, dann gibt es auch den
Standard, aber Warnung, der Standard ist lang.
Aber wenn es mal um Fragen geht, dann kann man da natürlich
nachschauen.
Wenn Sie meinen, ich hätte was Falsches verzapft, nachschauen.
Sagen, lieber Herr Tichy, aber im Standard steht.
Kann mir natürlich passieren.
Dann haben wir ein paar Übungen, die bespreche ich jetzt nicht.
Wir wären dann fertig mit den Klassendiagrammen.
Gibt es vielleicht noch global zu den Klassendiagrammen Fragen?
Assoziationen ist ein schwierigeres Kapitel, da muss man vielleicht
genau hinschauen, diese Schutzmechanismen, Vererbung und so, dürfte
Ihnen schon längst bekannt sein.
Ja, bitte.
Sie meinen Bibliotheksklassen?
Ja, also UML benutzt man dafür, um neue Software zu schreiben.
Also, wenn Sie wollen, können Sie natürlich so ein Uses-Peil eingeben,
aber dann müssen Sie die Bibliotheken noch nachmodellieren.
Der Sinn davon ist halt Fleißarbeit.
Ich weiß natürlich nicht, ob es vielleicht UML-Modellierungen gibt,
der Klassen, die in den Bibliotheken sitzen, aber würde man
normalerweise nicht machen.
Das ist einfach nicht notwendig.
Noch Fragen?
Okay.
So, dann, wenn es keine Fragen mehr gibt, dann schauen wir uns den
nächsten Abschnitt an.
Es gibt nämlich noch ungefähr, fast hätte ich gesagt, 26 weitere
Diagramme.
So viel sind es nicht ganz, aber es gibt schon noch einiges weiteres.
Eines davon,
also weitere UML-Diagrammtypen, wir werden, Sie müssen nicht alle
lernen, ja.
Wir lernen jetzt noch zwei.
Noch über zwei.
Die sind auch nicht schwierig.
Und ein drittes ist hier nochmal ausgeführt.
Hier ist der ganze Strauß von UML-Diagrammen.
Also wir hatten schon das Klassendiagramm behandelt.
Das Anwendungsfalldiagramm hatten wir auch schon behandelt.
Das kommt jetzt nochmal, wird nochmal kompakt aufgeschrieben.
Das überspringe ich jetzt aber, ja.
Wir hatten ja das schon behandelt, als wir beim Lastenheft waren.
So, dann gibt es noch, naja, Sequenzdiagramme.
Wir schauen uns noch ein paar an.
Das Zustandsdiagramm zum Beispiel, sind es doch noch drei.
Aktivitätsdiagramm schauen wir an.
Das Paketdiagramm ist etwas ganz, ganz einfaches.
Interaktionsdiagramm werden wir gleich sehen.
Also Usecasediagramm, Anwendungsfalldiagramm überspringe ich.
Wissen Sie schon, wie das geht.
Das sind die Strichmännchen und die Kästchen, ja.
Von der Sorte.
Das sind jetzt mehrere Beispiele.
Wir schauen uns die Aktivitätsdiagramme als nächstes an.
So, ein Aktivitätsdiagramm beschreibt einen Ablauf.
Zum Beispiel technische Abläufe im Programm, aber auch Workflows, also
Ablaufdiagramme für Vorgänge, also die auch menschliche Arbeit mit
einbeziehen oder auch ausschließlich aus menschlichen Arbeitsschritten
bestehen.
Kann man sich auch benutzen, das Aktivitätsdiagramm.
Gibt auch konkrete algorithmische Abläufe.
Was am Aktivitätsdiagramm besonders interessant ist, dass es im
Gegensatz zu vielen anderen Diagrammen auch erlaubt, nebenläufige
Prozesse, also gleichzeitig ablaufende Heilprozesse grafisch
darzustellen, plus deren Synchronisation.
Die Technik dafür ist ganz einfach.
Einfaches Diagrammpunkt, wo sich der Ablauf aufspalten kann in mehrere
Fäden, mehrere Abläufe, und dann wieder zusammengeführt werden und
aufeinander gewartet wird.
Wir haben im Aktivitätsdiagramm Aktions-, Objekt- und Kontrollknoten.
Objekt- und Kontrollflüsse.
Die Semantik ist an sogenannten PETR-Netzen angelehnt.
Hatten Sie PETR-Netze mal behandelt?
In den Grundlagenvorlesungen?
Nee?
Okay, aber es macht nichts, es ist nicht schwierig.
Sie erinnern sich an dieses Diagramm, das Wasserfalldiagramm.
Das ist bereits ein Aktivitätsdiagramm.
Das heißt, Softwareentwicklung hat diese vier, sechs Stufen, oder
vielmehr Aktionsknoten heißen die Dinger.
Also das wäre der Aktivitätsname.
Das muss man halt so in Kästchen malen.
Das ist eine Aktion oder Aktionsknoten, aber auch Ablaufschritt oder
Aktivität genannt.
Das dürfen Sie alles benutzen dafür.
Zusätzlich brauchen wir einen Startknoten, der sagt, hier geht es los.
Da muss man sich das dann so vorstellen, dass hier Marken durch die
Gegend fließen oder aber Objekte.
Ich zeige Ihnen gleich welche.
Wenn wir hier ankommen mit dieser Marke, das ist der Endknoten, wenn
hier diese Marke auftaucht, dann endet diese Aktivität, wie in dem
Aktivitätsdiagramm beschrieben ist.
Wir können außerdem noch angeben, an diesen Übergängen, von einer
Aktivität zur nächsten, welche Daten überliefert werden.
Das ist dann der Datenfluss.
In unserem Fall ist es so, dass die Planung, das Lastnav, Projektplan
und die Kalkulation liefern soll, an die nächste Phase, die Definition
der nächsten Aktivität und so weiter.
Pflichtnav, GUI, Beschreibung, Benutzerhandbuch an den Entwurf, der
Entwurf liefert die Entwurfsdokumente, Modulführer, werden wir noch
sehen, wie das aussieht und so weiter.
Das muss man sich halt wieder merken.
Das ist blöd, aber gerundete Ecken bedeuten immer Aktivitäten.
Eckige Dinger bedeuten Objekte, die übergeben werden, also Daten.
Eine Aktion wird so geschrieben, grün muss es nicht sein, aber
jedenfalls ein Kästchen mit gerundeten Ecken.
Es gibt auch verschachtelte Aktionen, das können Sie jetzt schwer
erkennen, ich auch kaum schwer, da ist so eine Gabel drin.
So ein Gäbelchen, das bedeutet nur, dass diese Aktivität B aus
mehreren Teilaktivitäten besteht und genauer beschrieben werden kann.
Das wäre der Startknoten, das hatten wir schon, Endknoten hatten wir
auch.
Da gibt es noch einen anderen Endknoten, das ist der hier.
Wenn Sie einen Ablauf haben, der sich aufspaltet in mehrere Äste und
Sie würden mit einer Marke auf sowas laufen, dann müssten alle Abläufe
sofort gestoppt werden.
Wenn Sie aber auf den hier laufen, dann heißt es nur, dieser Ast ist
jetzt beendet und was sonst noch vorkommt, das kann weiterlaufen.
Außerdem gibt es, das Aktivitätsdiagramm ist eigentlich so eine
Mischung zwischen Datflussdiagramm und Kontrollflussdiagramm.
Diese Raute bedeutet eine Entscheidung, das ist ein uralt Diagramm,
das habe ich noch als Schüler kennengelernt.
Das heißt, Sie schreiben Bedingungen dran und nehmen den Ast oder den
Ast.
Das heißt also, der Kontrollfluss verzweigt sich.
Und natürlich, wenn Sie eine Verzweigung haben, das ist eine
Entscheidung, da wird nur ein Ast, einer von beiden Ästen genommen,
wie bei der IF-Anweisung.
Das ist auch ganz einfach, da gibt es aber eine Zusammenführung, das
ist auch die Raute und da geht es weiter.
Da stehen keine Bedingungen dran, dann wissen wir, das ist eine
Zusammenführung.
Die Pfeile deuten auch das an.
Das ist ziemlich einfach.
Aber ich sagte vorhin etwas von Parellenabläufen.
Und das ist, wo das sogenannte Petrenetz reinkommt.
Dieser Balken hier bedeutet, dass wenn der Kontrollfluss an der Stelle
ankommt, der Kontrollfluss kommt an der Stelle an, wenn diese Marke
erscheint, dann spaltet er sich auf und das wird wieder so erledigt,
dass beide dieser wegführenden Pfeile auch eine Kontrollmarke
bekommen.
Das heißt also, die beiden Marken können jetzt parallel durch dieses
folgende Geäst oder Grafen laufen.
Wenn ich das habe, brauche ich auch das Gegenteil.
Nämlich die Synchronisation, das ist eine Unverknüpfung, die sagt, an
der Stelle geht es nur weiter, wenn an beiden Stellen hier mindestens
eine Marke angekommen ist.
Das ist also eine Unverknüpfung, die dazu führt, dass ich
Parallelabläufe sagen kann, so und jetzt warte auf den anderen.
Und wenn der andere da ist, dann machen wir weiter hier in diesem Fall
mit einem einzigen Arbeitspfaden.
Ich könnte auch mehrere Pfade anführen, ich könnte zum Beispiel hier 5
angeben oder 2 unten, bitte da zu aufpassen, das vergisst man oft.
Dann heißt dieser Block hier, die Marken, die hier oben sind, werden
komplett entnommen und jeweils eine Marke den wegführenden Pfeilen
gegeben.
Das heißt, wenn ich 5 wegführende Pfeile hätte, könnte ich von 2 aus 5
machen.
Wenn ich 2 auf 2 habe, also genauso viele reinkommen und die
herausgehen, dann habe ich eine Barriere.
Barriere sagt, alle warten aufeinander, je nachdem wie viele es sind.
In diesem Falle 2.
Sobald alle da sind, machen beide wieder weiter.
Das ist also auch möglich.
Okay.
So, die Abfolge der Aktionen, das habe ich jetzt schon mehrfach
erwähnt, wird durch Kontrollfluss oder Objektflusskanten bezeichnet.
Auf den Kontrollflusskanten wandern nur Kontrollmarken, die sagen mir,
wo bin ich?
Sowas wie im Buffetszeller.
Auf den Objektflusskanten wandern Objektmarken, die sind einfach,
können Sie sich vorstellen, an den Objekten angeklebt.
Und eine Aktion kann erst ausgeführt werden, wenn alle einführenden
Kanten an eine Aktion entweder die Kontrollfluss- oder die
Datenflussmarken tragen.
Objektflusskanten.
Wenn wir also an so einer Aktion sind, dann werden die Marken
entnommen, eine Objektmarke von jedem Objektflusskanten und alle
Kontrollmarken.
Das ist unterschiedlich zum Peternetz, da werden, wenn mehrere da
sind, immer nur eine entnommen.
So, jetzt Zeit für ein Beispiel.
Achso, das hatte ich schon gesagt, alle ausgehenden Kanten bekommen
eine neue Marke.
Also nicht so, dass die Marken durchgehen, und dann habe ich eine und
dann kriegt nur einer welche, sondern die Reinkommenden werden
verschwinden und alle Ausführenden bekommen ihre eigene Marke.
Also kein Markenerhalt.
So, das ist jetzt eine kurze
Visualisierung.
Also wir haben dieses Ding hier, da haben wir alles drin, was wir
brauchen, nämlich hier so eine Verzweigung.
Nein, das ist eine Zusammenführung.
Das hier ist eine Verzweigung, das sind Aktionen und das ist diese
komische Synchronisierungskante.
Hier ist eine Endmarke, das ist die Anfangsmarke.
Also gut, wir setzen uns eine Anfangsmarke hin.
Die Anfangsmarke geht hier rein.
Das ist natürlich eine Zusammenführung.
Deswegen ist hier eben die Zusammenführung, das sind die Einführenden.
Mir geht es also durch.
Da ist offenbar eine Schleife dabei.
Jetzt ist diese Aktion aktiv.
Wenn die Aktion fertig ist, erscheint außen wieder eine neue Marke.
Jetzt geht sie in den Bedingungsknoten.
Mussten irgendwelche Bedingungen abgefragt werden.
Mal schauen, was passiert.
Sie geht zum C.
Jetzt hat C an allen eingehenden Kanten ein Objekt gehabt.
Eine Kontrollfußmarke, muss ich sagen.
Jetzt ist C aktiv.
Als nächstes passiert folgendes.
Hier erscheint jetzt eine Marke.
Das ist eine Kontrollfußmarke.
Das hat den Effekt, wie ich Ihnen sagte, dass alle wegführenden Kanten
ebenfalls eine Kontrollfußmarke bekommen.
Die sind jetzt für den da und für den da.
Was passiert?
Jetzt haben wir tatsächlich schon eine Parallelverarbeitung, denn eine
Marke geht an D und eine geht an A.
Eigentlich ist A aktiv und D könnte auch aktiv sein, aber eigentlich
tatsächlich nicht, denn hier fehlt noch eine Marke.
An allen eingehenden Kanten muss eine Marke da sein, entweder ein
Objekt oder ein KFK, Kontrollfußkante.
Also gut.
Schauen wir weiter, was noch passiert.
Zur Abwechslung gehen wir wieder durch C.
C macht wieder das Gleiche.
Kante erscheint dort.
Beide ausgehende Kanten bekommen ihre Marken.
Das heißt, jetzt ist wieder A aktiv.
Bei D sind jetzt zwei da, aber D kann immer noch nicht arbeiten, denn
hier fehlt noch eine Marke.
So, jetzt zur Abwechslung gehen wir mal zu B.
B ist jetzt aktiv.
Nächster Schritt.
Marke an D liefern.
Jetzt sagt D, ach, jetzt habe ich Marken überall.
Jetzt werden alle Marken weggenommen, weil das sind Kontrollfußmarken,
keine Objektmarken.
Und D wird aktiv.
Schließlich sagt D, jetzt bin ich fertig, hier kommt die Marke rein in
den Endknoten und wir sind fertig.
Schön, oder?
Was glauben Sie, wie lange wir darin gearbeitet haben?
Danke.
Okay.
Alle Klarheiten beseitigt.
Ja, bitte.
Können Sie einen Endknoten mit mehreren Enden haben?
Nein.
Dann müssen Sie nur einen Balken davor setzen, wo die mehreren
reingehen und wenn die alle da sind, dann geht es weiter.
Gute Frage, aber hatte ich mir nicht überlegt, aber ich glaube, das
ist richtig, was ich sage.
Noch Fragen?
So, sieht alles relativ einfach aus.
Die Schwierigkeit ist nicht, sage ich Ihnen glaube ich schon mal, die
Syntax.
Also, was ist Aktion und was ist Kontrollfuß und was ist Marke und
wenn Sie so ein Diagramm haben, was passiert eigentlich?
Ich glaube, das haben Sie jetzt schon gemerkt.
Die Schwierigkeit wird sein, aus einer mündlichen oder einer
schriftlichen Beschreibung, also einer textuellen Beschreibung, so ein
Aktivitätsdiagramm zu entwickeln.
Und das ist das, was Sie üben müssen.
Deswegen werden wir entsprechende Aufgaben auf den Übungsblättern
haben.
Manchmal sind schon ganz einfache Aufgaben plötzlich unglaublich
kompliziert, weil man sich nicht so recht weiß, was muss ich denn
jetzt da alles tun.
Und man Schwierigkeiten hat, die einzelnen Aktionen aufzuteilen auf
verschiedene Schritte und deren Schritte zu synchronisieren.
Aber wie gesagt, das lasse ich Sie dann üben.
Es gibt noch ein paar Variationen, die weniger wichtig sind.
Ich gebe sie nur der Vollständigkeit mit an.
Das Aktivitätsdiagramm können Sie in verschiedener Art und Weise
verschönern.
Sie können an den Aktionen sogenannte Stecker anbringen.
Pins, nennen Sie die.
Stecker.
Da können Sie hinschreiben, welche Objekte eigentlich reingehen sollen
und welche Objekte rausgehen sollen.
Wir können es auch so darstellen, ohne die Stecker.
Und stattdessen, wie wir es ganz am Anfang gehabt hatten, das
Eingabeobjekt und das Ausgabeobjekt als diese eckigen Rechtecke als
Rechtecke anzugeben.
Oder Sie können es auch so angeben.
Alles ist erlaubt.
Bei der Schreibweise in Klausuren usw., weil man in der HAST
vielleicht nicht saubere Ecken zeichnen kann, würde ich stark das hier
empfehlen.
Sie schreiben die Aktionen aus Knoten, die Kanten dazwischen, für den
Kontrollfluss bzw.
Datenfluss und wenn Sie Datenobjekte angeben müssen, dann nehmen Sie
das hier.
Dann ist ganz klar für uns, selbst wenn hier mal eine gerundete Ecke
aufscheint, weil Sie irgendwie im Stift oder so daneben kommen, dann
haben wir keine Probleme, das zu interpretieren.
Wenn Sie sowas schreiben, kann es schon sein, dass wir nicht wissen,
ist das jetzt eine Aktion, ist das eine Eingabe oder was ist das?
Das hier ist nicht empfohlen.
Das hier ist empfohlen und das hier geht natürlich auch.
Ist aber wieder mehr Schreibaufwand.
Parameter von Aktivitäten.
Jetzt kommen die Verfeinerungen.
Wenn so eine Aktivität, die noch aufgespaltet ist, können Sie so ein
Gäbelchen reinmachen.
Das heißt, auf einem anderen Blatt möglicherweise, gibt es noch eine
ausführlichere Aktivität.
Die können Sie dann angeben auf der Art und Weise.
Die Eingabe ist vom Typ 1, die Ausgabe ist vom Typ 2 und hier ist der
Eingabe und der Ausgabe Parameter und hier drin selbst darf noch ein
ganzes Diagramm sitzen.
Wenn Sie also gewissermaßen eine Methode angeben wollen, so eine
Methode wie in aller Programmierung, ich will das jetzt nicht in
dieses eine Programm alles reinquetschen, sondern ich will ein Stück
rausnehmen und woanders aufzeichnen, zeigen, wie das funktioniert,
dann sagen Sie an einer Stelle Aktivität mit Gabel, das ist dann so
etwas wie ein Unterprogramm.
Und dann haben Sie hier die Subaktivität mit dem ganzen Diagrammzeug
beschrieben.
Das macht die Diagramme eben übersichtlicher.
Erinnern Sie sich, wofür wir das machen?
Wir sind ja jetzt in der Phase, wo wir sagen, wir müssen jetzt das
Pflichtenheft aufstellen und im Pflichtenheft wollen wir ja eindeutig
für den Programmierer sagen, was zu tun ist.
Und dem wollen wir diese Abläufe mitschreiben.
Es muss also nicht sein, dass ich für jede Zeile Code zu einem
Aktivitätsdiagramm schreibe.
Sondern nur da, wo eben die Abläufe dem Programmierer erst erklärt
werden müssen.
Auf einer möglichst abstrakten Ebene.
Wir müssen also nicht hier zum Beispiel für die Aktivität sortieren,
ein Sortierdiagramm hier unten hinschreiben.
Obwohl wir das könnten.
Nehmen wir an, dass Sie etwas über Sortieren inzwischen wissen, oder
wenn nicht, dann in der Algorithmenvorlesung kommt es demnächst.
Noch eine Verschönerung.
Das sind die sogenannten Partitionierungen.
Partition A oder B zum Beispiel haben wir hier.
Da können Sie sagen, das könnte zum Beispiel Ihr Rechner sein, Ihr
Klientenprogramm, Ihr persönlicher Laptop oder was immer.
Und das hier in der Partition B könnten die Teile des Diagrammes
sitzen, die auf einem Dienstgeber, auf einem Server ausgeführt werden.
Deswegen macht man diese Partitionen.
Das hier läuft auf meinem Rechner, Schritt 1 und Schritt 3, aber
dazwischen liegt ein Schritt 2, der wird aber von einem entfernten
Rechner ausgeführt.
Wenn man also verteilte Systeme angibt, dann kann man diese
Partitionen einführen.
Um anzudeuten, wo was läuft.
Okay.
Das nennt man auch den Verantwortlichkeitsbereich.
Wenn das diagrammatisch blöd wird, weil es, sagen wir, Sie haben fünf
Partitionen und die Pfeile gehen nur noch kreuz und quer über das
Blatt Papier, weil Sie immer von einer Partition in die andere
wechseln, aber die Partition trotzdem festgehalten werden soll, können
Sie es auch anders schreiben.
Bei OML gibt es immer mindestens zwei Möglichkeiten, was zu schreiben.
Dann geben Sie die Partitionen mit in dieser Aktion mit.
Dann sagen Sie also hier Partition A, Partition B und wieder Partition
A.
So geht das also auch.
Ändert aber nichts an der Funktionalität, sagt bloß, wo es hingehört.
Wenn Sie zum Beispiel denken an das Toll Collect System, das hatten
wir mal kurz besprochen, da gibt es natürlich unterschiedliche
Partitionen.
Da gibt es die Partition, die sagt, das Gerät im Fahrzeug, im Laster,
dann die Zentrale, die die Abrechnung macht, dann gibt es
wahrscheinlich noch die Partition, die die Überwachung macht, diese
Überwachungsbrücken, die die vorbeifahrenden Laster abfilmen.
Okay, Aktivitätsdiagramme.
So, jetzt kommt noch das Interaktionsdiagramm.
Gibt es Fragen zu dem?
Okay, dann machen wir weiter.
Hier kommt noch das Interaktionsdiagramm.
Es gibt noch mehr Diagramme.
Da gibt es sogar unterschiedliche Arten.
Interaktionsdiagramm gibt es drei oder vier verschiedene Stück.
Wir müssen sie nicht alle wissen, ich zeige es Ihnen nur mal kurz, wie
sie so aussehen.
Da gibt es ein Kollaborationsdiagramm, es gibt ein Zeitdiagramm, es
gibt ein Interaktionsübersicht und es gibt ein Sequenzdiagramm.
Wir schauen uns das Sequenzdiagramm an.
Wir sehen, wie die aussehen.
Dieses Kollaborationsdiagramm sieht irgendwie so aus.
Das Zeitdiagramm, das brauchen Sie für digitale Schaltungen eigentlich
eher, deswegen lassen wir das auch weg.
Das Interaktionsdiagramm, naja, ist auch nochmal kompliziert, lassen
wir auch weg.
Aber das Sequenzdiagramm, das schauen wir uns an.
Wird also nicht geprüft, das Sequenzdiagramm schon.
Ja, so, das zeigt auch einen Ablauf, aber ganz anders als das
Aktivitätsdiagramm.
Das zeigt Ihnen eher auf die Methodenaufrufe, die erfolgen, von einem
nach dem anderen.
Es gibt auch Varianten und Unterscheidungen und Schleifen und alles
mögliche ist da drin.
Hier geht es aber um den zeitlichen Verlauf von einzelnen Aktionen.
Und das sieht etwa so aus, intuitiv benötigt es aber viel Platz.
Das sieht also folgendermaßen aus.
Oben drüber stehen immer Objekte.
Das wäre zum Beispiel ein Objekt vom Typ Artikelreservierung.
Sie erinnern sich, eckige Kanten, unterstrichen, heißt Objekt,
Doppelpunkt davor, anonymes Objekt.
Und hier haben wir noch ein anderes Objekt, ein Artikellager, das ist
also schon sehr nahe an der Programmierung.
Für diese Objekte gibt es eine Lebenslinie, das ist diese gestrichelte
Linie und diesen komischen Balken drüber, das nennen die in UML die
Schwimmbahn.
Und die startet, indem man einen Aufruf macht.
Zum Beispiel bedeutet das hier, ich schicke eine Nachricht an dieses
Objekt, in diesem Fall anonym, interessiert mich nicht, welchen Namen
es hat, aber es bekommt jetzt einen Aufruf an eine Methode, Reserviere
mit dem Parameter B.
Dann läuft also hier auf dieser Lebenslinie hier in diesen Balken eine
Aktivität ab.
Sequenziell meistens.
Das nennt man dann auch noch den Steuerungsfokus und schließlich gibt
es den Rückgabewert, das ist der Antwort.
Selbstverständlich kann es sein, dass diese Artikelreservierung eine
Bestellung braucht, da gibt es ein weiteres Objekt B, das ist eine
Bestellung.
Sie sehen, hier kam das B rein als Objekt, also zeichnet das rein,
kann ich gut gebrauchen.
Das sagt jetzt also, geben wir die Bestellposition auf der Bestellung
und gibt es zurück.
Ja.
Das wäre also hier bei der Rückgabewert.
Das ist also ein Aufruf rüber an Bestellung mit der Funktion gibt
Bestellung Bestellposition.
Läuft kurz ab und kehrt zurück.
Dann gibt es nochmal Bestellpositionen, das heißt was wir hier sehen
ist, da gibt es einen weiteren Aufruf an die Bestellposition hier.
Das ist eine weitere, die kam hier zurück, sehen Sie.
Die wird hier benutzt.
Dann gibt man den Artikel, der Artikel kommt zurück und dann gibt man
die Anzahl von dem Artikel und ich bekomme das zurück.
Das ist also nichts weiter als Aufrufe an andere Objekte.
So wird das gezeichnet.
Und dann gibt es noch Artikel und Anzahl im Artikellager.
Das heißt also, dieses Sequenzdiagramm zeigt Ihnen so nach und nach,
welche Aufrufe geschehen sollen an den verschiedenen Objekten.
Ganz praktisch, kann aber sehr schnell groß und unhandlich werden.
Und jetzt kommen die Feinheiten.
Gibt es soweit vielleicht schon eine Frage?
Hier sehen wir einfach, wie die Reservieroperation eigentlich abläuft.
Die Dauer der Aktion ist ein bisschen implizit, es ist aber keine
Zeitachse wie vorgegeben.
Die Zeit verläuft von oben nach unten, aber ich habe keine Auflösung.
Das heißt, Sie können das langzeichnen oder kurzzeichnen, egal.
Meistens so, dass man es halt noch sehen kann.
Und die hier natürlich, da sagt man, dieses Objekt ist natürlich so
lange aktiv, als auch Unteraufrufe geschehen sind.
Also muss diese Spalte mindestens so lange sein, wie der letzte
Rückkehrer zurückkehrt.
Ja?
Noch Fragen?
Ja?
Ja, das ist auch ein anonymes Objekt.
Ja, klar.
Ich habe es hier nur eingeführt, B und B-Post, weil sie hier
vorkommen.
B -Post zumindest kommt hier vor und das B kommt auch vor.
Damit es zusammenhängt.
Also das sind Sie frei.
Wenn Sie es brauchen, einführen.
Wenn nicht, dann nicht.
Wenn Sie es nicht brauchen, dann nicht.
Synchrone Nachricht, da gibt es offenbar asynchrone Nachrichten auch.
Und die Antworten, die kennen wir schon.
Also der eine Pfeil ist hin zum Aufruf, die Antworten gestrichelt und
zurück.
Dürfen übrigens weggelassen werden, aber wir haben sie hier nicht
weggelassen.
Also meistens lässt man, wenn nichts besonders zu sagen ist, zum
Beispiel hier Reserviere Artikelanzahl, da kommt ja auch nichts
zurück, kann ich auch diesen gestrichelten Rückwärtspfeil weglassen.
Dann bleibt also nur das hier stehen.
So und die asynchrone Nachricht, also hier dieser Aufruf hat eine
gefüllte Spitze, die asynchrone Nachricht hat eine offene Spitze.
Das bedeutet zum Beispiel hier, Sie sehen hier, es sind alle
Nachrichten mit gefüllter Spitze.
Wenn ich das hier offen lassen würde, dann würde das bedeuten, dass
dieser Aufruf abläuft, während hier weitergerechnet wird.
Man könnte zum Beispiel an der Stelle zwischendurch ein anderer Aufruf
erscheinen, zum Beispiel hierhin.
Oder an ein anderes Objekt.
Das ist erlaubt.
Okay?
Also der Steuerungsfokus ist, dass diese Überlagerung der Lebenslinie,
so heißt diese gestrichelte Linie von oben nach unten, das nennt die
Lebenslinie, so lange ist dieses Objekt existent.
Mit einem senkrechten Balken oder Schwimmbahn, nennen sie das im
Englischen, gibt an, wo die Programmkontrolle gerade besitzt.
Ist übrigens auch optional.
Und jetzt kommen noch ein paar Varianten von der ganzen Geschichte.
Das war erstmal das Grundgerüst Lebenslinien, Schwimmbahnen und so
weiter.
Wo fange ich jetzt an?
Ja, zum Beispiel hatten wir immer gesagt, vorne oben stehen die
Objekte der Klassen.
Also hier steht XYZ drei Objekte vor der Klasse 1, Klasse 2 und hier
unten kommt was Neues hinzu.
In diesen Sequenzdiagrammen kann es natürlich vorkommen, dass Objekte
noch nicht existieren, sondern erst erzeugt werden müssen.
Das geht auch.
Hier erscheint also ein neues anonymes Objekt der Klasse X.
Das wird hier mit einem gestrichelten Pfeil angegeben, dass es erzeugt
wird.
Das wäre also die Objekterzeugung.
Dieses blaue Ding gehört nicht zu dem Diagramm, das ist die
Erläuterung.
Das ist die Objekterzeugung.
Genauso können Sie angeben, ob ein Objekt zerstört wird, indem Sie
hier hinten sagen, gut, an der Stelle brauche ich es nicht mehr.
Kreuzzeichen heißt, kann gelöscht werden, dem Garbage Collector
übergeben werden oder sonst irgendwie aus den Verzeichnissen gelöscht
werden.
Was haben wir noch?
Einfache Dinge vielleicht zuerst.
Hier ist also eine Nachricht.
Das ist nichts Neues.
Es ist auch ein Selbstaufruf möglich.
Wenn also ein Objekt einen Aufruf auf sich selbst macht, dann wird das
so angezeichnet.
Also eine eigene Methode mit Aufruf, dann ist es so mit einem
selbstreferenzierten Pfeil und einem überlagernden Balken gezeichnet.
Dann hatten wir immer gesagt, es läuft also sequenziell ab, aber was
machen wir mit Fallunterscheidungen?
Fallunterscheidungen werden so gemacht.
Sie haben eine Bedingung.
Wir nennen sie I größer 0 bzw.
else.
Haben Sie auch eine Nachricht hierüber und Nachricht 2 hierüber,
zufälligerweise das gleiche Objekt, können natürlich an ein anderes
Objekt gehen, ist auch erlaubt.
Und das klammern Sie ein in so ein Ding, damit man sieht, dass es
zusammen gehört.
Gewissermaßen geschweift die Klammer außen rum, damit ich weiß, wo die
Alternativ beginnt und endet.
Und schreiben dann diese Ecke alt dran.
Dann haben Sie vielleicht hier eine komplexe Interaktion, die auf
dieses Bild nicht mehr passt.
Dann sagen Sie, gut, da ist eine Interaktion 2, die kommt an einer
anderen Stelle vor, die schreibe ich auf ein separates Blatt.
Also so ähnlich wie beim Aktivitätsdiagramm.
Die zusammengesetzte Aktivität ist hier eine zusammengesetzte
Interaktion.
Die kommt auf ein separates Blatt, nennen wir Interaktion 2, da kommt
ein Ref-Verweis auf eine andere Interaktion und die würde zum Beispiel
dann hier oben in dem zweiten Blatt hier auftauchen als Interaktion 2.
Wir können also dem Blatt auch noch einen Namen geben und eben ein
Teilblatt schreiben.
Okay, schon wieder ein neues Diagramm.
Ja, auch wie gesagt, Syntax und Lesen ist manchmal ganz einfach, aber
wie immer muss man es dann selber machen.
Dann tauchen plötzlich die Fragen auf, wie ist das jetzt mit Parallel,
wann gehört das zurück und
wie mache ich Alternativen.
Es gibt noch mehr, es gibt noch Schleifen.
Also wenn man eine Vielzahl von Möglichkeiten hat, plötzlich viele
Fallunterscheidungen, dann macht man lieber ein Aktivitätsdiagramm,
weil es ist meistens leichter anzuzeigen.
Es gibt also diesen Operator Alt für Alternative, das hatten wir schon
gesehen.
Man kann auch mehrere Bedingungen da reinschreiben, also eher so wie
ein Switch oder Case-Anweisung.
Mehrere Fälle.
Dann gibt es eine Break-Bedingung mit einer Bedingung.
Dann wird eben der entsprechende Block ausgeführt und wir enden.
Das sind immer diese Kästchen, die man da so schreibt.
Optional, eine optionale Sequenz wird nur ausgeführt, wenn die
Bedingung erfolgt ist, fast so wie ein IF-THAN-ERS, aber ohne ERS.
Machen Sie von mir auch ein Foto?
Der schüttelt den Kopf, also wirklich.
Enthaltene Teilzeitungen werden parallel ausgeführt.
Das gibt es auch noch.
Das nennen Sie Operatoren.
Nochmal ein Diagrammtyp.
Zustandsdiagramm.
Das Zustandsdiagramm, das kennen Sie ja.
Ähnlicher Automat.
Aber es wäre nicht UML, wenn es nicht ein paar Feinheiten hätte.
Ein paar Erweiterungen, die praktisch sind.
Zustandsdiagramme, ähnlicher Automat, kennen Sie schon eigentlich.
Beschreibt mögliche Zustände und Zustandsübergänge zwischen den
Zuständen.
Natürlich eine ähnliche Anzahl.
Gelten meistens für den gesamten Lebenszyklus eines Objektes.
Also dieses Zustandsdiagramm verbindet man mit der Spezifikation einer
Klasse von Objekten.
Am besten, wir schauen uns ein Beispiel an.
Sieht fast wie ein Aktivitätsdiagramm aus.
Ist aber keins.
Hier sind die Dinger Zustände.
Und das sind die Übergänge.
Und die Übergänge passieren, wenn Ereignisse sich ergeben.
Und diese Ereignisse sind meistens in der Regel Methodenaufrufe.
Können aber auch Ereignisse sein.
Wir haben hier ein Flugreservierungszustandsdiagramm.
Zunächst mal ist der Flug ohne jegliche Reservierung.
Das ist der Anfangszustand.
Das wird angegeben, wie beim Aktivitätsdiagramm, als der
Anfangszustand.
Wenn wir zu laufen beginnen, dann weiß ich aus diesem Knubbel hier,
dass ich am Anfang in diesem Zustand bin.
Ich gehe in den Zustand teilweise reserviert, sobald ich eine
Reservierungsfunktion aufgerufen habe, dann ist natürlich ein einziger
Sitz reserviert.
Wenn mehrere Reservierungen kommen, dann bleibe ich in diesem Zustand,
solange die freie Plätze größer als eins ist.
Wenn schließlich eine Reservation passiert, sodass im Augenblick jetzt
die freien Plätze eins ist, d.h.
ich habe nur noch einen Platz zu vergeben, dann vergebe ich den
natürlich und gehe dann in den Zustand ausgebucht.
Wenn ich im Zustand ausgebucht bin, und dann kommt die
Schließenoperation, die Buchungsfunktionen sind geschlossen, dann gehe
ich in den Zustand geschlossen, und das ist auch der Endzustand des
Diagramms, Gott sei Dank ähnlich wie im Aktivitätsdiagramm.
Ich kann übrigens auch den Flug streichen, dann gehe ich von dem ohne
Reservierung gleich in dieses Ding.
Flugstreichen geht woanders nicht, das ist nicht erlaubt, führt zu
einem Fehler, denn was mache ich mit den anderen Buchungen?
Mit den bereits existierenden Buchungen?
Manche Fluglinien stört das aber nicht.
Wir stören uns daran.
So, was ist jetzt alles noch an Besonderheiten?
Das ist also das normale Übergangsdiagramm, natürlich gibt es auch
Übergänge in die umgekehrte Richtung, ein Stornieren z.B.
führt mich von ausgebucht in teilweise reserviert, teilweise
reserviert führt mich ein Stornieren in diesen Zustand, solange
reservierte Plätze noch größer als 1 ist, oder aber das Stornieren,
wenn ein Stornieren kommt und die reservierten Plätze ist genau 1,
geht natürlich wieder in den ohne Reservierungszustand.
Da sehen Sie aber schon eine Erweiterung, nämlich an diesen Übergängen
stehen die Aktionen, die die Übergänge verursachen, das Ereignis, oder
im ähnlichen Automat spricht man dann auch von einer Eingabe an den
Automaten, aber es sind hier Bedingungen dran.
Wir dürfen also in eckigen Klammern eine Bedingung setzen und die
sagt, dieser Übergang findet nur statt, wenn Nummer 1 dieses Ereignis
ankommt, die Eingabe oder Aufruf da ist, aber auch die Bedingung
erfüllt ist.
Sie sehen, jetzt kann ich stornieren, sowohl in dieser Klasse bleiben,
als auch mit dem gleichen in einen anderen Zustand gehen, aber dann
müssen sich diese Bedingungen gegenseitig ausschließen.
So.
Ach ja, und hier kommt noch was vor, nämlich ein Entry.
Sie können für jeden Zustand eine Eingangsaktion angeben, also eine
Entry -Aktion, die sagt, sobald ich mich in diesen Zustand begebe,
wird diese Aktion, nämlich das Zurücksetzen, ausgeführt.
Das ist eine praktische Erweiterung, denn Sie können in diesen Zustand
von mehreren Stellen kommen, nämlich von hier und von hier.
Sonst müssen Sie nämlich hier an den Kanten schreiben, welche Aktion
durchzuführen ist.
Zum Beispiel beim Stornieren, beim ohne Reservierungszustand
zurückgehen, müssen Sie auch noch angeben, mit einem Schrägstrich,
übrigens kann man, dass die Aktion Zurücksetzen auszuführen ist.
Es gibt auch eine Exit-Aktion.
Wir können hier noch was hinzuschreiben, nämlich Exit und eine weitere
Methode, und die wird aufgerufen, wenn man den Zustand verlässt.
Das sind also Erweiterungen des normalen Endlichen Automaten, aber
diese Erweiterungen können Sie jeweils übersetzen in ein ganz normales
Zustandsdiagramm, da werden noch mehrere Fälle so sehen.
Die Entry-Aktionen könnten Sie einfach an die hereinführenden Kanten
schreiben und die Exit-Aktionen an die herausführenden.
Hier haben wir keine Exit-Aktion.
Jetzt kommen die ganzen Beschreibungen dazu.
Ein Epsilon-Übergang ist einer, der erfolgt ohne Ereignis, kann also
zu einem beliebigen Zeitpunkt erfolgen, wenn wir hier sind.
Kann man auch wieder loskriegen, indem man das Diagramm umändert.
Endzustand...
Jetzt schauen wir uns noch genau an, was man an die Kanten schreiben
kann.
Ja, Sie hatten eine Frage?
Wenn Sie von dem Zustand selbst zurückkehren, dann wird das nicht
aufgeführt.
Wenn Sie waren im Zustand, sind Sie im gleichen Zustand.
Kein Entry, kein Exit.
Aber ja, gute Frage.
Noch Fragen?
Was kann ich an die Kante schreiben?
Den Übergang beschreiben.
Den Übergang beschreibe ich, das haben wir schon gesehen, indem ich
eine Methode Namen angebe, plus Parameter, wenn es welche gibt.
Ferner kann ich eine Bedingung angeben, nur dann wird sie ausgeführt.
Und ich kann noch eine Operation angeben, die ausgeführt werden soll,
als Antwort.
Wie gesagt, das könnte die Eingangsoperation, die Entry-Operation
sein, die müsste ich dann an alle einführenden Kanten geben.
Wenn aber nicht die gleiche Operation an allen einführenden Kanten
steht, dann habe ich eben unterschiedliche, und die schreibe ich dann
eben an die Kanten selbst.
Das wäre also der Übergang.
Dann kann ich also angeben, was löst aus, unter welcher Bedingung, und
was soll ich tun, wenn der Auslöser passiert.
Das ist diese Aktion hier.
Genau.
Spontanübergang gibt es auch, Epsilonübergang, wir hatten einen
gesehen, der hatte nicht mal eine Bedingung, wenn da eine Bedingung
dran stünde, könnte man auch noch hinschreiben, dann würde der
Übergang jederzeit passieren können, wenn die Bedingung erfüllt ist.
Was brauchen wir noch?
Bei gewissen Automaten möchte man Zeitbeschränkungen angeben, zum
Beispiel können Sie mit Add angeben, zu einem bestimmten Zeitpunkt,
sagen wir in drei Sekunden absoluten Zeitpunkt absteuern, ab Beginn,
vorher die Transition.
Da brauchen Sie gar nichts angeben, sondern Add.
Fünf Minuten, das, ja.
Oder was interessanter ist, After.
Sie kennen das ja von verschiedenen Geräten, zum Beispiel Armbanduhren
oder sowas, wenn Sie für eine bestimmte Zeit nichts tun, dann kehrt
die Armbanduhr Einstellung zurück in den Grundzustand.
Der Grundzustand ist Zeit anzeigen, ja.
Da können Sie sagen, also after five seconds, after fünf Sekunden,
zurück in einen bestimmten Zustand, den Sie jetzt mit einer Kante
markieren und an der Kante steht eben die Zeit.
Wenn Sie also jetzt dieses zeitliche Ereignis auch noch mit angeben.
Das kann ich auch noch an die Kanten schreiben.
Enteintes und Austritt Aktionen, das hatte ich schon gesagt, Entry und
Exit ist eine Aktion, wird beim entsprechenden Übergang sofort
ausgeführt und benötigt keine, das denkt immer an vernachlässigbare
Zeit.
So, hier hatten wir auch keine Zeit, Bedingung Wächter, gut, das
hatten wir eigentlich alles schon erklärt.
So, und jetzt kommen die etwas komplizierteren Erweiterungen.
Jetzt haben wir noch drei Minuten, die können wir noch machen, der
hierarchische Zustandsautomat.
Das ist ein einfacher Fall, das sagen wir, angenommen, wir haben hier
einen komplizierten Automat, der ist nicht so kompliziert, weil er nur
drei Zustände hat.
Da würde ich gerne das hier als einen Gesamtzustand betrachten.
Egal, was da drin steht.
Das darf man in diesem Zustandsdiagramm, Herald-Diagramm, da werden
also diese beiden zusammengefasst.
Der Ausgang, der Übergang hier zwischen A und B steht weiterhin, aber
die übrigen Übergänge hier von diesem Gesammelzustand auf diesen
Zustand sind jetzt plötzlich an der Stelle gezeichnet.
Zum Beispiel, jeder Übergang von A oder C führt immer in B.
Also mache ich es von dem hier.
Aber ein Übergang von B kann entweder an A oder an C gehen, unter
welchen Situationen fehlt jetzt, dann zeichne ich dieses Ding hier mit
rein.
Bei diesem kleinen Diagramm macht es nicht viel, macht mir das keine
große Erleichterung, im Gegenteil, macht es nur eher komplizierter.
Aber wenn Sie sich denken, das hier ist ein Zustandsautomat mit 23
Zuständen und hier ist ein Zustandsautomat mit 15, 20 Stück, Sie
können das in zwei Übergänge, zwei Zustände oder auch mehr,
Superzustände gewissermaßen vergröbern, dann kann mir das sehr viel
helfen.
Das nennt man also hierarchisch.
Bei diesem Zustandsübergang mit dem Hierarchischen gibt es auch noch
eine Markierung, die heißt mit Gedächtnis.
Das bedeutet Folgendes, wenn Sie also dieses H mit dranschreiben, dann
sehen Sie ja, Sie können diesen Zustand A, B verlassen, indem Sie mit
C in diesen Zustand gehen, egal wo Sie waren.
Und jetzt kommt ein Übergang von B in diesen Superzustand mit dem
eigenes kleinen d, aber ich weiß nicht an welche.
An welchen dieser kleinen Zustände hier drin.
Und da hilft mir dieses H-Attribut, das sagt nämlich, das merkt sich,
wenn Sie letztes Mal in dem Zustand waren, im Groß C und dann zu B
gingen und dann zurückgehen, dann landen Sie wieder in C.
Oder wenn Sie in A waren und gehen zurück mit diesem Übergang, dann
landen Sie wieder in A.
Das ist also nicht etwas, was Sie dieses Gedächtnis hatten wir nicht
in den normalen endlichen Automaten.
Aber, und das ist dann eine Hausaufgabe, man kann das simulieren mit
einem endlichen Automaten ohne diesem H-Attribut.
Indem man für diesen Fall mehrere Zustände einführen, die sich merken,
wo ich war.
Kann man also wieder auflösen in einem ganz normalen endlichen
Zustandsautomaten, der ohne das H-Attribut auskommt.
So.
Die Nebenläufigkeit, die besprechen wir dann nächstes Mal.
Vielen Dank.