Ja, auch von mir.
Guten Morgen zur Vorlesung.
Sie haben am Montag bei Professor Tichy die Klassendiagramme
angefangen.
So, nochmal kurz eine kleine Wiederholung.
Was war denn da das, was wir uns überhaupt angeschaut haben in den
Klassendiagrammen?
Natürlich geht es in Klassendiagrammen um Klassen und Objekte bzw.
Instanzen dieser Klassen.
Diese Folie hatten Sie am Montag schon.
Man sieht hier die Klasse Kreis mit verschiedenen Attributen.
In dem Fall Radius, ein X- und ein Y-Wert.
Und drei Methoden, anzeigen, verstecken und vergrößern.
Außerdem sieht man zwei Instanzen dieser Klasse.
Wichtig ist die Unterscheidung im UML-Diagramm zwischen den Klassen an
sich, die nur eine Beschreibung sind, wie eine bestimmte Instanz oder
ein Objekt ausgeprägt sein kann, und den wirklichen Instanzen, die
unter dem Klassennamen unterstrichen dargestellt werden.
Und außerdem natürlich einen Namen haben.
Der Name kann entweder explizit genannt sein, wie in dem Fall mein K1,
der eine Ausprägung von der Klasse Kreis ist, aber auch weggelassen
werden, wenn es um eine anonyme Instanz handelt.
Man kann diese Instanzbeziehung zum einen über den unterstrichenen
Strich unter dem Namen darstellen, dass es eine Instanz ist und dem
Doppelpunkt, zu welcher Klasse es gehört, aber auch über die Instance
-of -Kante, die wir hier sehen.
Die ist aber optional.
Die kann man hinschreiben, wenn man es damit klarer machen will, muss
es aber nicht.
Gibt es noch Fragen zu diesen Klassendiagrammen?
Vielleicht auch von Montag oder bei der Bearbeitung des Übungsblattes
oder sonstiges.
Sonst würde ich jetzt zu den Assoziationen weitergehen.
Der Kommentator hat gefragt, in welchem Fall in der Entwurfsphase
schon Instanzen definiert werden.
In der Entwurfsphase im normalen Fall nicht, aber es gibt Situationen,
zum Beispiel, wenn man genaue Beschreibungen von irgendwelchen
Ausführungen eines Programms darstellen möchte, zum Beispiel ein
Sequenz - oder Aktivitätsdiagramm.
Dort möchte man dann dieselben Notationen nehmen, die man auch schon
vorher hatte, aber jetzt halt mit Instanzen.
Das sehen wir aber dann nächste Woche.
Wenn es jetzt keine Fragen gibt, würde ich jetzt mit den Assoziationen
weitermachen, beziehungsweise nochmal vertiefen.
Ich glaube, mit der Folie hatten Sie am Montag aufgehört, wenn ich
richtig informiert bin.
Hier sieht man jetzt zwei Klassen, in denen eine Relation definiert
ist.
Das sind die sogenannten Assoziationen im UML-Klassendiagramm.
Was sieht man nun jetzt hier alles?
Diese Assoziation hat den Namen beschäftigt.
Das heißt, die beiden Klassen Firma und Person sind über die
Assoziationen beschäftigt, miteinander verbunden.
Und das liest sich jetzt wie folgend, dass man hier verschiedene
Multiplizitäten hat, die angeben, wie viele Exemplare oder Instanzen
einer Klasse jetzt an dieser Assoziation teilnehmen können.
Das heißt, wir haben in dem Fall hier bei Person die Multiplizität 0
bis Stern stehen.
Das kann man im UML auch einfach nur mit einem Stern ausdrücken.
Das bedeutet, dass bei der Firma, die wir uns jetzt hier anschauen,
oder bei einer Firma, die wir uns anschauen, 0 bis Stern Personen
beschäftigt sein können.
Deswegen gibt es auch hier diesen kleinen Pfeil hinter dem Namen.
Der gibt an, in welcher Richtung man diese Assoziation lesen soll,
bzw.
den Namen dieser Assoziation lesen soll.
Außerdem haben wir noch zwei weitere Informationen hier gegeben, die
rollen.
Einmal den Angestellten und einmal den Arbeitgeber.
Das sind einfach nur Informationen, die uns helfen sollen, wie wir
diese Assoziation zu lesen haben oder zu interpretieren haben.
Die sind optional, helfen aber deutlich beim Verständnis eines UML
-Klassendiagramms.
Außerdem haben wir jetzt auch noch eine Multiplizität hier bei der
Firma gegeben.
Das ist 0 bis 1.
Kann mir jemand sagen, was das jetzt angibt?
Die Kommiliton hat gesagt, dass eine Person entweder bei einer Firma
oder bei keiner angestellt sein kann.
Das ist genau richtig.
Das heißt, man liest diese Multiplizitäten immer genau inverse zu dem
Punkt, wo sie dran stehen.
Das heißt, wir geben mit dieser Multiplizität an, wie viele Instanzen
von Personen mit einer Instanz von Firma verbunden sein können.
Und andersrum, auf der Seite, mit wie vielen Instanzen von Firma diese
Klasse Person verbunden sein kann.
Dann gibt es noch Restriktionen, die man auch ebenso dort definieren
kann.
In dem Fall ist das UNIQ, welche angibt, dass die Person in diesem
Fall nur einmal bei einer Firma angestellt sein kann.
Das heißt, wir haben jetzt hier den Namen, die Leserichtung und die
Rolle.
Wie ich eben schon sagte, sind nur Etikette oder dienen der besseren
Lesbarkeit, sind aber nicht unbedingt zwingend.
Ich würde sie trotzdem bitten, sie in den meisten Fällen zu
definieren, weil es damit deutlich klarer wird, was sie meinen.
Und auch dem Entwickler, der vielleicht später in diesem Dokument
arbeiten muss, deutlich klarer ist, was denn überhaupt die Beziehung
ist, die sie dort darstellen wollen.
Nun nochmal kurz, das habe ich zwar gerade eben schon gesagt, diese
Unterscheidung zwischen Assoziationen und den Verknüpfungen selbst.
Also, Assoziationen sind immer zwischen Klassen.
Die Verknüpfung selbst ist dann die Verbindung zwischen konkreten
Instanzen dieser Klassen.
Das heißt, wir geben eigentlich mit diesen Assoziationen an, wie viele
eigentliche Instanzen verknüpft sein können über eine bestimmte
Relation.
Das heißt, in dem Fall haben wir jetzt zwar 0 bis Stern gegeben,
Personen, die mit einer Firma verbunden sein können, in einer Relation
stehen können.
Das heißt aber auch, wir können auch einfach 3 nehmen, wir könnten
aber auch keine Verbindung hier haben, keine Verknüpfung mit der Firma
F1.
Es könnten aber auch 4 sein.
Wenn jetzt hier aber eine konkrete Zahl steht, an der Multiplizität,
die direkt an der Person steht, dann muss festgelegt werden, dass auch
diese Anzahl an Instanzen als Verknüpfung da sind.
Das heißt, hätten wir jetzt hier in dem Bereich stehen, müsste genau
dieses Bild auch vorhanden sein, was wir hier unten haben.
So, jetzt nochmal kurz das, was ich eben schon mit dem Vorlesen
meinte.
Also, wie lesen wir jetzt das Ganze?
Also, eine Firma beschäftigt 0 bis beliebig viele verschiedene
Personen als Angestellte.
Das ist genau das, was uns jetzt über die Leserichtung die
standardmäßige Leseweise dieser Assoziation vorgibt.
Man kann das Ganze aber auch im Passiv vorlesen, dann sieht das Ganze
so aus.
Eine Person ist bei 0 oder 1 Firma beschäftigt.
Das geht eben so.
Das ist jetzt halt genau die inverse Form, wie man das vorlesen kann.
Das ist nicht nur die inverse Form, sondern die Assoziation geht ja in
beide Richtungen.
Das heißt, wir müssen die Assoziation auch aus Personensicht verstehen
können und dann wählt man eben genau diese Leserichtung.
Und dann muss man natürlich aufpassen bei der Leserichtung, dass
natürlich der Pfeil in die andere Richtung zeigt.
Das heißt, offensichtlich braucht man dann Passiv, wenn man das
richtig interpretieren möchte.
Das ist richtig.
Wir kommen aber gleich noch später genauer dazu, wie das ist, wie in
welche Richtung die Sichtbarkeiten bei dieser Assoziation definiert
werden.
Nur noch einmal kurz dazu, was eine Kardinalität ist, wissen Sie
hoffentlich alle schon.
Also, es ist einfach die Anzahl der Elemente einer Menge.
Die Multiplizitäten, habe ich eben auch schon kurz motiviert, können
auch verschiedene Ausprägungen haben.
Das heißt, 0 bis 1 bedeutet immer genau 0 oder 1.
Ein Kandidat, eine Instanz kann hier gewählt werden.
0 bis Stern heißt beliebig viele, aber auch 0.
1 heißt immer genau 1.
1 bis Stern heißt beliebig viele, aber mindestens 1.
Da kann natürlich auch eine 2, eine 3, eine 4 stehen.
Das heißt, es gibt halt die Mindestanzahl der an dieser Relation
teilnehmenden Instanzen an.
Und 17 heißt in dem Fall, genauso wie bei 1, dann auch immer genau 17.
Wenn man jetzt keine Multiplizität an eine Seite einer Assoziation
schreibt, heißt dies, es ist nicht spezifiziert, was so viel bedeutet
wie immer 1 oder genau 1.
Das heißt, wenn Sie keine Multiplizität an eine Assoziation schreiben,
bedeutet das immer 1.
Merken Sie sich das für die Übung zum Beispiel auch und wenn Sie
irgendwann mal ein Klassendiagramm zeichnen müssen.
So, jetzt schauen wir uns nochmal an, wie das jetzt genau mit dieser
Interpretation erfolgt.
Nochmal ein bisschen formaler.
Wenn wir jetzt eine n-äre Assoziation betrachten und neben uns ein
Ende dieser Assoziation, was wir jetzt e- nennen, und haben eine
Klasse, die an diesem Ende hängt, das wir mit k bezeichnen, dann
wählen wir n-1 weitere Enden dieser Assoziation, denn wir haben ja am
Montag schon gesehen, eine Assoziation muss nicht immer nur einen
Teilhaber haben.
Es können auch mehrere sein.
Und dann ist die Menge v der Verknüpfung aus dieser gegebenen
Assoziation, die von dem gewählten Satz von Instanzen zu Instanzen der
Klasse k gegeben ist.
Also das sind alle Verknüpfungen, die möglich sind.
Dann ist die Menge m dieser Instanzen, die verknüpft sind über diese
Assoziation, genau, ist dann die Menge m.
Dann beschränkt die bei e hingeschriebene Multiplizität die zulässige
Kardinalität von m, also die Anzahl der Instanzen, die über diese
Verknüpfung verknüpft sein können oder in Relation stehen können.
Das ist ein bisschen kryptisch, wir schauen das Ganze mal nochmal ein
Beispiel an.
Gegeben jetzt nochmal die von eben betrachtete Assoziation beschäftigt
mit n gleich 2.
Dann ist e in diesem Fall der Angestellte und k die Klasse Person.
Nun wählen wir für das andere Ende eine konkrete Instanz, die Firma
PeerCorp.
Und dann ist v genau die Menge derjenigen Verknüpfungen, also die
Verknüpfungen, die von PeerCorp zu Instanzen von Personen führen.
Das könnten jetzt in dem Fall zum Beispiel Klaus, Franz und Peter
sein.
Dann ist genau m die Menge dieser Instanzen hier.
Und die Multiplizität, die wir hier bei Angestellter stehen hatten,
beschränkt halt eben genau die Kardinalität dieser Menge m.
Da wir jetzt aber 0 bis Stern stehen haben, ist es sowieso egal, weil
alles möglich gewesen wäre.
Wenn es hier aber zum Beispiel 2 stände, dann wäre das so nicht
zulässig, dann hätte nur Klaus und Franz verknüpft sein dürfen in
diesem Fall.
Dann gibt es noch die Mehrstelligen-Assoziation, das hatte Professor
Tichy auch schon kurz angesprochen am Montag.
Dafür haben wir auch nochmal eine formale Aufschrieb dazu, den können
Sie sich aber dann zu Hause nochmal als Übung selber anschauen.
Jetzt gibt es noch andere Formen von Informationen, wie eben schon das
gesehene Unique, was eine Restriktion abbildet.
In diesem Fall haben wir jetzt die Assoziation Termin und Person, also
beide mit der Multiplizität Stern, was bedeutet, es können beliebig
viele Personen an beliebig vielen Terminen teilnehmen.
Was nicht ganz stimmt, denn wir haben jetzt hier diese Restriktion
Unique.
Diese gibt an, dass eine Person an einem Termin nur einmal teilnehmen
darf.
Das heißt, die Person darf nicht zweimal an demselben Termin
teilnehmen.
Ich glaube, das ist relativ klar.
Außerdem gibt es noch weitere Restriktionen, zum Beispiel Audit,
welche angibt, dass die, zum Beispiel in diesem Fall, bei der
Assoziation Liste und Element, die Liste enthält in dem Fall eine
bestimmte Null- bis Stern-Anzahl von Elementen und diese müssen einer
bestimmten Reihenfolge abgelegt werden.
Nichts anderes gibt diese Restriktion an.
Der Kommiliton hat gesagt, dass da jetzt zwar die Liste der Termine
unbeschränkt ist, aber das nicht gegeben ist, dass ein Termin nicht
zweimal dort drin sein kann.
Naja gut, wenn es zwei Instanzen dieses Termins sind, sind es zwei
unterschiedliche Termine.
Wenn jetzt jemand natürlich zwei Instanzen mit denselben Werten
anlegt, dann ist das natürlich ein Problem.
Das können wir so nicht modellieren, das müssten wir mit Zusicherung
machen.
Aber da ja hier das Ganze auf Klassen- und Instanz-Ebene ist, bedeutet
das, dass natürlich in dem Fall jede Instanz eine eigene Anzahl von
Verknüpfungen bekommt.
Also der Kommiliton hat gefragt, ob man auf Terminseite jetzt nicht
Unique verwenden muss.
Wenn man jetzt Unique auf Terminseite angeben würde, dann...
Das würde wahrscheinlich das Problem lösen, oder?
Dass man genau nur an einem Termin teilnehmen kann.
Ja,
das könnte man tun.
Das könnte man tun, ja.
Wenn man das so modellieren möchte.
Nein, der Kommiliton hat gesagt, eine Person könnte im gesamten Leben
auch nur einen Termin haben.
Das würde das Unique jetzt nicht angeben.
Das hieße, wenn die Multiplizität hier jetzt 1 wäre, dann würde das
der Fall sein.
Unique gibt nur an, wenn man diese Verknüpfung hat, dass eine
Verknüpfung zwischen zwei Instanzen nur einmal auftreten kann.
Das kommen wir gleich noch zu.
Also noch einen Moment Geduld.
Genau.
Gut, jetzt kommen wir nochmal dazu mit den Leserichtungen oder den
Zugriffsmöglichkeiten, die es gibt.
Wenn man jetzt Assoziationen definiert, es gibt dort drei verschiedene
Schreibweisen.
Die, wie eben schon gesehen, Variante ohne Pfeile oder sonstige
Informationen, bedeutet, dass es nicht spezifiziert, in welche
Richtung diese Navigation möglich ist.
Das heißt, man kann hier von A nach, man könnte von A nach B
navigieren oder auch von B nach A.
Es ist aber nicht spezifiziert.
Das heißt, es ist dem Entwickler überlassen, wie er das jetzt wirklich
umsetzt.
Wenn man explizit sagt, man macht hier einen Pfeil von A nach B, dann
muss auf jeden Fall gegeben sein, dass man von A nach B navigieren
kann.
Es wird aber wiederum keine Information darüber gegeben, ob man jetzt
von B nach A navigieren kann.
Wenn man jetzt dieses X an ein Ende einer Assoziation schreibt,
schließt man explizit aus, dass man nicht von A nach B navigieren
kann.
In diesem Fall.
Das heißt, wenn man jetzt erzeugen will, dass man nur von A nach B
navigieren kann, dann würde man hier hinten noch ein X hinschreiben.
Dann würde man nicht von B nach A navigieren können.
In den meisten Fällen möchte man aber den Entwickler gar nicht so sehr
einschränken im Entwurf, sondern möchte vielmehr ihm die Möglichkeit
überlassen, zu entscheiden, wie man das jetzt konkret umsetzen möchte.
Schauen wir uns nun nochmal zusätzliche Attribute für Assoziationen
an.
Wir haben wieder das Beispiel mit dem Termin und dem Unique.
Wenn jetzt aber ein Teilnehmer für diesen Termin zum Beispiel sich
einen Alarmzeitpunkt eintragen möchte, dann kann man sich natürlich
überlegen, wir speichern diesen Alarmzeitpunkt im Termin.
Das ist natürlich aber nicht so sonderlich sinnvoll, denn jede Person
hätte in diesem Fall dieselbe Alarmzeit für diesen Termin, was
vielleicht nicht für jede Person zutreffend wäre.
Wenn wir das Ganze aber andersrum modellieren, also die Alarmzeit in
die Personen stecken, dann hieß es, eine Person hätte für jeden
Termin, an dem es teilnimmt, dieselbe Alarmzeit.
Aber vielleicht möchte man ja für die Vorlesung 10 Minuten Vorlauf,
aber wenn man im selben Gebäude ist, nur 5 Minuten oder sonstiges, was
auch nicht sonderlich sinnvoll ist.
Wie lässt man so eine Modellierung jetzt in UML?
In UML hat man die Möglichkeit, für eine Assoziation eine sogenannte
Assoziationsklasse anzugeben, die weitere Attribute für eine
Assoziation setzt.
In dem Fall könnten wir jetzt die Assoziationsklasse Teilnahme
einfügen.
Als Konvention gilt übrigens, dass man für die Assoziationsklassen
oder für den Namen der Assoziationsklasse immer den Assoziationsname
benutzt, um das klar zu machen.
Und wenn wir jetzt hier die Alarmzeit reinsetzen, dann kann man pro
Teilnahme einer Person an einem Event genau die Alarmzeit setzen,
genau das, was wir haben wollten.
Das heißt, Klassendiagramme sind eigentlich so eine Art
Multihypergraph.
Das Problem ist, wie setzt man das jetzt um?
Denn zum Beispiel Java oder andere Programmiersprachen haben nicht die
Möglichkeit, für eine Assoziation eine Klasse anzulegen.
Das heißt, man muss jetzt hier, um das Ganze simulieren zu können,
über eine normale Klasse gehen, die jetzt in dem Fall die
Multiplizitäten von da oben erbt und trotzdem pro Teilnahme einen
Termin.
Also ein Termin kann beliebig viele Teilnahmen haben, aber nur eine
Teilnahme zu einem Termin zugeordnet sein und diese dann wiederum auch
hier umgesetzt werden.
Welches Problem könnte jetzt hierbei auftreten, wenn wir das so
modellieren?
Wir haben natürlich das Problem, in dem Fall können wir auch genau
diese Abbildung, also diese Verknüpfung sehen.
Wir haben einen Termin T und zwei Teilnahmen T1 und T2, die zur selben
Person in Relation stehen.
Das ist natürlich ein Problem.
Das Ganze können wir aber auch nur durch Überzusicherung z.B.
mit OCL lösen, denn wir haben jetzt nicht mehr die Möglichkeit, das
Unique hier in dieser Modellierung auch zu verwenden, um es dasselbe
zu erzeugen, was wir hier oben auch haben wollten.
Aber OCL machen wir hier in der Vorlesung nicht.
Genau.
Die Existenz einer Instanz, der Assoziationsklasse hängt an der
Existenz der zugehörigen Verknüpfung.
Das heißt, wenn jetzt eine Verknüpfung, also die Assoziation bzw.
die Verknüpfung zwischen zwei Instanzen nicht mehr existiert, muss
auch die Assoziationsklasse ihre Existenz aufgeben oder irgendwie
gelöscht werden.
Das ist genau das, was ich gerade gesagt habe.
Außerdem, wenn ein Verknüpfungspartner gelöscht wird, wird auch die
Verknüpfung gelöscht.
Das heißt, auch dann muss die Instanz der Assoziationsklasse gelöscht
werden.
Dafür muss man natürlich sorgen, wenn man diese Modellierung umsetzen
will.
Das heißt, man muss irgendwie beim Löschen eines der Kandidaten auch
den in der Mitte löschen.
Also wenn man diese Modellierung von eben betrachtet.
Das heißt, man sollte diese Assoziationsklassen nicht wie normale
Klassen im OML-Diagramm verstehen.
Sie sind eigentlich vielmehr nur eine Hilfestellung, um weitere
Informationen zu einer Assoziation geben zu können.
Sie sind nicht die normalen Klassen, die wir im Klassendiagramm sehen.
So, jetzt gibt es noch zwei Spezialformen oder eigentlich drei
Spezialformen, die ich Ihnen noch genauer vortragen möchte, die ein
bisschen mehr Informationen zu diesen Assoziationen geben.
Zum einen gibt es hier die Aggregation, welche ziemlich häufig
auftritt in normalen Modellierungen, denn sie ist eine Sonderform der
normalen Assoziationen und gibt eine Teil-Ganzes-Beziehung an.
In diesem Fall haben wir jetzt die Klassen PKW und Rad.
Und Rad ist ein Teil des PKWs.
Gut, man kann jetzt darüber streiten, ob ein PKW immer vier Räder hat.
Gehen wir mal davon aus, das wäre jetzt in unserer Modellierung so.
Dann gibt diese Raute hier vorne an, was das Zeichen für die
Aggregation ist, dass das Rad ein Bestandteil des PKWs ist.
Ein anderes Beispiel wäre zum Beispiel, wenn wir jetzt hier
Vorlesungen hätten und Studenten.
Auch Studenten sind Teil einer Vorlesung.
Wichtig ist hier aber die Unterscheidung zur Komposition, denn die
Komposition ist nochmal eine Sonderform der Aggregation und sie ist
eigentlich strikter als die Aggregation selbst, denn hier ist es so,
dass die Teilposten keine Daseinsberechtigung ohne das Ganze haben.
Das heißt, in dem Fall haben wir das Beispiel Rechnung und Posten.
Und ein Rechnungsposten darf nicht existieren, wenn es keine Rechnung
gibt, weil er ist immer konkret einer Rechnung zugeordnet, was wir
eben im Fall der Aggregation nicht hatten, denn bei einer Aggregation
können wir uns überlegen, das Rad möchten wir vielleicht trotzdem
behalten, wenn wir es vom Auto nehmen.
Ein weiteres Beispiel hier unten für eine Komposition wäre zum
Beispiel auch Gebäude und Raum, denn ein Raum existiert immer nur
dann, wenn es auch ein Gebäude gibt.
Der Unterschied in der Notation ist hierbei, dass die Komposition mit
einer ausgefüllten Raute dargestellt wird und die Aggregation mit
einer leeren Raute.
Nun noch kurz eine weitere Spezialform von Assoziationen, die
sogenannten Qualifizierer.
Die kennzeichnet man hier unten mit so einem weißen Kasten, der an
eine Seite der Assoziation gemalt wird.
Und diese geben halt eine Attributkombination an, die eine
Partitionierung der Menge der assoziierten Exemplare definiert.
Das heißt, in dem Fall haben wir das Beispiel Bank und Person.
Und eine Bank kann natürlich beliebig viele Personen als Kunden haben.
Aber pro Kontonummer, die definiert ist, kann eine oder keine Person
zugeordnet sein.
Dasselbe beim Schachbrett.
Ein Schachbrett hat 64 Quadrate.
Aber wenn wir jetzt Zeile und Spalte als Qualifizierer nehmen, dann
gibt es eben genau nur ein Quadrat, was diese Assoziation erfüllt.
Schauen wir uns das nochmal genauer an.
Also diese Qualifizierer kodieren also immer implizit 1 bis N oder M
bis N Beziehungen.
Wenn man sich jetzt hier nochmal anschaut, wir hatten 0 bis 1 bei
Person stehen.
Aber eigentlich, wenn wir das modellieren wollten, also die Beziehung
Kunde zwischen Bank und Person modellieren wollen, haben wir
eigentlich 0 bis Stern, denn eine Bank kann 0 bis beliebig viele
Personen als Kunden haben.
Aber eben genau für eine Kontonummer nur ein oder eben keine Person
zugeordnet haben.
Das heißt aber trotzdem immer noch, dass hier 1 bis Stern, also eine
Person kann immer noch Kunde von 1 bis Stern Banken oder auch 1 bis
Stern Kontonummern haben.
Das heißt aber, ein Kunde oder eine Person ist auch immer nur dann der
Bank zugeordnet, wenn er eine Kontonummer hat.
In diesem Fall.
Hier ist das Ganze nochmal in Textform.
Bei der Bank X sind keine oder maximal eine Person der Kontonummer Y
zugeordnet.
Niemand oder maximal eine Person besitzt das Konto mit der Nummer Y
bei der Bank X.
Eine Person muss mindestens eine Kontonummer bei mindestens einer Bank
zugeordnet sein.
Das ist hier das mit dem 1 gegeben.
Eine Person darf aber trotzdem beliebig viele Kontonummern haben.
Bei einer Bank eine Kontonummer bei beliebig vielen Banken oder auch
beliebig viele Kontonummern bei beliebig vielen Banken.
Das gibt der Stern hier vorne an.
Wenn wir uns jetzt das Beispiel noch mit 0 bis Stern hier und 1 bis
Stern auf der rechten Seite anschauen, dann verändert sich die
Semantik ein wenig.
Denn in dem Fall ist es so, dass bei einer Bank X jeder Kontonummer Y
mindestens, immer noch mindestens oder diesmal mindestens eine Person
zugeordnet werden muss, weil jetzt hier eine 1 steht.
Aber mehrere Personen können gemeinsam dieselbe Kontonummer bei einer
Bank haben.
Eine Person braucht auch kein Konto zu haben, weil wir jetzt hier 0
bis Stern stehen haben auf der linken Seite.
Eine Person kann aber beliebig viele Konten bei beliebig vielen Banken
haben, weil wir auch da wieder den Stern haben.
Das können Sie sich in Ruhe vielleicht nochmal zu Hause anschauen, da
sieht man dann genau den Unterschied zwischen diesen beiden Varianten.
Nun kommen wir noch kurz zu einer weiteren Sache, die man in OML
Klassendiagramm modellieren kann.
Wie Sie aus Java kennen, gibt es Klassenattribute und Klassenmethoden,
die in Java in Static dargestellt sind.
Auch das möchte man in OML zum Teil modellieren können.
Stellt man sich zum Beispiel mal vor, man hat die Klasse Math, die
bestimmte Konstanten hat, in dem Fall E und Pi, die für alle
Ausprägungen, also alle Instanzen dieser Klasse gelten sollen.
Auch die Funktionen sqrt und cosinus ändern sich in diesem Fall nicht.
Das heißt, man stellt im OML solche statischen, beziehungsweise
Klassenattribute und Klassenmethoden unterstrichen dar.
Das heißt, im Normalfall sind Attribute und Methoden in Klassen nicht
unterstrichen.
Und wenn Sie sie unterstreichen, sind es immer Klassenattribute und
Klassenmethoden.
Genau.
Die Klassenattribute und Methoden werden üblicherweise zur Laufzeit in
jede Instanz eingeblendet und können dort dann also wie die eigenen
verwendet werden.
Aber das sollte Ihnen klar sein, wenn Sie sich schon mal ein bisschen
programmiert haben, zumindest mit Java.
Denn es ist genau das, was Java mit Static macht.
Gibt es dazu Fragen?
Man muss an der Stelle ein bisschen aufpassen, weil wir ja auch
Instanzen mit der Unterstreichung markieren.
Also einfach darauf achten, was ist denn jetzt unterstrichen?
Ist es der Klassen- beziehungsweise Instanznahme oder sind es die
Attribute und Methoden?
Nicht, dass Sie da durcheinander kommen.
Genau.
Insgesamt müssen Sie aufpassen, wie Sie Sachen darstellen im
Klassendiagramm.
Zum Beispiel auch dadurch, dass man Sachen kursiv schreibt, sorgt man
dafür, dass die Sachen abstrakt sind und sonstiges.
Das kommt erst später.
Machst du das gleich?
Ja, das mache ich gleich.
Schade.
Dann müssen Sie sich das gleich nochmal anhören.
Tut mir leid.
Gut.
Konstruktoren kann man auch in UML-Klassendiagrammen angeben.
Hier ist es so, dass in UML nicht die Konvention gilt, wie sie in Java
ist.
Dass der Konstruktor immer so benannt sein muss wie die Klasse selbst.
Sondern man kann beliebige Namen für die Konstruktoren wählen.
Zum Beispiel jetzt hier Erzeuge in diesem Beispiel.
Hier haben wir jetzt die Klasse Kreis, die mit der Erzeuge-Methode
erstellt wird.
Das heißt, was wir in UML machen, um Konstruktoren anzugeben, ist,
diesen Stereotype Create davor zu schreiben, in diesen französischen
Anführungszeichen.
Das wollen wir deswegen jetzt so machen, weil die Modellierung, die
wir in UML machen, vollkommen unabhängig davon ist, welche
Programmiersprache wir darunter haben.
Wir haben jetzt zwar in Java zum Beispiel die Konvention, dass es so
genannt werden muss wie die Klasse, aber in anderen
Programmiersprachen gibt es diese Konvention nicht.
Das heißt, man möchte hier eigentlich eine Unterscheidung machen und
das einfach abstrahieren, indem man einfach mit dem Create-Stereotyp
angibt, dass es sich hierbei um einen Konstruktor handelt.
Aber wenn Sie jetzt wissen, dass Sie schon für Java modellieren, dann
sollten Sie trotzdem die Konvention einhalten und trotzdem das Ganze
so benahmen, wie Sie das ja aus Java kennen.
Noch kleiner Hinweis, kein Rückgabetyp hier, der ist implizit
natürlich gerade die Klasse.
Das ist aber wie bekannt auch schon aus Java und sonstigen
Programmiersprachen.
Ja, genau.
Nun eine kurze Fragerunde.
Mal schauen, ob Sie aufgepasst haben oder ob Sie am Montag aufgepasst
haben, vor allem bei der ersten Frage.
In UML kann eine Assoziation mehr als zwei Enden haben.
Wer ist der Meinung, dass das wahr ist?
Bitte melden.
Ich könnte jetzt fies sein und jemanden, der sich nicht gemeldet hat,
fragen, warum er der Meinung ist, dass es nicht so ist.
Aber es ist richtig.
Wir haben ja gesehen, es gibt mehrstellige Assoziationen.
Jetzt haben wir hier ein folgendes Modell gegeben.
Noch eine zweite Frage dazu.
Wir haben jetzt hier die Firma und wieder die Firma- und
Personenrelation, diesmal aber arbeitet bei, mit Leserichtung nach
links.
Und wir haben jetzt hier noch zwei Attribute gegeben.
Ist das hier eine sinnvolle Modellierung?
Hat jemand eine Idee, was daran sinnvoll ist oder nicht?
Die Antwort des Kommentatoren war, dass... hast du es ganz verstanden?
Also die Antwort war, dass eine Suche hier sehr langsam wäre, weil
wenn man eine Person sucht, müsste man durch alle Firmen durchgehen,
um die Person zu finden.
Ist das so?
Nicht unbedingt.
Könnte natürlich passieren, aber das ist nicht der Punkt an der
Stelle.
Es geht hier um eine andere, sagen wir, Unschönheit bei der
Modellierung.
Einigen Sie sich selber darauf, wer von Ihnen beiden darf.
Also der Kommilitone sagt, das Attribut bei Firma, Angestellte, ist
überflüssig, weil über die Assoziation bereits klar ist, dass ja diese
Verbindung zwischen Person und Firma, über dieses Arbeitet bei,
dargestellt ist.
Und das ist genau die richtige Antwort.
Das ist einfach doppelt und damit unnötig.
Man würde hier das Attribut eigentlich weglassen wollen.
Ja, das Attribut ist ja eigentlich schon die Umsetzung oder
Implementierung dieser Assoziation.
Das heißt, eigentlich möchte man das so nicht noch im Umweltmodell
oder im Modell generell modellieren, sondern wenn man eine Assoziation
definieren kann, möchte man das auch über die Assoziation lösen und
dem Entwickler die Freiheit geben, wie er später genau die Umsetzung
dieser Geschichte durchführt.
Genau, also was man ja hier hätte, man hätte ja genau, wie Tobias
gerade sagt, bereits vorgegeben, wie das Ganze zu implementieren ist,
in dem Fall mit einem Array.
Vielleicht möchte der Entwickler aber lieber eine Liste benutzen oder
was auch immer.
Und genau deshalb möchte man an der Stelle eigentlich lieber eine
Assoziation verwenden.
Da hinten gibt es noch eine Frage.
Ich höre heute schlecht, tut mir leid.
Die Frage war, ob eine Assoziation etwas darstellen kann, was später
eine Methode wird.
Naja, also eine Assoziation ist zunächst erstmal eine Relation
zwischen zwei Klassen.
Das heißt, eigentlich in dem Sinne, was Assoziationen bedeuten, ist
das nicht der Fall.
Es kann natürlich sein, dass Sie später irgendeine Umsetzung wählen,
in der irgendwelche Sachen mit Methoden dargestellt werden.
Aber die Assoziationen, die Sie haben, brauchen Sie trotzdem, denn Sie
brauchen diese Verbindungen zwischen diesen Instanzen.
Dann hat vielleicht die Methode als Parameter die Assoziation, die Sie
da definiert haben, oder das andere Ende der Assoziation.
Dann machen wir mal weiter mit der eigentlichen Vorlesung.
Und wir sind jetzt bei einem sehr schönen Thema angelangt, nämlich bei
der Vererbung.
Sie sollten schon mal was von der Vererbung gehört haben im letzten
Semester, zumindest in der Programmieren-Vorlesung.
Wir machen das jetzt mal noch ein bisschen mehr aus der
Modellierungssicht hier in der SVT und wir zeigen Ihnen noch so ein
paar Dinge, die man beachten muss, wenn man denn Vererbungen benutzt.
So ein paar übliche Fallstricke.
Bevor ich das aber tue, noch mal so ein bisschen zu den Begriffen, die
wir brauchen, wenn wir über Vererbung sprechen.
Wir haben dann eine Klasse, von der man eben erben kann.
Die bezeichnet man normalerweise als Oberklasse oder auch als
Basisklasse, Superklasse oder Generalisierung.
Alles das gleiche.
Alles synonym.
Und dann haben wir eben Klassen, die von dieser Oberklasse erben und
die bezeichnet man üblicherweise als Unterklasse, Subklasse oder eben
Spezialisierung, weil sie eben spezieller sind als die Oberklasse, von
der sie erben.
In UML stellt man das Ganze über diese Ist-Ein-Beziehung oder Ist-Ab
-Beziehung dar.
Diese wird mit diesem großen Dreieck symbolisiert.
Wichtig ist an dieser Stelle, das Ganze sieht so ein bisschen aus wie
eine Assoziation, ist aber keine.
Hier sehen wir tatsächlich nur eben genau die Vererbung, die zwischen
Bandlaufwerk in diesem Fall und meinem Videorekorder, Kassettendeck
und DAT-Rekorder besteht.
An der Stelle vielleicht eine kleine Zwischenfrage.
Hochaktuelles Beispiel, das wir uns da ausgesucht haben.
Wer hat denn noch einen DAT-Rekorder?
Hey, super.
Einer, einer, einer, zwei.
Super, zwei DAT-Rekorder.
Wir können ja mal weitermachen.
Wer kennt denn noch einen Videorekorder?
Machen wir es mal anders.
Wer hat denn noch einen Videorekorder zu Hause?
Ah, ganz schön viele.
Ich habe doch noch Hoffnung.
Wer hat denn noch einen DAT-Rekorder zu Hause?
Echt?
Ihre Eltern, nicht schlecht.
Sie haben zumindest schon mal tatsächlich einen live gesehen.
Interessant.
Gibt es also tatsächlich noch für alle anderen?
Das ist ein DAT-Rekorder.
Digital Audio Tape.
Also stellen Sie sich vor, wie CD, bloß auf Magnetband, aber
tatsächlich digital.
Gut, das war der kleine Exkurs.
Warum machen wir das jetzt hier als Beispiel?
Naja, all diese Rekorder, die arbeiten mit Bändern.
Das heißt, man kann da irgendwas abspielen, man kann was aufnehmen.
Man kann den Magnetkopf kalibrieren zum Beispiel.
Und deshalb gehören die alle zu dieser Oberklasse Bandlaufwerk.
Und alle, die hier unten aufgeführt sind, erben eben was auch immer
das Bandlaufwerk kann.
Alle Attribute, die es hat und natürlich auch die Methoden.
Hier haben wir eine kurze Zwischenfrage hoffentlich.
Also hier eine kurze Frage zur Notation.
Die Frage war, man muss bei Klassen oder im Tutorium wurde gesagt, man
muss bei Klassen immer dann auch noch diese vollständige Darstellung
wählen mit den zwei Strichen, damit man sieht, dass das auch wirklich
Klassen sind, auch wenn da überhaupt keine Attribute und Methoden
definiert sind.
Das ist richtig, wir handhaben das so.
Ich bin mir nicht sicher, ob das der UML-Standard tatsächlich
vorschreibt.
Ich glaube, das ist tatsächlich UML-konform, das so darzustellen.
Nichtsdestotrotz sollten Sie es tun.
Insofern ist der Hinweis schon richtig.
Was nicht UML-konform ist, wenn da nur ein Strich ist.
Genau, das auf keinen Fall.
Also man könnte jetzt nicht hier einfach noch für die Attribute den
Einstrich dazu machen, dass es dann tatsächlich nicht UML-konform ist.
Wenn Sie Attribute modellieren oder einen Kasten für Attribute haben,
dann müssen Sie auch den Kasten für Methoden drunter haben.
Wenn Sie aber eine simple Notation haben wollen, können Sie im UML
-Standard auch das so machen wie hier unten.
Also das ist so erlaubt.
Aber um da einfach sicher zu gehen, machen Sie es einfach.
Der Mehraufwand hält sich ja auch in Grenzen.
Gut, machen wir mal weiter.
Jetzt zur Vererbung und ein bisschen formaler das Ganze.
Was ist denn jetzt erstmal die Definition von Vererbung?
Vererbung ist ein A und B-Klassen, so wie Omega A und Omega B die
Menge der Objekte, die die Klassen A und B ausmachen.
Dann ist B eine Unterklasse von A, wenn gilt, die Omega B sind eine
Teilmenge von Omega A.
Das heißt also, alle Exemplare von B sind eine Teilmenge der Exemplare
von A.
Mehr fordert die Vererbung erst einmal nicht.
Warum das stimmt, schauen wir uns gleich nochmal im Detail an.
Was das aber heißt, ist, dass jedes Exemplar von B auch ein Exemplar
von A ist.
Das steckt da ja implizit irgendwie mit drin, wenn das eine Teilmenge
davon ist.
Das heißt, zwischen A und B besteht eben diese Ist-Ein-Beziehung.
Das heißt, B ist auch ein A.
Wenn jetzt A mehrere Unterklassen hat, so sollten in der Regel diese
Unterklassen des Jungs sein.
Das heißt also, wenn ich von einer Klasse A erbe, wie hier unten, B
und C, dann möchte ich, dass diese Klassen sich eben nicht überlappen.
Schauen wir nochmal hier.
Wir möchten dann, dass Videorekorder und Kassettendeck eben möglichst
keine Eigenschaften teilen.
Dass das nicht immer möglich ist, zeigt uns das Schnabeltier.
Wenn wir mal kurz in die Biologie gehen, da hat man ja die Klassen
Vogel und Säugetier.
Und hätte dann ja normalerweise eigentlich wunderbar schön die
jeweiligen Tiere, die davon erben.
Das Schnabeltier ist hier eine Ausnahme, hat Eigenschaften von beiden.
Das heißt, hier müsste man sich überlegen, wie man das modellieren
wollte.
Gäbe mehrere Möglichkeiten.
Also Vogel ist das Schnabeltier jetzt nicht unbedingt, aber es liegt
da ja.
Ich bin mir da nicht sicher.
Es kann sein, dass es ein Vogel ist, aber das ist glaube ich nicht der
Punkt an der Stelle.
Der Punkt ist, beim Schnabeltier wäre es nicht eindeutig, von welcher
Klasse ich jetzt erben wollte.
Wäre es jetzt Säugetier, wäre es denn jetzt der Vogel.
Wer das Schnabeltier nicht kennt, Schnabeltier ist ein Säugetier, legt
aber Eier.
Also hat von beiden offensichtlich Eigenschaften.
Auch das könnte man tatsächlich in UML lösen.
Man könnte hier Mehrfachvererbung verwenden, macht man aber möglichst
lieber nicht.
Das heißt, es kann solche Ausnahmen geben.
Da muss man ein bisschen genauer drüber nachdenken, wie man seine
Modellierung wählt.
Aber wie gesagt, das hier auch nur mal so als kleiner Exkurs.
Wir schauen uns noch ein bisschen genauer die Vererbung an.
Ich habe gerade eben gesagt, naja es gilt jetzt, alle Objekte, die zu
B gehören, sind auch Objekte, die zu A gehören.
Aber die B, die sind ja irgendwie spezieller, das haben wir ja gerade
hier gesehen.
Und jetzt ist die Frage, ist das nicht eigentlich Quatsch?
Ja, also vielleicht sitzen Sie in den letzten drei Reihen und haben
jetzt gerade mal zufällig aufgepasst, weil der Facebook-Feed hat
gerade nichts zu bieten und jetzt gucken Sie gerade hoch und denken
sich, was der Weigeltier von erzählt, das ist doch totaler Quatsch.
Weil Sie sich eben genau so ein Beispiel überlegt haben.
Wir haben hier eine Klasse, die heißt ein Int und die stellt eben
genau das dar.
Die hat ein Attribut und da steckt ein Int drin.
Und von dieser Klasse erbt jetzt eine zweite Klasse und die heißt 2
Int.
Und Sie wissen ja schon, Vererbung heißt, dieses eine Int ist auch
hier unten in der Unterklasse mit drin.
Aber hier haben wir noch ein zweites Int dazu.
Heißt das denn jetzt nicht eigentlich, dass ich mit dieser zweiten
Klasse eigentlich viel mehr darstellen kann?
Jetzt habe ich hier doch eigentlich nicht mehr 2 hoch 32 Elemente, wie
hier oben mit dem einen Int, sondern hier unten kann ich ja dann
plötzlich 2 hoch 64 darstellen.
Ist dann nicht eigentlich das 2 Int größer als das ein Int?
Ihr Kommilitone gibt genau die richtige Antwort.
Die Klasse hier oben sagt nur, dass wir mindestens dieses eine Int
haben.
Alle Klassen, die aber zusätzlich noch mehr Eigenschaften haben, sind
dann eben Unterklassen von dieser Oberklasse.
Das heißt, ich könnte hier auch noch eine zweite Klasse haben, die von
einem Int erbt, die zusätzlich zum Beispiel noch ein Float Attribut
hat.
Die ist dann auch Unterklasse von dem einen Int.
Und damit ist offensichtlich gezeigt, dass eben diese 2 Int Klasse
oder alle Objekte dieser 2 Int Klasse eine Teilmenge bilden von der
einen Int Klasse.
In dem Fall sogar eine echte Teilmenge.
Gut, Fragen bis hierher?
Gut, dann mache ich mal weiter.
Was kann man jetzt alles richtig machen oder auch falsch machen bei
der Vererbung?
Man muss ein bisschen aufpassen, wer da von wem erbt und ob das, was
man denn da modellieren möchte, auch wirklich sinnvoll ist.
Wir haben hier eine Klasse Punkt und ein Punkt besteht üblicherweise
aus 2 Koordinaten.
X und Y, die haben wir hier als Float.
Und jetzt sagen wir, naja okay, wenn wir schon einen Punkt haben, dann
wollen wir vielleicht auch eine Linie haben.
Dann lassen wir doch einfach mal die Linienklasse von der Punktklasse
erben, weil eine Linie kann man offensichtlich mit 2 Punkten
darstellen.
Ist doch alles wunderbar.
Dann machen wir das einfach mal so.
Haben wir uns sogar noch Schreibarbeit gespart, weil dann braucht die
Linie ja nicht mehr diese 2 Punkt Attribute von dem Punkt.
Alles gut.
Ist das eine gute Idee?
Schlägt jemand eine alternative Modellierung vor und sagt, wäre es
nicht besser, 2 Punkt Elemente zu benutzen in der Linie?
Und die Antwort ist ja.
Das heißt, dann hätten wir aber keine Vererbung mehr.
Diese Vererbung hier ist tatsächlich keine gute Idee, denn eine Linie
ist einfach kein Sonderfall von einem Punkt.
Also die Semantik, die da drin steckt in den einzelnen Attributen, ist
dann einfach eine andere.
Und deshalb wäre es an der Stelle tatsächlich besser, wie Sie
vorgeschlagen haben, tatsächlich 2 Punkte zu verwenden, um die Linie
zu modellieren.
Gut, also auch da ein bisschen aufpassen, dass sich nicht plötzlich
die Semantik endet.
Was haben wir als nächstes?
Wie finden wir denn jetzt in der Praxis eigentlich Vererbung?
Was man machen kann ist, naja, mal schauen, was hat man denn für
gleiche Teilmengen in Klassen?
Also denken Sie an unser Beispiel mit den Rekordern.
Es könnte ja sein, dass Sie angefangen haben, einen DATS-Rekorder zu
programmieren, weil Sie sich gedacht haben, ach, wie schön war es in
den 80ern, als es noch DATS-Rekorder gab.
Und dann ist Ihnen aufgefallen, wenn ich jetzt gerade schon mal in
dieser Nostalgie-Schiene drin bin, dann baue ich auch gleich noch
einen Videorekorder.
Und dann bauen Sie den auch noch.
Und dann stellen Sie fest, die haben ja irgendwie gemeinsame Elemente.
Und das ist genau das, was Sie in der Praxis häufig feststellen.
Und wenn Sie das feststellen, dann können Sie überlegen, ob es denn
nicht sinnvoll wäre, da eine gemeinsame Oberklasse, wie in unserem
Beispiel das Bandlaufwerk, zu bauen.
Was wir dann davon haben, ist offensichtlich, das sollte Ihnen
eigentlich auch schon klar sein, aus der Programmieren-Vorlesung, wir
sparen uns offensichtlich Redundanz.
Das gilt auch natürlich bei der Modellierung, nicht nur bei der
Implementierung.
Das heißt, wir haben dann einfach im Entwurf keine
Doppelinformationen.
Und wenn ich dann zum Beispiel bei meinem Anfangsbeispiel bleibe, wenn
ich irgendwas ändern möchte an der grundlegenden Funktion meiner
Rekorder, dann muss ich eben nicht anfangen, durch alle Rekorder
durchzugehen und das DATS zu verändern, sondern eben nur einmal in
meinem Bandlaufwerk und dann bin ich fertig.
Gut, jetzt müssen wir uns noch ein bisschen damit beschäftigen, was es
denn jetzt eigentlich heißt, dass alle Unterklassenteilmengen, alle
Objekte der Unterklassenteilmengen, der Objekte der Oberklassen sind.
Und dafür gibt es dieses Liskov'sche Substitutionsprinzip, was mal
formuliert wurde.
Und in dem steht drin, in einem Programm, in dem U eine Unterklasse
von K ist, kann jedes Exemplar der Klasse K durch ein Exemplar U
ersetzt werden, wobei das Programm weiterhin korrekt funktioniert.
Heißt also, Sie programmieren, was sagen wir denn mal, das Land Baden
-Württemberg, oder nein, anders, Sie programmieren eine Vorlesung.
Und in dieser Vorlesung gibt es Baden-Württemberger.
Und jetzt sagen Sie aber, okay, ich möchte da mal noch eine
Unterklasse definieren, Schwaben.
Und dann muss laut Substitutionsprinzip gelten, überall da, wo Sie
vorher in Ihrem Programm eine Instanz von Baden-Württemberger benutzt
haben, müssen Sie auch einen Schwaben einsetzen können.
Das muss genauso weiter funktionieren.
Bitte entschuldigen Sie meine Mullierung, das muss jetzt hier mal für
die Vorlesung so funktionieren.
Ob das im wahren Leben so ist, kann man jetzt zu bestreiten.
Aber das Substitutionsprinzip sagt, Sie haben eine Unterklasse von der
Oberklasse und wenn Sie jetzt die Unterklasse statt der Oberklasse in
Ihrem Programm verwenden, eine Instanz davon, dann muss das Programm
weiterhin genauso funktionieren.
Das heißt also, alle Eigenschaften der Oberklasse müssen auch in der
Unterklasse vorhanden sein.
Das heißt also, alle Attribute, alle Assoziationen, alle
Zusicherungen, alle Zustände und alle Methoden.
Das heißt aber auch, und jetzt wird es ein bisschen komplizierter und
wir sehen gleich noch, wohin das führt, alle Vorbedingungen der
Unterklasse müssen gleich oder schwächer sein.
Das heißt, die Unterklasse darf nicht mehr fordern.
Und genauso müssen die Nachbedingungen der Unterklasse entweder
genauso stark sein oder stärker.
Gut, was sind jetzt Folgerungen aus dem Substitutionsprinzip?
Alle vererbten Eigenschaften stehen in der Unterklasse zur Verfügung,
das habe ich schon gesagt.
Und zwar genau so, wie sie in der Oberklasse definiert sind.
Die Unterklasse wiederum darf aber weitere Eigenschaften hinzufügen.
Sonst hätte das Ganze ja keinen Sinn, bräuchten wir das ja nicht
machen.
Darf also im Grunde die Oberklasse spezialisieren, deshalb ja auch der
Begriff der Spezialisierung.
Auf der anderen Seite können aber keine Eigenschaften der Oberklasse
einfach weggelassen werden.
Das funktioniert nicht.
Wenn das nötig wäre, dann handelt es sich nicht um eine
Vererbungsbeziehung.
Das heißt, wenn sie feststellen, ich habe hier in meiner Modellierung
zwei Sachen, die sind ähnlich.
Bleiben wir mal bei den Baden und bei den Schwaben.
Stellen dann aber fest, es gibt auch irgendwie Schwaben in Bayern.
Dann stellen sie fest, die Eigenschaften sind zwar ähnlich, aber diese
Eigenschaft unterscheidet sich.
Dann sollte man das vielleicht nicht als gemeinsame oder als Klassen
modellieren, die eine gemeinsame Oberklasse haben.
Gut, weil, und das ist jetzt genau die Begründung, wenn sie das dann
trotzdem tun würden, dann wäre eben das Substitutionsprinzip nicht
mehr gewährleistet.
Zum Beispiel könnte der bayerische Schwabe dann eben nicht mehr in
seine Landeshauptstadt reisen und das wäre immer noch Stuttgart,
sondern der müsste dann plötzlich nach München.
Und das würde dann eben genau nicht mehr funktionieren, wenn sie sowas
so modellieren würden.
Gut, was ist eine weitere Folgerung aus dem Substitutionsprinzip?
Das ist die Transitivität der Eigenschaften.
Das heißt also, sehen Sie mal hier diese einfache Vererbungsbeziehung
von drei Klassen.
Wir haben einen Hund, der hat eine Methode Heulen.
Wir haben einen Wolf, der erbt von Hund, denn ein Wolf ist eine
Spezialform von einem Hund.
Und wir haben einen Polarwolf, der erbt wiederum von dem Wolf.
Und was bedeutet das jetzt?
Naja, das bedeutet zunächst einmal, der Hund kann heulen, der hat die
Methode Heulen.
Das heißt aber auch, dass der Wolf heulen kann, denn der erbt eben
genau diese Methode mit, das sagt ja gerade Vererbung, und genauso
auch der Polarwolf.
Der erbt über den Wolf die Heulen-Methode vom Hund.
Das heißt also, Vererbung ist transitiv.
Ich hoffe, das ist alles klar soweit.
Gibt es Fragen?
Ihr Kommilitone fragt, wie ist das denn jetzt mit dem Überschreiben?
Ist denn das überhaupt okay?
Ändern wir da nicht Eigenschaften?
Ich denke mal, darauf ziehen Sie ab.
Die Antwort ist, nein, da ändern wir keine Eigenschaften.
Kommt aber gleich noch.
Ich würde das einfach auf zwei, drei Folien später verschieben wollen,
die Antwort auf die Frage.
Wunderbar, sogar schon die nächste Folie.
Man muss unterscheiden bei der Vererbung, bei den Methoden, die
Signaturvererbung und die Implementierungsvererbung.
Wenn wir nur die Signatur vererben, dann geben wir der Unterklasse nur
die Signatur der Methode vor.
Auf der anderen Seite gibt es die Implementierungsvererbung.
Da geben wir die ganze Implementierung mit.
Das heißt also, wir könnten ja einmal sagen, okay, hier haben wir nur
die Signatur Heulen und die geben wir halt mit.
Und alle drunter können sich dann darum kümmern, wie denn Heulen
funktioniert oder wie es hier wahrscheinlich sinnvoller wäre.
Wir hätten schon eine Implementierung der Methode Heulen.
Und da steht halt sowas drin, wie gebe einen gewissen Ton aus, der wie
Heulen klingt.
Und wir geben dann über die Implementierungsvererbung eben genau diese
Implementierung an die Unterklassen weiter.
Die Implementierungsvererbung offensichtlich funktioniert nur, wenn
wir natürlich auch eine Signaturvererbung haben.
Das ist, glaube ich, klar.
Umgekehrt geht es natürlich.
Wir brauchen keine Implementierungsvererbung, um Signaturvererbung
haben zu können.
Und das ist jetzt eigentlich die entscheidende Aussage.
Für die objektorientierte Analyse, das Design und die Programmierung
ist die Signaturvererbung alleine ausreichend.
Damit können wir alles bauen, was wir wollen.
Das heißt also, Implementierungsvererbung brauchen wir nicht, ist
nicht die wesentliche Eigenschaft.
Und das ist dann die Antwort auf Ihre Frage.
Wir können Methoden überschreiben, ohne diese Eigenschaft oder diese
Eigenschaftsbestimmung zu verletzen.
Gut, die Implementierungsvererbung ist also nicht nötig, aber häufig
bequem.
Häufig ist das ja einer der Gründe, warum ich das überhaupt mache mit
der Vererbung.
Damit ich eben nicht nochmal die Implementierung der Methode
aufschreiben muss, aber kann, indem ich sie überschreibe.
Schauen wir uns aber gleich auch nochmal an.
Sie kennen das schon aus Java.
In Java und auch in C-Sharp gibt es sowohl Implementierung als auch
Signaturvererbung.
Gut, hier steht dann auch schon genau der Hinweis auf das
Überschreiben.
Da kommen wir jetzt auch noch dazu.
Wenn Sie meinen, naja, so ein Polarwolf, der heult ja ganz anders als
so ein normaler Wolf oder als ein Hund, dann können Sie natürlich die
Heulen -Methode überschreiben.
Das heißt, Sie ändern dann das, was in der Methode passiert.
Wichtig ist aber, die Signatur, die bleibt beibehalten.
Sodass man später den Polarwolf immer noch genauso wie ein Hund
benutzen kann.
Man kann immer noch sagen, Polarwolf, komm, heul doch mal.
Und er tut das dann halt.
Aber er tut das halt anders als der Hund.
Das heißt, ich kann dort dann eben meine eigene Implementierung
angeben.
Wichtig ist, und hier haben wir häufig mal die eine oder andere kleine
Verwirrung, Überschreiben hat nichts mit Überladen zu tun.
Wenn Sie eine Methode überladen, dann bauen Sie eine neue Methode mit
dem gleichen Namen, aber mit einer anderen Signatur.
Schauen wir uns das auch später nochmal an.
Das hat aber, wie gesagt, mit der Vererbung überhaupt nichts zu tun.
Gut, schauen wir uns mal ein Beispiel an für Vererbung.
Wir haben hier eine geometrische Figur mit einem Attribut, Position
und zwei Methoden, Zeichner und Löscher.
Und wir haben drei Klassen, die davon erben, nämlich der Kreis, das
Oval und das Rechteck.
Und offensichtlich sind das alles geometrische Figuren.
Aber wenn ich die jetzt zeichnen möchte, dann muss ich das irgendwie
anders tun.
Also wenn ich einen Kreis zeichne, dann ist das offensichtlich was
anderes, als wenn ich ein Rechteck zeichne.
Das funktioniert irgendwie anders.
Das eine wird rund und das andere hoffentlich eckig.
Ansonsten habe ich das falsch gemacht.
Das heißt, ich muss wohl diese Methode, die mir von meiner Oberklasse
vorgegeben wird, überschreiben und selber mir eine Implementierung
überlegen in meiner Unterklasse, wie ich denn jetzt eigentlich
gezeichnet werden möchte als Kreis.
Jetzt könnte man sich natürlich überlegen, wenn das schon so ist, kann
man dann überhaupt eine sinnvolle Implementierung für diese
Zeichnemethode der geometrischen Figur angeben?
Wer ist in der Meinung, das könnte man schon tun?
Keiner der Meinung?
Okay, dann haben Sie das alle richtig verstanden.
Man wollte natürlich hier eine abstrakte Methode vorgeben.
Das heißt also, wir geben in diesem Fall dann nur die Signatur vor und
sagen, okay, es muss für alle Klassen, die von mir erben, irgendwie
eine Zeichnemethode geben, aber ich gebe noch nicht vor, wie die zu
implementieren ist.
Liebe Unterklassen, kümmert euch da selber drum.
Ihr müsst irgendwie gezeichnet werden können, aber wie, das verrate
ich euch nicht.
Das müsst ihr selber lösen.
Was jetzt eine Folgerung daraus ist, habe ich eine abstrakte Methode
in meiner Klasse, dann wird auch die ganze Klasse abstrakt.
In UML schreibt man sie dann kursiv.
Das gilt sowohl für die Methoden.
Sie sehen, die Zeichnemethode wird hier kursiv.
Klasse wird auch kursiv und damit sieht man, dass sie abstrakt ist.
Von einer abstrakten Klasse gibt es keine Exemplare.
Und jetzt gleich die Frage hintendran, warum ist das so?
Hat da jemand eine Idee?
Genau, also ihr Kommilitone sagt ganz richtig, was passiert denn, wenn
ich jetzt eine Instanz der geometrischen Figur hätte und auf der
Zeichner aufrufen würde.
Ich habe gerade gesagt, genau diese Implementierung gebe ich nicht
vor.
Also wüsste man auch nicht, was man da tun sollte.
Und genau deshalb gibt es keine Instanzen von abstrakten Klassen.
Da kommt noch einer hinzumerken.
Okay, also ihr Kommilitone fragt, gibt es noch eine alternative
Schreibweise für abstrakte Klassen und Methoden?
Weil wenn man das in der Klausur schreibt, dann ist das ja ziemlich
blöd mit dem Kursiv.
Das kann man ja ziemlich schlecht sehen.
Und ich möchte darauf hinweisen, ich habe mich nicht vorher mit ihm
abgesprochen und ich hoffe, er hat auch nicht die Folien gesehen.
Aber das ist genau das Stichwort.
Ja, die gibt es.
Man kann in diesen geschweiften Klammern einfach das Abstract angeben
und damit weiß man dann auch das Abstraktes.
Und ja, der Hinweis ist genau richtig.
Bitte verwenden Sie sowas in Klausuren und in Übungsblättern, damit
man das eindeutig identifizieren kann.
Insofern vielen Dank für die Frage.
Das hat mir die Überleitung abgenommen.
Genau, was haben wir noch?
Gibt es hier noch etwas zu sagen?
Die abstrakte Methode haben wir schon definiert.
Wenn Sie, und hier sieht man noch einen Spezialfall, wenn Sie sagen,
okay, ich habe noch weitere Unterklassen von Kreis, ist jetzt
vielleicht in dem Beispiel nicht so wirklich sinnvoll, aber könnte man
ja haben, und ich möchte im Kreis die Zeichnermethode gar nicht
implementieren, dann müssen Sie das auch nicht tun.
Also hier steht auf der letzten Folie der Hinweis, abstrakte Klassen
vererben die Verpflichtung, eine Methode zu implementieren.
Das stimmt, aber das muss nicht unbedingt direkt in der nächsten
Klasse sein, die erbt, sondern Sie können auch sagen, okay, Kreis ist
auch nur abstrakt, und dann muss man wieder vom Kreis erben und dort
dann die Zeichnermethode implementieren.
Das heißt, wenn Sie die nicht implementieren möchten in Ihrer
Vererbung, dann müssen Sie die natürlich auch nicht hinschreiben.
Gut, was haben wir noch in OML?
Schnittstellen, das ist jetzt keine direkte Vererbung im eigentlichen
Sinne, aber passt ganz gut zu dem Thema, weil es sich ja doch
irgendwie ähnlich verhält.
Was ist die Definition einer Schnittstelle?
Naja, eine Schnittstelle definiert eine Menge abstrakter Methoden, die
von den Klassen, die Sie implementieren, angeboten werden müssen.
Das heißt also, in meiner Schnittstelle gebe ich eine Menge von
abstrakten Methoden vor, gebe aber dort tatsächlich immer nur die
Signatur vor, das ist ganz wichtig.
Das heißt, ich habe dort dann keine Möglichkeit der
Implementierungsvererbung und natürlich kann ich eine Schnittstelle an
sich auch nicht instanziieren.
Genau, was Sie bei der Notation noch beachten müssen, hier benutzen
wir wieder die französischen Einführungszeichen, um zu sagen, hier
haben wir ein Interface.
Sie müssen dann aber nicht die Methoden nochmal extra markieren als
abstrakt.
Da sagt dann OML, also die Information, dass das ein Interface ist,
die genügt uns.
Das heißt, das können Sie sich sparen.
In OML sieht das dann so aus, wenn Sie eine Schnittstelle
implementieren, dann benutzen Sie so eine Darstellung, die ähnlich ist
zu der, die Sie gerade bei der Vererbung gesehen haben, bloß dass eben
die Linie gestrichelt ist.
Sie können zusätzlich noch ein Realize mit dazu schreiben, damit Sie
im Grunde sagen, diese Klasse realisiert jetzt diese Schnittstelle.
Das müssen Sie aber nicht tun.
Sie können für Klassen, die die Schnittstelle benutzen, wie hier in
unserem Beispiel unsere E-Mail, die ein SMTP-Server benutzt, diese
Benutzrelation angeben, die wiederum gestrichelt mit einem offenen
Pfeil dargestellt wird.
Und so kann man im Grunde ausdrücken, die E-Mail benutzt irgendwo den
SMTP -Server.
Es gibt eine Frage.
Zwei sogar.
Ah, ja, deiner Tipp an die mal ganz oben sitzen, die hier unten sehe
ich viel schlechter.
Gut.
Wann benutze ich eine Benutzrelation im Vergleich zu einer
Assoziation?
In der Regel gilt, wenn Sie eine Schnittstelle benutzen, sollten Sie
die Benutzrelation verwenden anstelle einer Assoziation.
Genau, wenn Sie jetzt direkt die Klasse benutzen, dann Assoziation.
Hier sagt ein Kommilitone, wir haben vorhin gesagt, wenn man schon
irgendwie Methoden hinschreibt, dann muss man auch ein Kästchen für
die Attribute hinschreiben.
Und das erfüllt weder unsere Interface noch unser MySMTP, die
Implementierung.
Da müssen wir tatsächlich nochmal nachschauen, ob das im UML-Standard
tatsächlich so drinsteht.
Ich glaube, beim Interface ist es okay.
Genau, bei der Schnittstelle ist es auf jeden Fall okay so.
Bei den Klassen, du hast das zwar vorhin so behauptet, aber jetzt ohne
dir in den Rücken fallen zu wollen, ich bin mir da nicht
hundertprozentig sicher, das müsste ich nochmal nachschauen.
Wir benutzen 2.4, wenn ich mich nicht täusche.
Steht aber auf irgendeiner Folie, glaube ich, ganz zum Schluss.
Also die Frage war, welche Version von UML wir denn verwenden würden.
Gut, dann mache ich mal weiter, versuche mal wieder reinzukommen.
Was machen denn jetzt Schnittstellen eigentlich?
Naja, Schnittstellen übertragen die Verpflichtung, bestimmte Methoden
zu implementieren.
Was sie damit tun, ist, sie geben im Grunde eine Garantie, dass
bestimmte Methoden vorhanden sind.
Man spricht auch ganz gerne von einem Vertrag.
Wenn ich eine Klasse benutze, die eine gewisse Schnittstelle
implementiert, dann weiß ich, dass zumindest die Methoden, die in der
Schnittstelle definiert sind, auch auf jeden Fall irgendwie verwendbar
sind.
Dafür sind Schnittstellen eben da.
Das heißt aber, dass natürlich alle Exemplare einer Klasse, die eine
gewisse Schnittstelle implementieren, genauso verwendet werden können,
als hätte ich jetzt im Grunde die Schnittstelle vor mir.
Das heißt aber auf der anderen Seite auch wieder, ich weiß ja nicht,
wie jetzt die einzelnen Methoden implementiert sind.
Das heißt, eine Schnittstelle gibt nur vor, wie ein Objekt zu
verwenden ist, aber nicht, was es darstellt.
Gut, hier ein kleines Beispiel.
Sie bekommen einen schwarzen Kasten.
In dem Fall ist der so schwarz, dass er schon wieder weiß ist.
Und wir sagen, es gibt einfach so gewisse Schnittstellen.
Darauf haben wir uns irgendwann mal geeinigt.
Und die sagen, naja, wenn ich irgendwie eine Fernbedienung habe, dann
ist auf der Schnittstelle Fernbedienung immer irgendwie ein Play
-Knopf, ein Stopp-Knopf, ein Vorspulen, ein Zurückspulen und ein
Skippen -Knopf.
Und das kann ich irgendwie an das System senden.
Und wenn Sie jetzt von außen wissen, dass solche Schnittstellen
existieren, dann können Sie dieses Gerät verwenden, ohne zu wissen,
wie das intern funktioniert.
Das heißt, wenn Sie jetzt zu Hause vor Ihrem Fernseher sitzen und die
Fernbedienung benutzen wollen, dann müssen Sie nicht erst die
Fernbedienung aufschrauben und gucken, wie innen drin die Kontakte
verlötet sind.
Und können trotzdem sicher sein, dass das passiert, was passieren
soll.
Genau.
Was ist noch zu sagen?
Bei Schnittstellen gibt es keine Vererbung.
Schnittstellen erweitert man.
Das heißt also, man kann, wenn man eine Schnittstelle hat, zu dieser
Schnittstelle noch weitere abstrakte Methoden hinzufügen.
Das macht man in Java über Extents.
Das bedeutet dann im Wesentlichen eigentlich nur, dass die abstrakten
Methoden der Schnittstelle A eine Teilmenge der abstrakten Methoden
der Schnittstelle B sind, die eben die Schnittstelle A erweitern.
Für die Klasse X, die die Schnittstelle A implementiert, heißt es
dann, dass die Menge der abstrakten Methoden von A eine Teilmenge der
Methodendefinition von X sind.
Aber X darf natürlich noch zusätzlich Dinge implementieren.
Das heißt also, wenn wir mal zurückgehen zu dem Beispiel von gerade
eben.
Wir wissen, es gibt eine Fernbedienungsschnittstelle.
Dann wissen Sie, es gibt Play, es gibt Pause.
Aber wenn jetzt sagt, wir möchten hier noch einen zusätzlichen Knopf
haben für Record, den muss es nicht bei allen Fernbedienungen geben,
aber bei unserer speziellen Fernbedienung soll es den geben, dann
können wir das natürlich zusätzlich implementieren.
Gut, das heißt also, wir können mit Vererbung auch sowas wie eine Art
Mehrfachvererbung erzeugen.
Wir können in Java von unterschiedlichen Schnittstellen
implementieren.
Bei Vererbung geht das in Java nicht.
Kann mir jemand sagen, warum man in Java nicht von mehreren Klassen
erben kann?
Warum ist das ausgeschlossen?
Was könnten dafür Probleme auftreten?
Ja, also Ihr Kommilitone sagt, man könnte ja dann einfach gleiche
Benahmungen haben, zum Beispiel von Attributen in Oberklassen.
Und wenn man dann erbt, dann ist die Zuordnung eben alles andere als
einfach.
Noch eine Anmerkung.
Ihr Kommilitone fragt, ob das nicht auch bei Interfaces passieren
kann.
Kann es das?
Genau, aber Sie geben ja nur eine Signatur vor.
Dann ist das halt die gleiche Signatur.
Das stört Sie ja nicht.
Wenn Sie eine andere Signatur verwenden, dann implementieren Sie ja
nicht mehr die Methode des Interfaces.
Wenn Sie den gleichen Namen haben mit einer anderen Signatur, dann
müssen Sie auch beide Methoden implementieren.
Dann haben Sie zwei unterschiedliche Methoden in den beiden
Interfaces, die Sie implementieren.
Ich glaube, wir diskutieren das lieber später.
Sonst führt das jetzt hier zu weit.
Wir machen mal lieber weiter mit dem Substitutionsprinzip.
Schaffen wir das heute noch?
Ah ja, doch, das reicht noch.
Wir machen es einfach ohne Technik.
Gut, was können wir denn jetzt noch zum Substitutionsprinzip und zu
den Methodensignaturen sagen?
Naja, das Substitutionsprinzip sagt ja erstmal gar nichts über die
Signaturen aus.
Das heißt, vielleicht können wir ja sogar Methodensignaturen ändern,
ohne das Substitutionsprinzip zu verletzen.
Vielleicht ist das möglich.
Das schauen wir uns jetzt mal an.
Dazu müssen wir uns erstmal überlegen, was könnte man denn überhaupt
verändern bei einer Methode.
Offensichtlich sind das die Eingabetypen und die Ausgabetypen, Eingabe
- und Ausgabeparameter der Methoden.
Und es gibt jetzt eben unterschiedliche Möglichkeiten, Dinge zu
verändern.
Wenn man einfach nur Dinge verändert, dann spricht man von Varianz.
Das heißt also, man kann da einfach Dinge ändern.
Sie können zum Beispiel einfach den Typ Ihres Parameters austauschen
durch einen beliebigen anderen Typen.
Das wäre Varianz.
Dann gibt es Kovarianz und jetzt wird es ein bisschen komplizierter.
Das bedeutet, wenn Sie jetzt einen anderen Typen verwenden wollen,
dann muss der in einer Unterklasse einen anderen Typen verwenden
wollen als in Ihrer Oberklasse, dann muss auch dieser Typ spezieller
sein.
Wir schauen uns das gleich nochmal im Detail an.
Kontravarianz heißt genau das Gegenteil.
Wollen Sie einen anderen Typ in Ihrer Unterklasse verwenden, dann muss
der wiederum allgemeiner sein als der, der in der Oberklasse verwendet
wird.
Und Invarianz wäre die Variante mit, ja gut, wir verändern einfach gar
nichts, die Typen müssen gleich bleiben, wir haben hier keine
Modifikation.
Das heißt also, Varianz und Invarianz sollte hoffentlich verständlich
sein.
Für Kontra- und Kovarianz zeige ich Ihnen das jetzt nochmal ein
bisschen genauer, was das bedeutet.
Wir haben hier eine Vererbungshierarchie von A nach B, das heißt also
B erbt von A.
Und wir haben noch zwei weitere, nämlich S, das von R erbt und Q, das
von P erbt.
Und wir haben hier jeweils noch Methoden definiert.
In R ist das eine N-Methode mit dem Typ B, also von hier unten.
Und wir haben in S, das ja bekanntlich von R erbt, eine Methode N.
Also diese Methode wird hier überschrieben.
Und wir haben hier aber als Parameter einen Parameter vom Typ A.
Das heißt also, Sie sehen, hier dreht sich die Vererbungshierarchie
gerade um.
In R habe ich die Methode, die einen Parameter vom Typ B hat.
Und in S habe ich eine Methode, die gleiche Methode, die ein Parameter
vom Typ A ist.
Das ist Kontra-Varianz.
Bei der Q-Varianz sieht es anders aus.
Dort ist die Vererbungsrichtung die gleiche.
Das heißt, ich habe hier eine Methode M in der Klasse P, die einen
Parameter vom Typ A hat.
Und in der Klasse Q habe ich die gleiche Methode, die einen Parameter
vom Typ B hat.
Also hier wird die gleiche Vererbungshierarchie eingehalten.
Und hier kommt wieder eine Frage.
Ich antworte jetzt mal nicht auf Ihre Frage, weil das ist vielleicht
schon die Lösung auf die nächste Frage, die ich stellen möchte.
Deshalb ignoriere ich das einfach mal und gehe einfach mal weiter.
Hier nochmal konkrete Beispiele.
Ich möchte darauf hinweisen, das sind nicht meine Folien.
Also nicht, dass hier jemand sagt, der hat was gegen Kroaten.
Das möchte ich gleich ausschließen.
Ich halte hier nur die Vorlesung.
Aber hier ist mal was modelliert.
Und in dieser Modellierung heißt es, na gut, Europäer als Oberklasse
und eine Unterklasse Kroaten, die von Europäern erben.
Und wir haben eine Methode Nehmen.
Und Europäer nehmen eben nur den Euro.
Wo in Kroaten dann nicht so wählerisch sind, die nehmen einfach eine
Währung im Allgemeinen.
Das heißt, hier haben wir ein typisches Beispiel von Kontravarianz,
weil eben der Euro eine Oberklasse zu Euro darstellt.
Beispiel für Kontravarianz.
Was bedeutet das jetzt, wenn ich das benutze?
Vielleicht gehe ich da einfach drüber hinweg, sonst wird es zu lang.
Ist, glaube ich, klar.
Also ich kann ja meinen Europäer einfach die Nehmen-Methode benutzen
lassen und kann dann wiederum meinen Europäer auf den Kroaten setzen.
Das ist ja erlaubt, weil ja der Kroater eine Unterklasse vom Europäer
ist.
Und was passiert dann jetzt eigentlich, wenn ich die Nehmen-Methode
benutze?
Denken Sie da mal drüber nach.
Gleich die Frage dazu.
Jetzt wieder die andere Richtung.
Wir haben hier wieder den Baden-Württemberger und den Schwaben.
Und na ja, gut, ein Baden-Württemberger isst Nudeln ganz gerne.
Der Schwabe wiederum sagt, nee, ich esse nur Spätzle.
Und hier zumindest ist es mal so modelliert, dass Spätzle eine
Unterklasse von Nudeln sind.
Bitte jetzt nicht gleich diskutieren.
Das ist hier so modelliert.
Wir nehmen das jetzt mal als gegeben hin.
Das heißt, das ist ein Beispiel für Kovarianz.
Und jetzt überlegen Sie sich mal wieder das gleiche Beispiel.
Wir haben hier eine Instanz von Baden-Württemberger und wir haben
genauso Schwaben.
Wir haben wieder die Nudeln.
Wir lassen den Baden-Württemberger eine Nudel essen und sagen dann,
hey Baden-Württemberger, sei mal ein Schwabe.
Der arme Baden-Württemberger.
Und jetzt sagen wir wieder, Baden-Württemberger, isst doch mal bitte
die Nudel.
Was passiert?
Und jetzt kommt genau die Frage und ich bitte Sie jetzt nicht die
Technik zu verwenden.
Das klappt ja doch nicht.
Die Frage ist jetzt, was ist denn jetzt eigentlich erlaubt?
Welche der vier Varianten ist denn erlaubt in der Objektorientierung?
Ist A erlaubt, die Varianz?
Das heißt, ich kann einfach Typen verwenden, wie ich möchte.
Wer ist denn der Meinung, dass Varianz für Eingabe-Parameter völlig
okay ist?
Typen, wie man möchte.
Einer meldet sich, sagt Varianz im Allgemeinen ist okay.
Wer sagt denn Kovarianz ist in Ordnung?
Das heißt, wenn ich eine speziellere Oberklasse habe und eine
speziellere Unterklasse habe, dann müssen auch die Typen in der
Unterklasse spezieller sein.
Also Kovarianz.
Hier gibt es einige, die sagen, die Kovarianz ist okay.
Das heißt, wir haben diese parallelen Vererbungsstrukturen.
Okay, das waren jetzt schon einige.
Wie sieht es denn aus mit der Kontravarianz?
Wer ist denn der Meinung, die Vererbungsrichtung muss sich gerade
umkehren?
Das sind auch einige, aber deutlich weniger als gerade eben.
Vielleicht so zehn Leute.
Okay.
Und wer sagt denn, nee, das ist alles blöd, nur Invarianz ist das
Wahre.
Wir dürfen da gar nichts ändern.
Einer zögerlich für die Invarianz.
Ja, gut.
Also einer sagt, da darf gar nichts geändert werden.
Was ist denn die richtige Antwort?
Ich gehe mal vielleicht noch nicht so weit.
Die richtige Antwort ist Kontravarianz.
Also, es ist eben nicht so, dass die Vererbungsstrukturen parallel
verlaufen dürfen.
Und das schauen wir uns vielleicht am besten nochmal in den Beispielen
an.
Wenn wir jetzt Kovarianz hätten und sagen würden, wir machen aus
unserem Baden-Württemberg einen Schwaben und geben dem jetzt eine
Nudel, dann haben wir genau das Substitutionsprinzip verletzt.
Wir haben nämlich plötzlich die Vorbedingungen strikter gemacht.
Wir sagen nämlich jetzt plötzlich, der Schwabe, der isst jetzt nur
noch Spätzle.
Und die Spätzle an sich, die sind ja spezieller als die Nudel.
Das heißt, wir haben im Grunde die Vorbedingungen verschärft.
Es kann ja sein, dass ich in dieser Methode mich auf irgendwas
verlasse, was nur Spätzle können.
Und dann habe ich natürlich ein Problem.
Das heißt, an der Stelle funktioniert das nicht.
Und jetzt können wir uns noch überlegen, warum funktioniert es
andersrum?
Naja, wenn ich einen Europäer habe, der plötzlich zum Kroaten wird und
ich gebe dem Kroaten Euro, dann ist das für den Kroaten völlig okay,
weil Euro ist ja auch noch eine Währung.
Der Euro kann mindestens das, was die Währung kann.
Vielleicht noch ein bisschen mehr, aber das ist dem Kroaten völlig
egal, weil er braucht nur die Eigenschaften von der Währung.
Deshalb gilt für Eingabe-Parameter, Kontravarianz ist erlaubt,
Kovarianz nicht.
Und das sagt auch meine schlaue Folie hier zum Schluss.
Andersrum sieht es aus bei den Ausgabe-Parametern.
Da können Sie sich dann mal selber Gedanken drüber machen, warum das
so ist.
Kleiner Tipp.
Schauen Sie nochmal, was die ersten Folgerungen aus dem
Substitutionsprinzip waren.
So, und jetzt kann es natürlich sein, dass der ein oder andere
ungläubige Student, den es ja immer mal wieder geben soll, nach Hause
geht und sagt, so, ich nehme jetzt mal Java her und jetzt baue ich das
mal mit der Kontravarianz der Eingabe-Parameter und jetzt überschreibe
ich da mal hier die Methode der Oberklasse und nehme mal einen weniger
speziellen Typen und übergebe den.
Und dann sagt meine IDE plötzlich, nee, also du überschreibst hier gar
nichts.
Also zumindest Eclipse würde das sagen.
Und das liegt einfach daran, dass Java zwar objektorientiert ist, aber
sagt, okay, das mit dieser Kontravarianz, das wollen wir trotzdem
nicht.
Und das liegt einfach daran, dass in der Sprachdefinition von Java
geregelt ist, wenn sich die Signatur ändert und der Eingabe-Parameter
gehört bei Java zur Signatur, dann haben wir eben keine Vererbung
mehr, kein Überschreiben mehr, sondern dann hätten wir tatsächlich an
der Stelle, wenn überhaupt, eine überladene Methode.
Gut, dann wären wir an der Stelle durch.
Noch der Hinweis, wenn ich Parameter habe, wie das in manchen
Programmiersprachen zulässig ist, die sowohl ein- als auch Ausgabe
-Parameter sind, dann muss ich das Ganze natürlich nochmal
verschärfen.
Dann gilt natürlich nur noch die Invarianz ist erlaubt.
Wunderbar, dann würde ich sagen, machen wir an der Stelle das nächste
Mal weiter.
Ich danke für Ihre Aufmerksamkeit.