Ja, schönen guten Tag. Man glaubt es ja kaum,
vierte Woche des Sommersemesters, zwei Grad, Regen, Graupelschaber,
meine Aufgabe ist es also bei Ihnen heute, ein sonniges Gemüt hervorzurufen.
Ich werde mir wie immer im Rahmen der mir gegebenen Möglichkeiten Mühe geben. Heute geht es darum,
das Geheimnis zu lüften, warum sie nicht Code duplizieren müssen,
sondern durch eingebaute Sprachmechanismen das Ziel, das sie erreichen wollen,
nämlich bestehenden von ihnen oder anderen geschriebenen Code wiederzuverwenden,
wie sie das mit den Mitteln einer Objektorientierten Programmiersprache,
wie sie dieses Ziel,
wie sie dieses Ziel erreichen können.
Der heilige Gral der Softwaretechnik ist ja die Wiederverwendung.
Das Rad nicht jedes Mal neu erfinden.
Und sie haben jetzt so eine Möglichkeit der Wiederverwendung,
haben sie im Laufe der letzten 18 Wochen kennengelernt.
Die Use-Beziehung, also dass sie Code wiederverwenden können,
indem sie andere Klassen benutzen.
Das ist mittlerweile für sie
ja gang und gäbe
Und das, was wir letzte Woche, so die Richtungen,
die wir uns bewegt haben und die wir heute weiter verfolgen werden,
ist, wie man durch den Einsatz des Vererbungsmechanismus, wie man Wiederverwendung,
Reuse, wie man das systematisch unter Kontrolle kriegen kann.
Und wir werden uns so verschiedene Arten von Vererbung,
werden wir uns anschauen, also auch für verschiedene Zwecke,
sie sehen es hier zum Anpassen,
also irgendwas ist ungefähr so, wie ich das haben möchte,
aber doch nicht ganz, was kann ich da tun,
um Redundanzen zu vermeiden,
also tatsächlich etwas doppelt zu implementieren und dann
so interessantere Aspekte
Wie kann man auf Basis eines noch nicht vollständig vorhandenen Wissens darüber,
wie eine Implementation später mal aussehen soll,
wie kann man trotzdem das, was man schon weiß,
in einer Klasse so implementieren,
dass zu einem späteren Zeitpunkt eine Art von Wiederverwendung stattfinden kann.
Das sind dann so die interessanteren Effekte,
die durch den Einsatz der Vererbungsbeziehung entstehen,
aber wir fangen erstmal
ganz gemütlich an
Ich höre jetzt mal so in ihre Richtung und versuche
das Aufklatschen von Stirnen auf Tischplatten zu identifizieren.
Habe noch nichts gehört. Vielen Dank. Jetzt stellen wir uns mal vor,
es gibt also so eine Klasse Konto und die hat eine Operation.
Gib Saldo an ihrer Schnittstelle. Ja, das können Sie
sich vorstellen. Diese Klasse wollen Sie jetzt wiederverwenden, um
eine klasse Sparbuch zu schreiben. Und was die auszeichnet,
so nach ihrer Modellvorstellung, Wie es dich möchte,
alles das, was ich im Konto bereits implementiert habe,
das möchte ich verwenden und ich möchte zusätzlich noch eine Operation in dem Sparbuch unterbringen,
die da heißen soll, berechnet Zinsen.
Das ist so das Ziel, das wir jetzt so vor
uns sehen und wir möchten nicht, also den Code duplizieren,
sondern wir möchten diese Art von Wiederverwendung durch einen Mechanismus realisieren,
der in der Programmiersprache enthalten ist.
Und das schauen wir uns jetzt mal an,
mit welchem geheimen Schlüsselwort man genau diesen Zweck in Java ausdrücken kann und sie sehen,
dass hier schon rot hervorgehoben Es gibt so dieses Schlüsselwort Extens,
das an einer bestimmten Stelle in ihrem Klassentext auftauchen darf und mit dem sie ausdrücken,
also wenn man das hier mal so im Sinne eines Programmierdiktats so sprechen würde,
ich möchte eine Klasse Sparbuch schreiben und diese Klasse Sparbuch soll alle Eigenschaften von Konto haben,
also einer bereits definierten Klasse Konto haben und ich möchte also ein spezielles Konto werden,
das über die Eigenschaften des Kontos hinaus dir hier stehen,
soll also noch Folgendes gelten
Ich möchte zusätzlich diese beiden Operationen,
die hier blau hervorgehoben sind, die möchte ich implementieren.
So, das können Sie jetzt ausdrücken, wie Sie sehen.
Also da braucht man jetzt also überhaupt nichts zusätzlich.
Das sehen Sie natürlich nicht, aber ich sage, was ich Ihnen sage. Der Effekt,
den Sie erzielen, wenn Sie das jetzt so hinschreiben,
ist, dass die Klasse Sparbuch erbt alle Operationen und Felder,
die bereits in Konto enthalten sind und ergänzt, dass um das,
was man eben in den Klassentext von Konto hineinschreibt.
So, Erben bedeutet, Zunächst mal nichts anderes, als dass so ein Compiler,
so stellen Sie sich für den Moment jetzt mal vor,
Sie sind ein Compiler.
Jeder denkt jetzt, ich bin ein Compiler. Was für
eine Aufgabe. Und der Compiler wird jetzt Folgendes tun und sagen,
okay, da möchte jemand die klassische Sparbuch schreiben,
oh, das ist ja gar kein richtiger,
oder da erwartet mich jetzt was Besonderes.
Der junge Mensch, der Mensch,
der dieses Programmstück geschrieben hat, Hat das Schlüsselwort Extenz kennengelernt.
Und was der jetzt von mir erwartet ist, ich dublitz ich, der Compiler.
Ich nehme jetzt den Code von Konkonto und ich stelle
den einfach vor die Klassenbeschreibung, die jetzt gleich kommt.
Nämlich das, was dieses Sparbuch speziell ausmacht.
Nichts anderes. Compiler heutzutage machen das nicht so.
Aber man kann sich das jetzt erst mal so vorstellen,
dass der Compiler den Code kopiert, der in der Klasse Konto vorliegt.
Und dann schreibt man noch was Zusätzliches dazu, nämlich das,
was das Sparbuch eigentlich ausmachen will. Das heißt,
wir erweitern Zunächst mal den Code von der Klasse,
die wir in Code der Klasse, von der wir erben,
in der von uns jetzt geschriebenen Unterklasse.
So, und wie kann man das dann verwenden?
Also sie hatten ja ein bestimmtes Ziel,
sondern wollen sie sich mal ein Objekt der Klasse Sparbuch erzeugen.
Das tun sie dann, das heißt ein Sparbuch. Und das,
was sie jetzt an Objekten der Klasse Sparbuch aufrufen können,
sind sämtliche Operationen, die einmal in der Klasse Sparbuch selbst definiert sind.
Also hier diese beiden Operationen, aber dadurch,
dass sie, das wird in der UML durch dieses,
Hab ich selten, dass ich zu klein bin.
Das, was durch dieses Dreieck ausgedrückt wird mit der UML
in dem Klassendiagramm, ein Sparbuch ist ein Konto.
Jetzt haben Sie zusätzlich die Möglichkeiten an Objekten der Klasse Sparbuch,
auch alle Operationen aufzurufen, die in der Klasse Konto definiert sind.
Also als da wären Gipsaldo-Auszahlen und Einzahlen.
Und das ist ja jetzt so eine Art von Wiederverwendung,
die Ihnen jetzt so Tür und Tor öffnet für die Verwendung
von bereits wahlweise von ihnen oder von anderen geschriebenen Code.
Und das kann ich Ihnen versichern von dieser Möglichkeit,
wird unter Verwendung von objektorientierten Programmiersprachen auch sehr häufig Gebrauch gemacht.
Weil das ist eigentlich, was heißt eigentlich,
das ist so die Art von Wiederverwendung,
von der man also lange Jahre, um nicht zu sagen,
Jahrzehnte glaubte, dass das, also jetzt die Notwendigkeit,
selbst noch Code schreiben zu müssen, drastisch minimieren würde.
Diese Erwartung hat sich so nicht erfüllt. Die Gründe dafür
sind auch vielfältig. Auf jeden Fall, das ist der Mechanismus,
mit dem wenn man dieser Vererbungsmechanismus in Java ausgedrückt durch Extense.
Das ist der Mechanismus, mit dem man diese Art von Wiederverwendung
erst mal ermöglichen kann. So auf einer technischen Ebene. So,
jetzt kann man sich fragen, jetzt gucken wir etwas genauer hin.
Ich habe gesagt, der Code wird dupliziert und dann
haben wir ja schon so ein paar Sichtbarkeitsregeln kennengelernt.
Sie kennen ja diese Modifier bis jetzt zwei Private und Public.
Public bedeutet öffentliche Schnittstelle für die Benutztbeziehung, jeder alles was
Public definiert ist, kann von einer benutzenden Klasse auch gesehen werden.
Private bedeutet, kann nicht gesehen werden. Jetzt stellt sich die Frage,
wie ist es denn dann bei der Vererbung? Und das,
was ich gesagt habe, ist so von der Modellvorstellung,
der gesamte Code wird vor den eigenen Klassentext,
also der Text der Oberklasse wird vor den eigenen Text gestellt
und die privaten Variablen kann ich dann darauf auch zugreifen.
Und die Antwort darauf leitet nein. Was privat ist in der einer Klasse,
was als privat deklariert wird, das bleibt auch privat.
Auch eine Unterklasse kann nicht auf die privaten Methoden,
auf die privaten Exemplarvariablen einer Oberklasse zugreifen.
Manchmal möchte man aber so in einer,
möchte man einer erbenden Klasse auch zugestehen, das Recht zugestehen,
auf eine Exemplarvariable oder möglicherweise auch auf eine Methode zuzugreifen.
Und zwar in einer Art und Weise,
wie sie für benutzende Klassen
nicht erlaubt ist
Man macht also jetzt so, fängt an,
sich so Gedanken darüber zu machen, in welchen Kontexten,
also erlaube ich den Zugriff auf etwas,
was so in einer Klasse definiert worden ist.
Und dafür gibt es jetzt einen, so im Zusammenhang mit Vererbung,
verwendet es im Zusammenhang mit Vererbung,
verwendete Sichtbarkeitsstufe und die heißt Protected.
Protected bedeutet, für jemanden, der eine,
der diese Klasse benutzt, ist dieses,
ist diese Exemplarvariable,
beziehungsweise diese Methode
nicht sichtbar
Das ist also genauso gut wie Private. Aber für eine
erbende Klasse macht es einen Unterschied. Die erbende Klasse kann als Protected
deklarierte Exemplarlvariablen Methoden zugreifen. Das ist also ein Unterschied zwischen,
ich benutze eine Klasse und ich erbe von einer Klasse,
diese etwas feingranulare Unterscheidung macht man,
um einer Unterklasse einen größeren Zugriff auf,
also die in Anführungsstrichen Innereien einer vererbten Klasse zu ermöglichen.
Sie sehen das jetzt hier am Beispiel, die Exemplarvariable Flow,
deutsche Flow, die Hexemplarvariable Saldo vom Typ Flowd ist als Protected definiert.
Und das bedeutet, ich kann in der Unterklasse Sparbuch,
kann ich auch auf diese Exemplarvariable zugreifen.
Wenn ich die Klasse Konto benutze,
also sei es eine Klasse Kunde oder sowas von der Art
, dann ist die weiterhin nicht sichtbar.
Wir kommen später auch noch dazu, wie man das
systematisch verwendet
Wir hatten ja schon an anderen Stellen in SE1 gesagt,
wir lüften jetzt, fangen jetzt an,
noch so ein paar Geheimnisse zu lüften.
Also warum funktioniert überhaupt so ein Konstrukt wie system.out?
Das haben wir mittlerweile geklärt, wie das geht.
Jetzt lüften wir ein weiteres Geheimnis. Warum kann ich zum Beispiel an
einem Objekt der Klasse Konto die Operation to String aufrufen?
Das ist in unserem bisherigen Erklärungsmodell, ist das eigentlich nicht beantwortbar,
weil sie definieren ja die Operation to String nicht in der Klasse Konto,
Sparbuch, wo auch immer.
Trotzdem können sie
das aufrufen
Und die Antwort jetzt auf diese Frage ist, nun,
jede Klasse in Java, selbst wenn sie das nicht hinschreiben,
erbt von der Oberklasse Object.
Und die Oberklasse Object, die definiert eine Reihe von Operationen,
die damit auch in jeder Unterklasse aufrufbar sind.
Und dazu gehören die Operationen to string. Also man kann sich
eine String-Repräsentation eines Objektes liefern lassen, die Operation Equals,
die zwei Objekte miteinander vergleicht oder auch die Operation Hashcout,
die haben sie ja schon in einem anderen Kontext kennengelernt
So, das sind so ein paar weitere Fragen oder ein weiteres Mysterium,
um Gottes Willen, ist jetzt für Sie erklärbar.
Also Object ist als direkte oder indirekte Oberklasse für jede Klasse,
die Sie schreiben, definiert und damit auch die Operationen,
die von Object exportiert werden.
So, das ist ja jetzt mal Stufe 1 des Einsatzes
von der Vererbungsbeziehung. Jetzt kommen wir mal zu dem,
was da so ich noch alles so an Möglichkeiten,
dass wir diesen Mechanismus jetzt haben,
was das jetzt so an Möglichkeiten eröffnet.
Was auch dazu führt, dass, ich will jetzt erstmal hoffen,
ich will sie jetzt erstmal hoffnungsvoll machen, was alles geht.
Und irgendwann später hole ich sie dann mal wieder auf den Boden zurück.
Also was jetzt eigentlich alles kaputt machen können durch den
unqualifizierten Einsatz der Vererbungsbeziehung. Aber so ist das halt.
So Möglichkeiten kommen immer auch mit Gefahren daher.
Jetzt erstmal zu den Möglichkeiten
Stellen wir uns eine etwas kompliziertere Klasse-Konto vor.
Die habe dann mal zwei Operationen, nämlich die Operation,
mache Jahresabschluss und die Operation gibt es Aldo.
Sie wollen, wie vorhin in der Ausgangssituation,
wollen Sie von der Klasse Konto erben und sagen also,
gib es Aldo, ist wunderbar.
Das will ich genau so haben.
Berechne Zinsen, super,
nichts anderes kommt mir
Kommen wir uns hin unter den Hut.
Aber die Operation mache Jahresabschluss. Die gefällt mir eigentlich nicht
. Die muss in der Klasse Sparbuch aus fachlichen Gründen,
muss die anders aussehen als in der Klasse Konto.
Was kann ich jetzt tun? Und die Antwort darauf ist,
nun wir re-definieren den Code,
den wir geerbt haben und machen uns unseren eigenen Code in
der Klasse in der Klasse Sparbuch für die Operation Macherjahresabschluss.
So, jetzt gucken wir uns das mal an.
Also in der Klasse Kontomarkt dieser Code
so aussehen
Ja, gut. Ist halt so. Das mit dem Overright
hatte ihn Fred letzte Woche schon erklärt. So, und das,
was hier passiert, ist tatsächlich im Sinne eines Überschreibens,
ist, ich behaupte, ich behaupte das nicht nur,
was ich hiermit festlege, wenn ich das so mache,
eine Operation, die dieselbe Signatur hat,
wie die Operation der Oberklasse überschreibt,
den Code,
den ich geerbt habe
Schreibt man hier, da kommen wir gleich noch zu,
ob das jetzt wirklich notwendig ist. Ich schreibe hier denselben
Coach hin wie oben. Achtung, Code-Duplikation, böse,
böse, böse. Und dann mache ich halt noch was Zusätzliches.
Und wenn jetzt an einem Sparbuch die Operation,
mache Jahresabschluss gerufen wird, dann wird eben nicht der geerbte Code ausgeführt,
sondern man hat ja seinen eigenen Code hier geschrieben,
dann wird der Code ausgeführt.
Ich hoffe, das ist soweit dann auch. Also für
Sie erwartungskonform. Wenn ich mir schon die Mühe mache,
also einen eigenen Code in einer Unterklasse zu definieren,
dann möchte ich auch, dass der aufgerufen wird.
Und das passiert auch
Jetzt sind wir nur schon an der Stelle. Also wir haben so,
sagen wir mal, 80 Prozent der Operationen aus der Oberklasse
haben wir geerbt und benutzen die auch genau so,
haben noch was Zusätzliches gemacht, auch toll.
Das ist unsere Bemühungen, keinen Code zu duplizieren,
sind schon ziemlich weit. Der Code aus der Oberklasse,
der wird ja nicht dupliziert, der ist ja weiterhin bloß einmal vorhanden.
Aber an der Stelle, da sehen wir doch noch,
sehen wir noch ein bisschen Optimierungsbedarf, oder?
Oder Potenzial. Und dieses Potenzial, Das wird gelüftet durch oder gehoben vielmehr.
Wird er durch ein weiteres Schlüsselwort, das ich Ihnen hiermit bekannt mache, das Schlüsselwort super. Das Schlüsselwort super hat zum,
also wenn man das einsetzt, hat zum Effekt,
dass so in der Vererbungsreihenfolge geschaut wurde in der Vererbungskette,
Entschuldigung, in der Vererbungskette geschaut wird,
gibt es eigentlich eine Oberklasse oder nicht nur,
gibt es in der Oberklasse,
es gibt ja in Java immer bloß eine Oberklasse,
gibt es der Compiler überprüft, Gibt es in der Oberklasse eine Operation,
die also dieselbe Signatur hat und wenn es das gibt,
dann möchte ich diese Operation, dass diese Operation jetzt aufgerufen wird.
In der Operation mache ich Jahresabschluss,
rufe ich jetzt die Implementation der Oberklasse auf,
die wird dann ausgeführt und dann mache ich das,
was halt zusätzlich getan werden muss.
Das heißt also, wir haben unseren Anteil des Codes,
den wir dupliziert haben,
den haben wir
hiermit auf null reduziert
Indem wir den Aufruf einer, den Aufruf der Operation in
der Oberklasse hier manifestieren. Soweit so gut. Da sind so
die Mechanismen, damit ist auch Schluss mit neuen Schlüsselwörtern.
Jetzt können wir eigentlich mindestens unser Anspruch, keinen Code mehr
zu duplizieren, ist an dieser Stelle schon vollständig erfüllt.
Jetzt könnte ich sagen, 15 Uhr in Deutschland, schönes Wochenende.
Noch nicht. Müssen wir uns jetzt mal noch mit ein paar Effekten auseinandersetzen,
die durch den Einfluss der Vererbung,
also durch das Verwenden von Vererbungsbeziehungen und der Möglichkeit,
Code redet zu definieren, welche Effekte dabei auftreten.
Und diese Effekte, das ist hier mal an einem,
also hier ist es erstmal intentional definiert.
Wir machen das gleich uns auch nochmal an einem Beispiel klar
Wir stellen uns jetzt erst mal abstrakt vor. Es gibt hier
so eine Oberklasse und die definiert zwei Operationen und das,
was ja für sie jetzt eigentlich auch erst mal nicht weiter verwunderlich ist,
ist, dass die Operation eins der Oberklasse, die ruft,
während sie abläuft, ruft sie halt die Operation zwei auf.
Warum auch nicht? Kann sie ja machen, oder?
Wo ist das Problem? Da ist auch kein Problem,
das ist alles gut. Jetzt gibt es,
jetzt kommen Sie da mit Ihrem frisch gelernten Wiederverwendungswissen und sagen,
juhu, ich erbe jetzt mal in der Unterklasse von der Oberklasse
Und das, was ich gerade gesagt habe, das wenden sie jetzt an.
Ich redefiniere die Operation 2. Kann ich ja machen,
weil also der Code der Operation 2 aus der Oberklasse,
der hat halt nicht wirklich gepasst.
Also redefiniere ich das. Und jetzt kommt der interessante Effekt
. Und das finde ich, ist in diesem Satz,
hier ist es also wirklich prächtig ausgedrückt,
wenn in einer Unterklasse eine Operation re
definiert wird
Unsere Operation 2, die in der Oberklasse durch eine andere Methode aufgerufen wird.
Also unsere Operation 1, dann wird in der Unterklasse
die neue Implementation dieser Operation aufgerufen, aufgerufen.
Das ist doch klasse, oder? Für das Protokoll,
das Publikum zeigt keine Reaktion. Und das ist ja auch so,
ich hoffe jedenfalls, das entspricht erstmal ihrer intuitiven Erwartungshaltung
Ich appelliere jetzt an Ihre Intuition im Umgang mit redefinierten Operationen in Unterklassen
. Möglicherweise haben Sie da doch nicht so viel Intuition.
Aber das ist ja auch eigentlich ganz,
also das ist doch eigentlich wirklich klar, Leute, oder?
Wenn, also Sie die Operation 2,
wenn Sie sich die Mühe machen, was ist meine Maus?
Wenn Sie sich die Mühe machen, diese Operation hier zu redefinieren.
Und Sie rufen diese Operation hier auf,
dann erwarten Sie doch nicht, dass diese Operation ausgeführt wird,
sondern dass diese Operation ausgeführt wird,
oder?
Das kann man doch wohl erwarten
Ich versuche das jetzt noch mal extensional. Also diese Erwartungshaltung zu bestärken,
indem ich Ihnen das an einem Beispiel versuche noch mal klarzumachen.
Es mag also so diese Oberklasse Konto geben und die hat eine Operation,
Drucke, Jahresbericht. Achtung, das ist unsere Operation 1.
Die ruft in ihrem Ablauf, ruft sie,
die Operation 2 auf, nämlich Mache-Jahresabschluss, soll sie machen.
Sparbuch, wieder unsere, das ist unsere Unterklasse.
Und die re definiert jetzt die Operation, mache Jahresabschluss.
Und wenn ich jetzt an einem Konto die Operation Druckejahresbericht aufrufe,
dann soll und wird die im Konto redefinierte Operation, mache Jahresabschluss aufgerufen.
So ist das. Dass das jetzt hier noch so ein super beinhaltet,
das sei, das mag so sein oder mag auch nicht so sein,
das trägt jetzt nichts dazu bei, dass der Effekt,
den ich jetzt hier gerade beschreibe, der nennt sich Jojo-Effekt.
Das ist nicht der Effekt, das ist der oder weniger verzweifelte versucht,
sich das klarzumachen oder zu erklären, was da passiert,
während ihr Programm abläuft. Und das Jojo besteht darin,
dass zunächst immer in der Klasse des aktuellen Objektes geschaut wird,
vom Laufzeitsystem, ob eine Implementation,
eine Reimplementation für die Operation vorhanden ist, die aufgerufen wird.
Wenn das der Fall ist, dann wird die Methode aus der Unterklasse ausgeführt.
Und ansonsten schlägt also das Jojo runter schaut, also in
der Oberklasse nach, ob dort eine Implementation vorhanden ist.
Und dann wird beim nächsten Aufruf wieder geschaut,
also von einer Operation, ist in der Unterklasse eine Implementation.
Und immer wenn das der Fall ist, dann wird die
auch genommen. Und ansonsten geht das Jojo wieder,
ich erkläre das, glaube ich, genau,
also Jojos gehen ja nicht in die Höhe.
Jedenfalls nicht beim normalen Jojo-Spielen. Dann geht das Jojo wieder in Richtung Oberklasse,
wo wir mir jetzt beim Erzählen auffällt,
Oberklassen sind eigentlich auch oben und nicht unten
Aber so viel geistige Flexibilität haben wir, glaube ich,
alle, dass wir diese Metaphorik verfolgen können. Und jetzt sind sie,
jetzt haben sie so einen ersten, möglicherweise verwirrenden Eindruck davon,
was man also mit Erben und Reh definieren erreichen kann.
Und vielleicht fragen sie sich auch, muss ich das machen?
Es ist wirklich notwendig. Ich könnte ja auch Code duplizieren
, oder? Sowas muss ich mich nicht mit diesem Mechanismus
und seinen Folgen auseinandersetzen
Wenn sie das jetzt im Moment denken, fühlen,
kann ich das nachvollziehen. Ich versichere ihnen aber,
sie werden sich daran gewöhnen und sie können auch positive Effekte damit erzielen.
Auf einem positiven Effekt komme ich jetzt heute noch zu sprechen.
Jetzt stellen wir uns mal wieder diese Ausgangssituation, oder nein,
nicht wieder, wir stellen uns jetzt diese Ausgangssituation vor,
die da heißt, derjenige, diejenige,
die die Klasse Konto implementieren, die wissen ziemlich sicher,
Ich brauche die Operation, ist Betrag gedeckt.
Die werde ich, also ich brauche diese Operation. Ich weiß aber noch nicht,
wie ich diese Operation implementieren soll, weil ich ahne dunkel,
dass so diese Implementation für ein Sparbuch oder für ein Girokonto unterschiedlich aussehen wird.
Ich weiß das einfach noch nicht. Also Sparbuch kann man
nur das abheben, was drauf ist.
Girokonto kann man auch überziehen, Überziehungszinsen berechnet,
dieses ganze Zeug.
Aber die Frage ist ja berechtigt
Und ich möchte eigentlich auch nicht,
dass so jemals ein Objekt einer Unterklasse von Konto erzeugt wird,
ohne dass diese Frage beantwortet werden kann.
Und dazu gibt es jetzt die Möglichkeit, sogenannte abstrakte Methoden zu definieren.
Abstrakte Methoden bedeutet, ich schreibe in einer Klasse wie Konto
, schreibe ich jetzt nur einen Methodenkopf hin.
Das ist ganz so, wie sie das auch von Interfaces
bereits kennen. Und mit dieser Möglichkeit sage ich Folgendes.
Ich definiere hier eine abstrakte Methode Und damit die Konsequenz ist,
ich kann von der Klasse Konto keine Objekte mehr erzeugen,
weil hier noch einfach ein Methoden Rumpf fehlt. Aber ich bin mir sicher,
wenn es dann mal eine konkrete Klasse wie Sparbuch gibt,
die von meiner abstrakten Klasse erbt, dann muss die dafür sorgen,
jedenfalls wenn Objekte von ihr erzeugt werden sollen,
dass diese abstrakte Definition in der Oberklasse,
dass die durch definierenden Code zu einer echten Methode gemacht wird
und dann können auch Objekte von dieser Klasse erzeugt werden.
Das heißt, mit diesem Mittel der abstrakten Klassen
verwirklicht durch abstrakte Methoden Habe ich die Möglichkeit geschaffen,
schon mal im Voraus eine Spezifikation vorzunehmen, derart.
Ich sage, ich habe vorhin, glaube ich, behauptet,
das wäre das letzte Schlüsselwort, das Sie heute kennenlernen.
Da habe ich wieder gelogen. Zu dem Zeitpunkt,
als ich gesagt habe, habe ich subjektiv die Wahrheit gesagt.
Also mit dem Schlüsselwort Appstrack legt man fest,
ich gebe hier eine Signatur vor,
aber ich weiß noch nicht,
wie diese Signatur durch eine Implementation mit Leben
erfüllt wird
Und das ist jetzt ein Mechanismus,
der wird so vielfach zur Implementation von objektorientierten Rahmenwerken eingesetzt.
Und wir schauen uns gleich ein Beispiel an,
an dem es hoffentlich sinnvoll für sie sinnvoll erscheint, dass dieses Mittel eingesetzt wird.
So, erstmal noch wieder ein Beispiel,
oder dass wir so ein bisschen einen Java-Code,
die Klasse Konto wird dadurch abstrakt, das heißt,
man kann keine Objekte von ihr erzeugen,
dass sie eine abstrakte Methode enthält.
Und dann sagt man halt, das hier ist der
bisher bekannte Sachverhalt. Ich kann halt sagen,
das ist eine Bullshit-Funktion und ich kann, ich weiß,
wie sie heißt und ich weiß, welchen Parameter sie erwartet.
So, in einer Unterklasse, wie Sparbuch von der Objekte
erzeugt werden sollen, muss ich diese Implementation vornehmen.
Und na gut, die Implementation ist, wie auch immer,
ist also vergleichsweise simpel. Also hier ist die Folge,
hier wird sie vorgelegt oder vorgenommen,
diese Implementation und das bedeutet nicht,
die klassische Sparbuch ist nicht mehr abstrakt.
Ich kann von der klassische Sparbuch
auch Objekte erzeugen
So, was sind die Eigenschaften abstrakter Klassen?
Das habe ich jetzt eigentlich auch alles gesagt. Sie können
keine Objekte von abstrakten Klassen erzeugen durch, also das Laufzeitsystem,
wird auch sichergestellt, dass sie das nicht können.
Und das Gegenteil einer abstrakten Klasse ist, also die konkrete Klasse,
die konkrete Klasse, von der Objekte erzeugt werden können.
Jetzt kommt die nächste Frage. Ich habe den Verwendungszweck,
das Motiv für abstrakte Klassen abstrakte Methoden,
habe ich
jetzt erläutert
Man die eigentlich aufrufen,
diese abstrakten Methoden und die Antwort auf diese Frage lautet,
ja, jetzt fragen Sie sich vielleicht noch, warum sollte ich das tun?
Ich habe ja noch gar keine Implementation für diese Klassen.
Das weiß ich doch gar nicht. Also wie soll das
überhaupt laufen? Und der Punkt ist, naja,
möglicherweise, das kommt gleich noch ein etwas sinnvolleres Beispiel.
Also Sie haben in der Klasse ein Konto,
haben Sie schon eine ziemlich konkrete Vorstellung, eine implementierbare Vorstellung davon,
so wie das Auszahlen vom Konto stattfinden soll.
Sie haben aber noch keine Idee davon, hatte ich ja gerade motiviert.
Also wie diese Operation implementiert wird. Und trotzdem können sie
bei der Implementation von Zahle aus schon so tun,
als ob dieser Code vorhanden wäre.
Und das ist insofern keine völlig unberechtigte Hoffnung,
dass also zur Laufzeit des Programms,
das auch tatsächlich ausführbar ist, weil sie wissen ja,
Objekte von Unterklassen von Konto können überhaupt nur erzeugt werden,
wenn diese Implementation einmal vorliegt
Und durch den Mechanismus, den ich gerade erläutert habe,
ist auch sichergestellt, dass also dann die Implementation verwendet wird,
die sie gerade definiert haben.
Und wir haben das kennengelernt, jetzt den Jojo-Effekt.
Also das ist genau derselbe Effekt,
der beim Redefinieren zum Tragen kommt. Und dieser Mechanismus,
also die Mechanismen, die ich Ihnen jetzt vorgestellt habe,
die werden zur Konstruktion von objektorientierten sogenannten Rahmenwerken, werden die verwendet.
Und das muss das. Wir werden uns auch noch etwas intensiver
über Entwurfsmuster unterhalten. Das Muster, das dabei verwendet wird,
ist die sogenannte Schablonenmethode. Die Schablonenmethode, Ist eine Methode,
wie man so unter Verwendung von abstrakten Methoden bereits ein generelles Schablonenartiges Verhalten implementieren kann,
ohne dass man Kenntnis davon hat,
wie dieses Verhalten nachher konkret von Unterklassen implementiert wird.
Stellen wir uns mal was anderes als ein Konto vor,
das hier ist unsere Titelliste, eine Liste von Titeln von Büchern,
Zeitungen, irgendwas.
Diese Titelliste ist eine abstrakte Klasse und sie ist abstrakt,
weil ich noch nicht weiß, wie werde ich mal die Operation erpennt.
Also ich möchte einen Titel an diese Liste hinzufügen.
Wie wird die mal implementiert werden? Ich weiß das noch nicht,
beziehungsweise ich werde diese Entscheidung jetzt noch nicht treffen,
weil es gibt Verwendungszusammenhänge, in denen ich diese Titelliste als Arraylist implementieren werde.
Es gibt Verwendungszusammenhänge, in denen ich sie als verkettete Liste
implementieren werde
Entscheidung mache ich ja davon abhängig, wird mehr gesucht in der Liste
oder wird mehr eingefügt, vom Verwendungskontext ist das abhängig.
Was ich aber genau weiß, ist, ich werde alle
diese Operationen an dieser Titelliste haben. Ich werde erpennt haben,
Start, also geh an den Anfang,
gib mir mal das nächste Element, ist die Liste leer,
bin ich bereits über das Ende der Liste hinausgegangen und gib mir mal das Item,
die Stelle, an der ich gerade stehe.
Das weiß ich alles, so dass ich diese Operationen haben möchte und,
wie ich gerade gesagt habe, Ich weiß noch nicht,
womit ich das implementieren werde.
Diese Entscheidung lasse ich mal noch offen. Und jetzt kommt
der spannende Punkt. Ich kann unter Verwendung dieser noch nicht implementierten Operationen...
Meine Maus macht mich wahnsinnig. Ich kann unter Verwendung dieser
noch nicht implementierten Operation, kann ich bereits eine Operation namens,
oder eine Funktion namens Contains implementieren.
Also enthält eine Titelliste einen bestimmten Titel. Und dann schauen
wir uns das mal an, wie das ausschaut. Naja,
ich weiß ja, wenn die Liste nicht leer ist,
Dann sage ich, geh mal an den Anfang,
solange ich nicht übers Ende gelaufen bin und das Item,
auf dem ich gerade stehe, nicht das ist,
was ich suche, bewege dich mal einen Schritt weiter.
Und dann bin ich entweder hinter dem Ende oder ich habe
es getroffen und das kann ich durch eine Abfrage auf die Operation off
, kann ich das feststellen.
Das heißt, ich habe hier eine konkrete Implementation auf Basis
abstrakter Operation vorgenommen. Und das ist jetzt so der Trick,
den man in Objektorientierten Rahmenwerken häufig anwendet. Das heißt,
es wird gefährlicher Code, wo man viel falsch machen kann.
Also zum Beispiel das Iterieren durch eine Liste.
Das ist ja bekanntlich schwierig, weil man vergisst,
also wahlweise das erste Element oder man rennt hinter das letzte Element.
Diese Implementation gebe ich vor. Und die Einschubmethoden, die Hook-Methoden,
die nicht so gefährlich sind, die lasse ich von demjenigen implementieren,
der zum Beispiel weiß, wie die konkrete Implementation ausschaut.
Weil die Operationen pennt Start Nexton, das können sie implementieren,
unter anderem auch, weil sie wissen, naja,
will ich jetzt eine Airrealist verwenden, will ich eine verkettete Liste und so weiter.
Weil sie diese Entwurfsentscheidung treffen können. Und dieses Verhalten Also jetzt das,
was man ja in der Verwendung tun muss, ist,
sie werden also ihre Titelliste, die erbt von der Abstract-Titelliste.
Sie definieren die abstrakten Methoden. Sie erben und verwenden dann
die Operation Contains. Und dass das dann im Ablauf funktioniert,
ist dem vorhin geschilderten Jojo-Effekt zu verdanken, weil dann ja
der von ihnen definierte und oder re-definierte Code verwendet wird.
Jetzt können Sie erben. Sie sind jetzt Mitglied der Generation Erben
und verfügen jetzt über sämtliche technischen Möglichkeiten, denen die Vererbungsbeziehung einzusetzen.
Wenn ich jetzt das nächste Versprechen, ich habe heute schon Versprechen gebrochen,
wer einmal lügt, dem glaubt man nicht.
Aber ich glaube, wir sind jetzt ziemlich durch,
was Schlüsselwörter in Java anbetrifft, oder?
Nächste Woche kommen noch Exceptions, okay.
Aber dann sind wir durch. Herzlichen Glückwunsch. Vielen Dank
. Schönes Wochenende
Musik Also ich bin stark dafür,
dass ich wieder das andere Mikro bekomme.
Ich fühle mich nicht
sehr wohl.
Woran muss ich denn jetzt alles denken?
Einmal das OML-Tool. Damit seid ihr jetzt ja hoffentlich glücklich
geworden in den Übungen, dass ihr das verwenden sollt.
Genau. Jetzt muss ich wieder schauen,
dass sich das auf den Bildschirm einigermaßen hingezoomt bekommen.
Ja, da ist es. So
, dann muss ich
E-Clips starten
Schaut eigentlich irgendeiner von euch diese Videos, die ich mal aufgenommen habe?
Oder ist das einmal ein Handtuch, wem das irgendwas nützt,
sonst kann ich es ja eigentlich auch demnächst mal nicht so wahnsinnig viele.
Also ich habe den Eindruck, Lecture Droge flutscht schon ganz gut mittlerweile.
Also das letzte Video war schon nach nicht mal 24 Stunden online.
Ich denke mal, ich mache das heute noch einmal aus Spaß.
Aber sofern ich jetzt nicht massive Proteste kriege,
werde ich das wahrscheinlich einstellen,
weil das einfach mittlerweile ganz gut läuft, da habe ich den Eindruck.
Und der Ton über mein mitgebrachtes Mikro ist auch nie
so dolle. Obwohl das eigentlich ein ziemlich gutes Mikrofon ist
So, jetzt gucke ich einmal, dass das auch eine
gute Lautstärke hat. Sehe ich das hier irgendwo?
Das ist irgendwo hier, 100 Prozent, sehr gut.
Okay, habe ich hier irgendwo Ton, habe ich?
Dann drücke ich jetzt einfach mal eine Taste.
Und ich checke einmal kurz, dass es wirklich aufnimmt,
ja. Okay, also ein, ja wie gesagt,
vielleicht stelle ich das demnächst ein.
Ich glaube, der Aufwand lohnt sich einfach nicht.
Gut. Ja, ich wollte mit euch nochmal
einen Blick auf dieses Diagramm werfen
Was wir letzte Woche gebaut hatten,
ob das überhaupt noch konsistent ist mit dem Code,
den wir in der Zwischenzeit geschrieben haben.
Als erstes ist es mir aufgefallen, wir haben vergessen,
den Rückgabetyp bei Schreibezinsen gut hinzuschrieben, das hinzuschreiben.
Das ist, glaube ich, nicht so schlimm.
Den schreiben wir da einmal hin. Okay. Aber dann
hatten wir ja noch ganz viele weitere Klassen geschrieben, naja gut,
zwei weitere. Also das Diagramm zeigt jetzt quasi dieses Interface.
Diese implementierende Klasse und diese implementierende Klasse. Was wir in
einem Diagramm noch nicht sehen, ist diese Klasse Bank,
die können wir eigentlich noch mal kurz hinzufügen.
Ja, dass uns irgendwie klar ist,
eine Bank verwaltet eine Liste von Konten und man kann eben
ein neues Konto eröffnen,
das könnte man doch eigentlich wunderbar mal
hier dazuschreiben
Also machen wir eine neue Klasse. Bank. Was hat sie
für Attribute? Sie hat also diese Liste von Konten,
Liste mit Elementary-Konto und namens Kunden.
So. Und man konnte ein Konto eröffnen, eröffnen, Konto,
Konto, neues Konto, waren das, glaube ich.
So. Ein bisschen zu beschieben, dass man es auch gut erkennt.
Und dann nehmen wir vielleicht mal nicht diesen Fall, sondern diesen Fall.
Wir nehmen ja immer gerne diesen durchgezogenen Pfeil.
Wenn eine Klasse Exemplarvariablen des Typs hat,
von denen wir abhängen,
dann nehmen wir meistens
diesen durchgezogenen Fall
Okay. Und da hatten wir noch was ganz komisches gesehen letzte Woche.
Das war mir eigentlich fast ein bisschen peinlich.
Ich schiebe jetzt mal das hier so ein bisschen runter,
dass ich da so ein bisschen mit den Armen wedeln musste.
Ich hatte diese merkwürdige Methode geschrieben und dieses merkwürdige Interface implementiert,
was ihr ja alles noch gar nicht kennt.
Und da habe ich mich gefragt, woran liegt das eigentlich?
Dass ich hier diese Magie einführen wollte.
Ich hatte das eigentlich gar nicht so geplant.
Und das können wir uns auch mal ganz kurz zusammen erschließen.
Also ich habe jetzt hier diesen Klienten, diese Klasse Main,
das tue ich jetzt mal nicht ins Diagramm,
da ist einfach nicht mehr genug Platz.
Aber was ich ganz interessant finde,
ist, es gibt hier zwei statische Methoden in diesem Main
Mache Jahresabschluss und schreibe Seiden auf Konsole.
Und beide Methoden nehmen eine Referenz auf eine Bank entgegen.
Das ist irgendwie extrem komisch. Oder ich sage mal so,
das ist so ein kleines Warnsignal, irgendwie komisch,
mache Jahresabschluss, klingt ja eigentlich gar nicht so,
als sollte das ein Klient machen.
Also stellt euch vor,
ihr geht zu eurer Bank und sagt eurer Bank,
gib mir mal eine Liste aller eurer Konten und ich sorge dann mal dafür,
dass der Jahresabschluss gemacht wird.
Da würde die Bank wahrscheinlich nur sagen, Moment,
wer sind sie? Was wollen sie von uns?
Das ist doch eigentlich unsere Aufgabe. Ja,
also diese Methoden würde man sagen,
gehören eigentlich gar nicht in irgendeinen externen Klienten,
sondern gehören direkt in die Klasse Bank
selber rein
Ja, ich nehme mir jetzt einfach mal diesen Code und schneide dann
einfach mal aus und packe den mal hier in die Klasse Bank rein.
Und jetzt müssen wir natürlich gucken, mit den Status,
das macht jetzt hier nicht wirklich Sinn.
Ich nehme das Static jetzt mal weg. Ich bin ja
schon in einer Bank drin. Und dann will ich hier
über meine Konten iterieren. Und vielleicht noch mal kurz als Kontrast,
dass ihr es umseht. Also dieser Parameter entfällt jetzt,
denn ich bin jetzt ja in der Klasse Bank,
ich habe jetzt ja direkt einen Zugriff auf die Bank.
Und anstatt jetzt über die Bank zu iterieren, iteriere ich über die Konten.
Das ist irgendwie sinnvoller, oder? Das heißt,
ich nehme jetzt hier auch diesen Parameter weg,
das Static weg und iteriere direkt über die Konten.
So. Und jetzt brauche ich diese blöde Magie nicht mehr.
Das heißt, ich löschte jetzt mal. Weg damit.
Das kommt auch weg. Dann bin ich schon viel beruhigter,
weil jetzt ist, will er irgendwas, warum,
warum sind die jetzt durch komischen roten Striche?
Achso, das ist in dem Klienten. Da kümmern wir
uns gleich drum. Aber jetzt dürfte in der Klasse Bank
eigentlich nichts mehr sein, was euch verwirrt.
Wir haben eine Liste, das kennen wir ja,
wir haben einen Konstruktor, wo wir eine R-Realist erzeugen.
Wir haben eine Methode, um ein Konto hinzuzufügen.
Und dann haben wir, Herrgood Private ist jetzt wahrscheinlich auch
nicht so sinnvoll.
Ich mache das mal
ganz kurz public
Das soll ja von außen aufgerufen werden. Und dann haben wir jetzt
zwei Methoden, die halt was mit diesen Konten machen.
Die Konten gehören der Bank und die Bank macht irgendwas mit diesen Konten.
Das ist doch irgendwie sinnvoll, dass der Code jetzt hier ist.
Okay. Gut, dann gehen wir jetzt mal in den Klienten.
Warum ist das denn jetzt nicht übersetzbar? Tja,
wir müssen die Methoden an der Bank aufrufen, richtig?
Ich muss also den Empfänger davor schreiben,
dann einen Punkt und dann gibt es natürlich hier auch keinen
aktuellen Parameter mehr, den lösche ich jetzt und hier genauso
Man nennt das gerne, immer wenn man sich so
Fragen stellt, wo gehört dieser Code eigentlich hin?
Das nennt man einen Entwurf nach Zuständigkeiten. Ich gucke also
irgendwie auf dieses Diagramm und stelle mir so vor,
eigentlich ist es doch viel sinnvoller, wenn die Bank selber ihren Jahresabschluss macht.
Also fügen wir die Methoden mal dazu, mache Jahresabschluss und
schreibe Salben auf Konsole. Es gibt überhaupt keinen Grund,
diese Konten, die ja eigentlich der Bank hören und privat sind,
irgendwie nach außen rauszureichen und dann anderen Leuten die Zuständigkeit zu überlassen,
da mal den Jahresabschluss aufzurufen,
das macht irgendwie keinen Sinn
Okay. Wer hat das verstanden, was ich hier
gerade vorgeführt habe? Okay, gut. Also immer,
wenn ihr so den Eindruck habt,
ihr müsstet irgendwas nach außen reichen,
weil ihr irgendwelche interessanten Algorithmen dann ausführen müsst auf diesen Daten,
schreibt es lieber direkt in die Klasse rein.
Gut, dann haben wir das jetzt gemacht.
Und eine spannende Frage letztes Mal, zu der sind wir
leider gar nicht mehr gekommen. Es ging ja letzte Woche
um das Vertragsmodell, auch noch so ein bisschen.
Wupps, das wollte ich nicht. Ich wollte eigentlich hierhin.
Genau, also bei dem Girokonto und bei dem Sparbuch.
Haben wir uns auch die Frage gestellt, wie ist das eigentlich mit dem Abheben?
Hatten wir da in dem Interface-Konto schon irgendwas, ja schaut mal.
Wir hatten also gesagt, Man kann nur positive Beträge einzahlen
und abheben. Aber wir haben uns die Frage gestellt,
darf ich denn mehr abheben, als drauf ist?
Ich nehme jetzt wieder einfach mal hier kurz diese Vorbedingungen.
Einfach der Vollständigkeit halber schreibe ich die auch noch mal ganz kurz in die konkreten Unterklossen.
Einmal hier beim Girokonto und dasselbe noch mal beim Sparbuch.
Und beim Girokonto waren wir uns, glaube ich,
alle einig, dass man mehr abheben kann, als drauf ist.
Dann freut sich die Bank. Dann müsst ihr irgendwelche Zinsen
oder so bezahlen. Das ist okay. Aber beim Sparbuch
darf man ja nicht mehr abheben, als drauf ist.
Wir könnten also eine Vorbedingung formulieren. Und die sagt,
wie kann ich das jetzt in einem Java-Ausdruck formulieren?
Ich darf nicht mehr Affemals drauf ist
Also wir haben ja hier diesen,
dieser formale Parameter sagt ja, wie viel drauf ist.
Und mit dieser Methode kann ich ja checken, Entschuldigung,
mit dieser Methode kann ich checken, wie viel drauf ist.
Und dieser Parameter sagt mir, wie viel ich abheben will.
Gibt es da jetzt irgendeinen mathematischen Zusammenhang, der auf
jeden Fall Geld muss? Du sagst, jedes Mal,
was gibt es nicht noch irgendjemand anders da hinten?
Betrag kleiner gleich Saldo. Was? Ah, Gibsaldo.
Kleiner gleich, Gibsaldo. Genau, man darf es also
leerräumen, aber man darf nicht noch mehr abheben
Sehr schön. Ich glaube, da sind wir uns
alle einig, dass das so halbwegs hinkommt.
Und jetzt ist es eine spannende Frage.
Wir haben jetzt also in dem Sparbuch, ja,
ich weiß nicht, kann man in der OML irgendwelche Vorbedingungen?
Ihr müsst euch das jetzt einfach vorstellen.
Es gibt hier diese zusätzliche Vorbedingung, ja.
Diese Methode sieht man jetzt hier auch leider nicht,
weil wir uns letztes Mal davon entschieden haben, sie nicht runterzukopieren.
Aber gut. Ich darf auf Sparbücher nicht mehr abhemals darauf,
was bei Giro konnten, darf ich das aber.
Jetzt ist die Frage, muss ich diese Vorbedingungen jetzt
auch hier oben in das Interface-Konto reinschreiben
Oder darf ich es nicht oder gehen beide Varianten?
Vielleicht können wir da einfach mal eine Abstimmung machen.
Denk doch mal ganz kurz drüber nach. Also die Aussage,
du darfst nicht mehr abheben, als es drauf ist.
Muss sie hier rein, darf sie nicht hier rein oder ist beides vorstellbar?
Das ist eine ziemlich interessante Frage. Ich habe da mehrere Minuten
drüber nachgedacht, bis ich zu einem Ergebnis gekommen bin.
Die ersten Hände gehen hoch. Ich glaube, ich mache
tatsächlich mal eine Abstimmung. Also wer ist dafür,
dass wir dieses Require auch in das Interface reintun müssen
Da sehe ich jetzt zwei, drei Hände oder so, okay.
Wer ist dafür, dass wir das auf keinen Fall machen dürfen?
Das wäre falsch. Okay, das sind die meisten.
Und wer ist dafür, dass beide Varianten gehen? Okay,
das Interessante ist, dass die meisten Leute Unrecht haben.
Ich weiß aber auch, woran es liegt. Denn die Intuition sagt,
wenn ich jetzt hier oben dieses Require reinschreibe,
mache ich so eine Aussage wie, es gibt keine Konten,
die man nicht überziehen darf.
Ich glaube, deswegen haben sich alle gemeldet.
Es gilt ja nicht für alle Konten. Deswegen habt ihr
euch wahrscheinlich gemeldet. Aber die Situation ist in der Objektunion
eine andere. Die Situation ist,
ihr habt eine Referenzvariable vom statischen Typ Konto und ihr wisst nicht,
ob sich ein Sparbuch oder ein Girokonto dahinter verbirgt.
Es könnte das eine sein,
es könnte das andere sein
Und die Frage ist jetzt, kann ich dem Klienten erlauben
oder kann ich ihm sagen, hey, keine Angst,
du kannst das Konto ruhig überziehen, es kann nichts passieren.
Oder muss ich defensiv sein und sagen, ich weiß ja
nicht genau, was sich für ein Konto dahinter verbirgt.
Wir müssen vorsichtig sein. Und das ist tatsächlich der Fall
. Wir müssen diese Vorbedingungen oben auch reinschreiben. So,
weil wir nicht wissen können, was sich für ein Konto
dahinter verbirgt. Wir müssen also die schärfste Vorbedingung,
die wir in den Subtypen haben, müssen wir hier reinschreiben.
Ja, es ist also
völlig in Ordnung
Wenn Subtypen das irgendwie weniger streng handhaben,
aber es darf niemals sein, dass Subtypen weitere Vorbedingungen hinzufügen.
Man sagt, Subtypen dürfen Vorbedingungen nicht verschärfen.
Merkt euch das mal für die Klausur. Es könnte sein,
dass sowas gefragt wird. Okay, das ist witzig.
Ich hatte das nämlich auch erst falsch rum. Okay,
ja, ich höre schon die ersten Leute lachen.
Das ist wirklich witzig. So. Dann fehlt, glaube ich,
noch was in unserem OML-Degam, was ich gerade gesehen habe.
Was diesen Zustand angeht. Also,
wir haben ja in Girokonto und in Sparbuch,
haben wir diesen Saldo und der taucht in dem Diagramm gar nicht auf.
Und das ist, glaube ich, eine gute Vorbereitung für
das heutige Thema, den dann nochmal reinzunehmen.
Ich sage also, sowohl das Girokonto Hatte ich da eigentlich Unterstriche verwendet?
Ich komme manchmal so ein bisschen durcheinander. Ja, habe
ich. Warum habe ich sie dann nicht ins Diagramm getan?
Wahrscheinlich habe ich es später mal geändert. Dann machen wir
das hier auch. So. Okay. Ich glaube,
jetzt haben wir den aktuellen Stand ganz gut erfasst.
Ja, und jetzt haben wir eigentlich genau die richtige Reihenfolge,
wie man sie machen soll.
Wir haben also zwei konkrete Klassen
Und wir überlegen uns jetzt, ob wir Gemeinsamkeiten dieser Klassenimplementationsgemeinsamkeiten,
ob wir die irgendwie rausziehen können. Wir gucken uns mal
ganz kurz an, ob wir diese Implementationsgemeinsamkeiten nochmal sehen im Quelltext.
Also einmal das, was ich ja gerade gesehen hatte,
das mit dem Saldo. Ich glaube, ich werde einmal
ganz kurz versuchen mit euch, ob ich das hinkriege,
dass ich das Sparbuch rechts nehme, das Girokonto.
Ach, guck mal, das funktioniert sogar. Was passiert,
wenn ich jetzt hier doppelt draufklicke? Passiert irgendwas? Nee
, passiert nicht. Jetzt kann ich das hier kleiner ziehen?
Ja, ziehen wir es doch mal so klein wie möglich.
Ich glaube, jetzt sieht man ganz gut was, oder?
Also wir haben in beiden konkreten Klassen Diesen Saldo drin.
Das ist ein Implementationsdetail, das man rausziehen könnte in eine abstrakte Oberklasse,
sodass wir es nur noch an einer Stelle haben.
Warum sollte man das machen? Stellt euch vor,
nächste Woche stellt ihr fest, ach ein Long war doch nicht so eine gute Idee.
Wir nehmen lieber einen Big Desimal. Ja, dann müsstet ihr momentan,
müsstet ihr das einmal hier ändern und einmal hier.
Und in allen anderen Subklassen von Konto oder in allen anderen Klassen,
die Konto implementieren. Wenn wir das einmal rausgezogen haben,
werden Änderungen recht einfach, weil es nur noch eine Stelle gibt,
die wir ändern müssen.
Also das heißt, so eine Kot-Duplizierung ist an sich erstmal
noch gar nicht so ein großes Problem.
Erst wenn Software geändert wird, was eigentlich immer passiert,
wird es dann ein zukünftiges Problem.
Das könnte man also rausziehen. Was haben wir denn noch?
Was machen wir denn so mit diesem Saldo? Ja,
guck mal, hier zum Beispiel das Zahle ein und hebe ab.
Da müssten wir mal gucken,
was wir da rausziehen können und was nicht beim Einzahlen.
Es ist ja wirklich komplett gleich. Und bei dem Abheben
haben wir verschiedene Vorbedingungen. Das könnte noch recht spannend werden,
inwiefern man da was rausziehen kann und was nicht.
Okay, das Giebseil, du hast in beiden Fällen gleich
, das wird man hochziehen können.
Ich glaube, den Rest werden wir nicht hochziehen können,
weil der ja speziell ist für die beiden Klassen.
Lass mal ganz kurz gucken, das ist auf jeden Fall
unterschiedlich. Und das ist auch jeweils unterschiedlich, was hier
drin gemacht wird
Okay, dann fangen wir doch damit einfach mal an.
Ich habe heute in der Betreuerrunde gesehen,
es gibt Möglichkeiten in den Clips, automatisch Refactorings zu machen,
die euch Dinge aus Unterklassen hochziehen.
Und das war sehr amüsant. Es hat eigentlich nicht das
gemacht, was wir wollten. Deswegen mache ich das jetzt
einfach mal von Hand. Gut. Das heißt also,
ich gehe jetzt hier mal in mein Geldprojekt und sage,
ich will eine neue Klasse haben.
Das seht ihr jetzt nicht, ich sage New Class.
Und dann sage ich doch mal App-Strack-Konto.
Das heißt jetzt hier alles ein bisschen anders auf den Folien,
auf den Folien hieß es einfach nur Konto,
aber so heißt ja schon unser Interface.
Das verwirrt euch hoffentlich nicht, dass die Namen hier teilweise
so ein bisschen anders sind und teilweise auch die Fachlichkeit
Okay. Also eine klasse Appstrack-Konto. Ich glaube,
ich nehme jetzt mal diesen komischen Modus hier raus,
weil drei Dinge kann ich nicht gleichzeitig anzeigen.
Und wir machen jetzt mal wieder Copy on Paste,
aber anschließend werden wir die Originale ja entfernen.
Also ich nehme jetzt mal dieses Saldo und packe das mal in die abstrakte Klasse.
Okay, was wollen wir noch nehmen? Das zahle ein,
das hebe ab und das gib Saldo,
das wollten wir auch irgendwie da reintun.
Zack. Okay, das ist schon spannend,
warum man sich hier beschwert.
Was ist hier
das Problem
Ah. Genau, es wäre vielleicht ganz sinnvoll.
Wenn wir diese abstrakte Klasse quasi hier reinschieben, ja,
zwischen die konkreten Klassen und das Interface,
das können wir einfach direkt mal machen,
dass euch klar ist, was ich hier überhaupt tue.
Ich will ja so eine Klasse haben, die heißt App-Stract-Konto.
Da soll der Saldo drin sein. Und den Rest schreiben wir
gleich vielleicht rein. Und der Witz ist jetzt eben,
dass ich das dazwischen schieben will.
Also dieser Fall, kann ich Pfeile im Nachhinein ändern?
Ich glaube, ich lösche ihn lieber und mache ihn nochmal neu.
Das geht wahrscheinlich schneller, als wenn ich jetzt versuche herauszufinden,
wie das funktioniert. Zu welchem Fall nehmen wir denn jetzt
mal in Harris
Das klingt doch ganz gut, wir waren davon erben.
So. Und diese Klasse implementiert dieses Interface. Und jetzt muss ich
das ein bisschen entzerren, weil so kann das ja keiner lesen.
Also dann machen wir das doch mal so, ups, nein.
Nein, den Fehler mache ich jedes Mal, Select.
Also die Klasse tun wir jetzt mal hier drunter.
Vielleicht irgendwie so ein bisschen Abstand, was nehmen wir mal
ganz, zwei, drei, vier, fünf.
Kriege ich das ja auch hin mit fünf, nee,
das passt nicht hin.
So, ich mag das, wenn es gleichmäßig ist.
Okay, so ungefähr, ja.
Es ist jetzt
einigermaßen eher
Naja gut, reicht. Ja, das ist also
unser Ziel. Die Gemeinsamkeiten hier rauszunehmen, das heißt,
ich lösche jetzt auch das Saldo hier raus,
das wollen wir ja konzeptionell machen.
Weg damit. Und hier raus würde ich es auch löschen.
Wieso verschiebt sich jetzt die Grafik so hässlich? Das finde ich
nicht so schön. Dann machen wir mal so und so.
Seht ihr, unser Plan sieht doch schon gar nicht schlecht aus.
Dieses Long Saldo soll es jetzt nur noch einmal geben.
Sehr schön. Genau, das wollen wir also machen.
Und wir haben jetzt noch diesen einen Pfeil noch nicht,
der Pfeil, den ich hier als letztes gezeichnet hatte,
diese abstrakte Klasse soll dieses Interface implementieren.
Diesen Fall, der befindet sich noch nicht im Code
Das mache ich jetzt einfach, indem ich ja sage,
Implements, Implementskonto, dann sollte er sich hier aufhören zu beschweren hoffentlich, genau.
Und was will er jetzt noch? Ach so, dieses Mache-Jahres-Abschluss
. Ah. Oh, das ist interessant. Also,
wir haben gerade versprochen, dass diese Klasse, dieses komplette Interface implementiert,
aber wir können ja nur die ersten drei Methoden implementieren.
Das mache ich Jahresabschluss, können wir ja gar nicht sinnvoll implementieren.
Ja, und jetzt beschwert sich der Compiler,
dass wir das nicht gemacht haben.
Sieht jemand das Problem oder die Lösung?
Also das Problem habe ich ja gerade erklärt,
was ist denn jetzt
die Lösung dazu?
Wir könnten Sie hinzufügen und leer lassen, aber das ist,
glaube ich, nicht so ganz im Sinne des Erfinders.
Du hast gesagt, es ist ja Abstract.
Das geht extrem in die richtige Richtung. Also wir haben
die Klassiker Abstract-Konto benannt, aber es fehlt noch was.
Also momentan weiß Java noch nicht, dass das eine abstrakte Klasse ist.
Ja. Nee, eine abstrakte Methode brauchen wir auch nicht.
Ja. Genau, ich muss einfach hier vorschreiben,
dass es eine abstrakte Klasse ist.
Ja, und dann verschwindet der Fehler
Also, wenn eine normale Klasse ein Interface implementiert,
muss ich für alle Operationen Methoden anbieten.
Und wenn eine abstrakte Klasse ein Interface implementiert,
dann muss das eben nicht der Fall sein.
Ja? Okay. Gut, hier gibt es also jetzt keine Beschwerden mehr.
Dann gehen wir doch mal zurück in die Klasse Konto.
Entschuldigung, in die Klasse Girokonto. Dann nehme ich jetzt
mal den Saldo hier weg. So, jetzt gibt es
hier ein Problem, darum kümmern wir uns gleich.
Das zahle ein, brauche ich nicht mehr selber implementieren.
Das macht jetzt ja die Superklasse, das hebe ab,
haue ich auch nicht und das gibt es da,
das brauche ich auch nicht
Jetzt haben wir noch so ein paar kleine, interessante Probleme.
Wo fangen wir nochmal an? Vielleicht erstmal mit der Holzhammer-Methode.
Also, hier wird gesagt, dass wir auf Saldo,
wäre es eigentlich sogar noch schlimmer, er sagt mir,
ich kenne das Saldo gar nicht.
Warum nicht? Ich gehe mal mit dem Cursor an die
richtige Stelle. Ja. Richtig, Extenz abstract-Konto.
Also quasi, welchen Pfeil habe ich hier gerade realisiert?
Diesen Pfeil, ne? Das hatte ich ja vorher
dem Java-System gar nicht mitgeteilt, dass ich jetzt von dieser
abstrakten Klasse Ebe eben erben will
Okay, jetzt müsst ihr eine andere Fehlermeldung kommen.
Genau, The Field Saldo is not visible.
Wieso ist das denn nicht visible? Wollen wir da mal
reinspringen? Wo ist es denn? Hier ist es.
Ist nicht visible. Du, das war noch nichts.
Aha, man könnte es protected machen, ne? Genau,
das könnte man machen und ich mache das jetzt auch einfach mal,
um euch zu zeigen, dass die Fehlermeldung dann verschwinden.
So. Und wir schauen nachher mal, ob wir das
auch ohne Protected hinkriegen. Ja,
weil es hat sich so die Meinung durchgesetzt,
protected für Zustandsfelder ist meistens nicht so
eine gute Idee
Ich habe da, glaube ich, sogar extra
einen Link rausgesucht. Wollen wir mal ganz kurz gucken,
ob ich den Link euch zumindest einmal zeige.
Dann könnt ihr euch den nämlich zu Hause in Ruhe durchlesen.
Protected Data Members. Genau. Protected Data Members ist gleich
Bad Design. Also ihr findet unglaublich viel, wenn ihr
einfach noch protected Data Members oder protected Fields oder so sucht.
Und so gut wie jeder Mensch,
der eine Programmiersprache entwickelt hat und das protected eingebaut hat,
hat sich später darüber geärgert.
Aber dann war es zu spät, um es, um es rauszubauen.
Gebt einfach mal in Google ein, protected Data Members oder protected Fields.
Da könnt ihr dann einfach mal im Selfstudium fahren,
warum das nicht so eine gute Idee ist.
Ja, aber haben wir dann nicht ein Problem?
Also nehmen wir mal an
Wir würden das jetzt wieder private machen oder ich mache es einfach,
dann ist es nicht so abstrakt, mein Gedankengang.
Ich mache es jetzt einfach wieder private,
aber dann haben wir doch jetzt wieder das Problem von eben.
Also ich kann an dieser Stelle nicht auf Saido zugreifen,
ich kann es hier nicht initialisieren und ich kann sie auch nicht verändern.
Vielleicht fangen wir mit der Stelle hier unten an,
vielleicht ist die einfacher. Also ich will, ja,
diese Gebühren abziehen und ich kann das jetzt nicht einfach
nur mit dem Minus gleich direkt auf dem Saldo machen.
Fällt euch irgendeine andere Möglichkeit ein, wie ich dafür sorgen kann,
dass auf einem Konto weniger drauf ist, ja.
Ich glaube, das ist nicht erlaubt,
wir gucken einmal
ganz kurz
Nee, das geht nicht. Das geht nur für Methoden.
Vielleicht geht noch mal aufs OML-Tagramm, weil die jetzt nicht alle Methoden sind.
Also gibt es in dem Typ-Konto irgendwo eine Operation,
die mir ermöglicht, hey,
vorher war so und so viel drauf und jetzt soll weniger drauf sein,
weil ich die Gebühren abziehen will oder so.
Gibt es hier irgendeine Methode, die etwas abzieht von dem Konto?
Ein Minusgleich auf einer etwas höheren Ebene? Ja.
Ja. Und man kann ja was abheben vom Konto.
Ob das jetzt die sauberste Lösung ist oder nicht,
weiß ich nicht. Könnt ihr euch in Ruhe überlegen,
ich mache es jetzt einfach mal
Wenn es euch nicht gefällt, könnt ihr auch einfach irgendwelche Protected-Methoden schreiben,
die euch dann direkt in Zugriff darauf erlauben.
Aber ich finde das so irgendwie ganz nett. Okay,
jetzt haben wir hier aber noch ein Problem. Also,
wir gehen noch mal ganz kurz auf das OML-Diagramm zurück.
Ich habe hier diese private Exemplarvariabler und momentan war der Code noch so strukturiert,
dass ich diese private Exemplarvariable in einer anderen Klasse initialisiert habe.
Das scheint irgendwie nicht sinnvoll zu sein, oder?
Ich hatte im Konstruktor vom Girokonto,
wollte ich eine private Exemplarvariable einer anderen Klasse initialisieren.
Das ist doch irgendwie Quatsch. Entwurf nach Zuständigkeiten.
Wer sollte dafür zuständig sein, diese Exemplarvariable zu initialisieren?
Ja, natürlich die Klasse selber
Und das heißt also, in der Klasse Abstract-Konto sollte es
einen Konstruktor geben. Und das ist immer die Stelle,
wo viele Programmieren vielleicht sagen, aber das macht doch gar keinen Sinn,
ich kann doch gar keine Exemplare davon erzeugen.
Eine Klasse, von der ich keine Exemplare erzeugen kann,
warum sollte die denn einen Konstruktor haben?
Und dann sage ich immer, naja,
ein Konstruktor ist ja kein Erzeuger, sondern nur ein Initialisierer.
Und das ist genau das, was wir machen wollen.
Wir wollen initialisieren. Also, ich mache einen Konstruktor,
Appstract-Konto, der kriegt einen Saldo übergeben und sagt dann,
unterstrich Saldo,
gleich Saldo
Das ist überhaupt kein Problem aus der Java-Perspektive. Jede Klasse,
die über Exemplarvariablen verfügt, sollte auch ein Konstruktor haben, der die initialisiert.
Das heißt nicht, dass ich von außen jetzt sagen kann,
New-Appstract-Konto, das wird nach wie vor nicht gehen,
weil ja Java sieht, Moment, die Klasse ist abstrakt,
dann lasse ich ein New-Appstract-Konto nicht zu.
Jetzt ist aber natürlich die Frage, wenn ich nicht New-Appstract-Konto
sagen kann, wann wird denn dieser Konstruktor überhaupt aufgerufen?
Es macht also keinen Sinn, von außen zu sagen,
hey, ich will diesen Konstrukt aufrufen,
ich will
dieses New-Appstract-Konto machen
An welchen Stellen würde es denn Sinn machen, diesen Konstruktor aufzurufen?
Sollte die Bank den Konstruktor aufrufen oder ja? Richtig. Aus
den Konstruktoren von Girokonto und Sparbuch sollten wir diesen Konstruktor aufrufen.
Ja? Denn da hatten wir ja auch gerade das Problem
genau an dieser Stelle. Wir waren in dem Konstruktor von Girokonto
und hatten hier dieses Problem, wir wollten eine Initialisierung machen,
für die wir eigentlich gar nicht zuständig waren.
Ich will jetzt, ich schreibe es mal auf Deutsch hin,
rufe den Konstruktor der Oberklasse auf und übergib Saldo als aktuellen Parameter.
Das wollen wir doch
jetzt machen
Der Konstruktor von oben soll sich darum kümmern.
Der weiß, wie er mit dem Sado umzugehen hat.
Könnt ihr schon wissen, wie man das macht?
Ja. Ja, mit super, genau.
Ich sage einfach super. Und dann übergebe ich den Saldo.
Das ist so ein etwas merkwürdiger Syntags. Wie will er das?
Wenn ich speichere, aus irgendeinem Grund will er es nicht.
Warum will er es nicht? Ach, habe ich Innenstadtlong geschrieben
? Ach, guck mal, wie blöd ich bin.
So. Das macht er
Also diese sehr kompakte Zeit ist Supersaldo, hat nichts mit
irgendwelchen Superhelden zu tun. Heißt einfach nur, ich will
den Konstruktor der Oberklasse aufrufen und Saldo als Parameter übergeben.
Ich lösche jetzt mal den Kommentar. Und wenn ihr irgendwie
neugierig seid, was ist denn das für ein merkwürdiges Sprachkonstrukt?
Wenn ihr F3 drückt, dann landet ihr,
wie ihr es kennt, in der Definition dieses Superkonstruktes.
Ist das überhaupt keine Magie? Es ist einfach nur eine
komische Syntax. Okay. Gut. Dann haben wir
das gemacht. Jetzt ist also hier alles übersetzbar.
Wollen wir jetzt dasselbe mal mit dem Sparbuch machen?
Oder gibt es jetzt nur noch Fragen?
Gibt es hierzu
irgendwelche Fragen
Ja. Gut. Dann gehen wir ins Sparbuch.
Und schauen mal, dass wir jetzt denselben Kram machen.
Also, wir brauchen den Saldo nicht mehr. Der ist
ja schon in der Oberklasse. Dann können wir den Saldo
nicht selber initialisieren, sondern müssen es über den Superaufruf machen.
Okay. A. Ich muss ja auch sagen, dass ich
dieses Abstract-Konto, dass ich davon erben will, okay.
Dann brauchen wir dieses ganze Zahle ein und hebe ab.
Nicht mehr. Wie gesagt, mit den Vor- und Nachbedingungen.
Da können wir uns, wenn am Ende Zeit ist,
nochmal drum kümmern, sonst müsst ihr das als Hausaufgabe machen.
Okay. Ach so, ja. Und hier,
was müssen wir denn jetzt
hier machen
Ja. Genau, wir zahlen einfach ein, ne?
Ich finde das völlig okay, wenn ihr das nicht so schön findet,
was ich hier gerade an der Stelle mache.
Ihr könnt es dann ja hübscher machen. Das ist ja kein Problem.
So, ne? Eigentlich auf Saldo habe ich ja keinen Zugriff.
Diebster Album. So. Okay. Normalerweise würde man jetzt
die Tests durchlaufen lassen, ob alles noch funktioniert,
aber wir haben ja leider keine Tests, ne?
Also das ist jetzt so eine Stelle,
da hätte man gerne vorher Tests geschrieben.
Aber in den 45 Minuten der Vorlesung ist es meistens nicht
so einfach. Mit der Zeit dahin zu kommen.
Ich glaube, dann habt ihr dazu jetzt auch keine Fragen.
Das ist ja dasselbe in Grün, ne? Okay. Spiegelt
unser Diagramm jetzt den aktuellen Stand wieder oder haben wir irgendwas vergessen?
Nee, ich glaube, das sieht ganz gut aus.
Okay. Dann wollte ich als nächstes was mit euch machen,
um euch weiter auf die Übung vorzubereiten.
Ich wollte in das Interface-Konto eine neue Operation reintun.
Wie wollen wir die nochmal nennen? Flat Beschreibung.
Sie liefert einen String zurück und heißt Beschreibung.
Ich will also irgendwie das irgendwie eine halbwegs sinnvolle Beschreibung dieses Konto
als String zurückgeliefert wird.
Also was ist das für ein Konto und wie viel ist gerade drauf?
Vielleicht reiterst du schon als Beschreibung. Gut. Jetzt,
wenn ich das hier in mein Interface reintue,
können wir uns überlegen, was wird das für Konsequenzen haben,
an welchen Stellen wird es krachen?
Wupps, das wollte ich nicht. Beschreibung. Schauen wir
mal, wo jetzt die roten Xe kommen. Sie kommen jetzt
bei den konkreten Typen und nicht zum Beispiel bei Abstract-Konto.
Also irgendwie fehlt jetzt bei den konkreten Typen was.
Dann machen wir doch so erstmal ganz konkret,
ist ja mal eine gute Herangehensweise
Also der Compiler wird jetzt hier sagen,
hier fehlt einfach, ja, die Implementation zu Beschreibung.
Die fehlt jetzt hier einfach. Ich sage einfach Steuerung eins
und sage, add unimplemented methods. Dann haben wir sie
hier unten. Und ich zeige euch jetzt einmal,
wie ich so eine Beschreibung, wie ich die formatiert haben will.
Ich will also vielleicht, dass hier einfach steht, Giro,
Konto mit, gibt es halt auch Cent drauf oder so.
Ja, ist jetzt ziemlich daher improvisiert. Okay. Und
so was ähnliches will ich haben. Ach so,
wir haben ja noch mehr, wir haben ja noch mehr Zusatzinformationen.
Was haben wir denn noch in dem Giro-Kontonent?
Eine Cent-Gebühr. Das wäre doch auch nicht schlecht,
wenn wir das hier hätten
Gebühren, betragen. Sandcampure. Sand. So. Ach,
dieses Deutsche, das gefällt mir eigentlich gar nicht so wahnsinnig gut.
Ich glaube, wir machen das mal mehrzeitig mit einer schöneren Formatierung.
Also sagen wir vielleicht einfach mal Saldo, Doppelpunkt, Giebsalde.
Und dann kommt ein Zeilenumbruch, Backslash, ups,
Backslash N ist ja ein Zeilenumbruch. Dann könnten wir sagen,
Gebühren, sonst wird das so lange Sätze,
das gefällt mir irgendwie nicht. So, Saldo,
Gebühren, das Cent brauchen wir eigentlich auch nicht unbedingt.
Vielleicht noch ein Zeilenumbruch am Ende. Und am Anfang,
wie gesagt, die Information,
dass es ein Girokonto ist.
So. Das sieht, glaube ich, ganz gut aus.
Okay, da machen wir das gleich nochmal im Sparbuch oder etwas ähnliches.
Dann sage ich jetzt, also hier an dieser Stelle Sparbuch,
ein Saldo hat es ja auch.
Es hat jetzt keine Gebühren, sondern was hat stattdessen
einen Zinssatz? So. Und das ist ja ein Zinssatz.
So. Okay. Ja, dann könnten wir doch eigentlich
mal in unsere Bank gehen
Und vielleicht mal, wir hatten ja hier irgendwo diese Methode Schreibesalden
auf Konsole. Das war ja eine ziemlich langweilige Methode,
die da einfach wirklich nur die Salden auf die Konsole geschrieben hat.
Ich nenne das mal um und schreibe Konten auf Konsole
und dann sage ich jetzt hier, ja,
im Prinzip kann ich das direkt so machen.
Ich will auf dem Konto direkt diese Beschreibung mehr abfragen.
Also ich kriege von dem Konto diesen String zurück und
den schreibe ich einfach auf die Konsole.
Das ist doch irgendwie verständlich. Dann müssen wir es
natürlich in Main auch noch anpassen.
Das hätte ich mal lieber mit dem Refactoring machen sollen.
So. Okay
Ja, vielleicht machen wir das einmal davor und einmal danach,
das ist vielleicht auch ganz nett.
Und dann können wir das einfach mal starten und gucken,
ob da irgendwas Sinnvolles bei rauskommt.
Wo ist mein... So. Könnt ihr alles sehen? Ja. Okay,
also wir hatten erst ein Girokonto mit 500 Euro und einem Euro Gebühr,
ein Sparbuch mit 10 Euro und ein Promille-Zinssatz.
Dann hatten wir den Jahresabschluss gemacht, dann war auf dem Girokonto
weniger drauf und auf dem Sparbuch war ein bisschen mehr drauf.
Das macht auch alles irgendwie Sinn,
Gut. Und dieser Schritt war für euch auch
überhaupt nichts Neues, denn, was haben wir gemacht?
Wir haben ja einfach nur in ein Interface eine Operation reingepackt
und wir haben diese Operation in konkreten Klassen implementiert.
Das macht ihr seit sechs Monaten, oder?
Das will ich jetzt nicht erstaunt, wenn ihr mir hier
einfach wegschlaft, wenn ich jetzt so weitermache.
Aber jetzt wird es ja spannend. Denn wir überlegen jetzt
wieder, gibt es irgendwelche Gemeinsamkeiten zwischen Girokonto und Sparbuch?
Ich will das mal irgendwie so ziehen, dass sie nebeneinander sind.
Beziehungsweise im Moment, nee, Giro-Konto so und Sparbuch
dann so
Genau. Ob es in diesem Format irgendwelche Gemeinsamkeiten gibt,
die wir ausnutzen können. Jetzt ist es natürlich blöd,
dass hier das alles so klein ist.
Vielleicht muss ich hier noch öfter auf Enter drücken.
Also, ja, es tut mir leid,
ich muss jetzt hier einfach ein paar Mal Enter drücken.
Vielleicht irgendwie so. Okay, und hier machen wir es mal genauso,
so gibt es halt, dann haben wir den Zins-So.
Jetzt ist doch ungefähr gleich gelayoutet, oder? Wenn man
das Layout nennen kann, was ich hier mache,
Usky Layout, haben das ungefähr so, ja.
Und jetzt schauen wir, ob wir da Gemeinsamkeiten finden
Was haben wir denn da? Also irgendwie so von hier
. Bis hier sehe ich irgendwie Gemeinsamkeiten. Also Saldo,
Doppelpunkt mit dem entsprechenden Saldo, soll auf jeden Fall ausgegeben werden.
Ihr seht, das passiert hier genauso.
Aber natürlich ist der String davor irgendwie anders. Und das,
was danach kommt, ist auch noch irgendwie anders,
weil sich das ja unterscheidet in den verschiedenen Subtypen.
Das wirkt jetzt vielleicht ein bisschen an den Haaren herbeigezogen,
was ich mache. Aber man könnte tatsächlich die Generierung dieses Teilstrings,
könnte man rausziehen. Und ich mache es jetzt mit euch.
Das ist so eine kleine Fingerübung. In der Übungsgruppe wird
es ein bisschen ein größeres Beispiel, wo es ein bisschen
sich mehr lohnt
Mache ich das erst mal wieder. Wie kriege ich das wieder klein?
Ich schließe es einfach. Das ist so ein kleiner Hack,
um das wieder klein zu kriegen. Gut,
das heißt, ich nehme jetzt diese Methode namens Beschreibung,
kopiere sie erst mal komplett in meine abstrakte Oberklasse und dann lösche ich alles,
was nicht abstrakt ist sozusagen.
Also das Girokonto kann ich noch nicht wissen. Saldo kann ich wissen,
gib Saldo auch und vielleicht den Wechslesh Ende hinter.
Das ist vielleicht ganz nett, dann ist es eine komplette Zahl.
Den Rest muss ich löschen,
weil ich auf dieser Abstraktionsebene ja noch nicht weiß,
was für eine Art von Konto das ist und ob
das irgendwelche speziellen Eigenschaften hat oder nicht.
So, jetzt würde ich
das einmal formatieren
Also die Beschreibung eines abstrakten Kontos liefert mir nur so ein Saldo-String.
Das ist ja nicht besonders viel. Aber was nützt es mir jetzt?
Das nützt mir Folgendes. Ich kann das jetzt an dieser Stelle,
muss ich das hier einmal kurz quasi so zumachen.
So, an dieser Stelle kann ich das jetzt quasi in
die Mitte dazwischen kleben. Ich kann also jetzt sagen,
ein Girokonto soll in seiner Beschreibung erstmal diesen String-String-Girokonto haben.
Dann soll, ja, das,
was ich eben gerade quasi geschrieben habe, aufgerufen werden.
Und der String soll da eingeklebt werden.
Und dann soll der Rest da reingeklebt werden.
Aber wie kann ich denn jetzt
Aus der Klasse Girokonto, die Methode-Beschreibung aus der Klasse da drüber aufrufen.
Habt ihr das in den Folien heute schon gelernt?
Also bei einem Konstruktor hatten wir gesagt, wir nehmen das
super Schlüsselwort und schreiben dahinter und klammern einfach die aktuellen Parameter.
Wie kann ich jetzt eine Methode aus einer Klasse über mir aufrufen?
Ich weiß, ich bin mir nicht 100% sicher,
ob es auf den Folien stand, ja?
Sehr gut. Super, Punkt. Und wie heißt die Methode?
Es ist ja die Methode, die wir gerade überschreiben.
Es ist die Methode-Beschreibung. So, gibt es jetzt hier
irgendwelche Probleme? Ich habe einen Plus zu viel
Ja. Kann ich das schöner formatieren oder passt es dann
wieder nicht hin? Ich gucke einmal ganz kurz,
ja, schade, es passt nicht ganz auf meinen Bildschirm.
Komisch eigentlich. Ich hätte erwartet, dass das Backstage Ende hinten noch reinpasst.
Egal. Ja, also wir haben also hier etwas,
was für uns spezifisch ist. Dann rufen wir die Beschreibung
der Oberklasse auf und dann kleben wir wieder spezifischen Kram dahinter.
Ja, das mache ich jetzt einfach mal in dem Sparbuch genauso.
Nee, wir sind, wo ist mein Sparbuch?
Hier ist es. Ich nehme also jetzt hier quasi
diesen Streng hier immer weg. Den will ich mir ja
aus der Superklasse geben lassen
Und dann brauche ich auch diesen Zeilenumbruch nicht mehr.
So, was fehlt jetzt? Das Plus ist zu viel.
Okay. So, jetzt machen wir einen manuellen Test.
Ich habe das Programm noch nicht gestartet. Ich ziehe das
jetzt mal so hoch, dass man alles sieht.
So, wenn ich das Programm nochmal starte,
sollte dasselbe da stehen wie jetzt. Achte mal darauf,
dass es einmal vielleicht zwischendurch blinkt oder so.
Ja, es hat sich ein bisschen leicht anders gescrollt.
Ich glaube, das liegt aber an der Eclipse-Konsole.
Ja, ja. Es ist dasselbe wie vorher,
oder? Also erst Girokonto bzw. Sparbuch,
dann auf jeden Fall der Saldo und dann jeweils die Gebühren
oder der Zinssatz
Okay, was ist jetzt der Vorteil davon?
Der Vorteil davon ist, wenn ich irgendwann mal ändern will,
wie diese Zeile aussehen soll auf der Konsole,
dann kann ich das an einer Stelle ändern, nämlich hier.
Ja, wenn ich irgendwie, keine Ahnung,
hier ein Smiley-Face haben will und das nochmal neu starte,
dann werdet ihr sehen, dass dieses Smiley-Face jetzt überall auftaucht.
Sowohl bei den Girokonten als auch bei den Sparbüchern.
Okay. Gut. Das ist also die eine Möglichkeit,
wie man es machen kann. Gibt es dazu noch Fragen
? Also das ist jetzt erstmal nichts Besonderes,
das ist eine ganz normale Methode,
aber ich rufe diese Methode von hier unten auf mit diesem Superschluss,
aber das ist eine Frage
Wir haben jetzt auch die Tiefe, das Tierobrat,
das Bratkartentöne. Das ist das ruhigeste. Ja,
also wir könnten tatsächlich sagen, hey, wir nennen die
Methode nicht Beschreibung, sondern wir nennen sie To String.
Oder meinst du, Entschuldigung, oder meinst du,
dass du diesen String auf magische Art und Weise generieren willst?
Ah, das ist eine spannende Frage. Ja,
Thisto String wird dir dann sagen, Girokonto, Klammeraffe,
1, 2, 3, 4, 5,
6, 7 oder so.
Vielleicht kann man da dann irgendwas machen.
Spontan weiß ich es nicht, schauen wir gleich mal.
Schauen wir gleich mal. Okay,
aber gibt es sonst noch Fragen zur Funktionsweise von dem,
was hier bereits steht?
Warum das funktioniert oder so
Okay. Wir haben jetzt noch ein kleines Mini-Problem oder eine kleine Möglichkeit,
die man noch so ein bisschen schöner machen kann.
Das grundsätzliche Format, vielleicht zeige ich es einfach nochmal in Textformen,
dass man ja hier unten sieht, dass grundsätzliche Format ist ja eigentlich immer gleich.
Ich will also nicht,
dass Girocon und Schwabücher völlig anders auf die Konsole geschrieben werden,
also in einem Fall in der Tabelle und in einer anderen Form irgendwie eindimensional oder so,
sondern ich will eigentlich immer diese drei Zeilen haben.
Diese erste Zeile weiß ich noch nicht so genau, was sie sein soll.
Die zweite Zeile ist völlig klar und die dritte Zeile weiß
ich auch wieder nicht, was sie sein soll.
Und dafür habt ihr ja auch schon was kennengelernt. Guido hat euch doch heute vorgestellt,
das Beispiel mit dieser Abstract-Titel ist,
Wenn ich so eine grobe Vorstellung davon habe,
wir etwas ablaufen soll, aber es gibt so ein,
zwei Stellen, wo ich dann später konkrete Implementationen reinschieben will.
Dafür eignet sich die Schablon-Methode sehr gut. Das heißt,
ich zeige euch jetzt einmal, wie man das alternativ mit der Schablon-Methode lösen kann.
Also vielleicht noch mal ganz kurz in dem UML-Therapen,
dass der Unterschied klar wird.
Wir hatten momentan das Muster einmal hier drin implementiert und einmal
hier drin implementiert und jeweils aktiv die Supermethode aufgerufen.
Jetzt will ich den Kontrollfoss genau umkehren. Ich will also
sagen, das Muster soll in Abstract Konto festgelegt werden.
Ich weiß, es gibt diese drei Bestandteile und einen davon
kann ich sogar selber schon bereitstellen.
Und ich will nur noch die beiden Einschubmethoden in den Unterklassen definieren.
Ich zeige euch einmal, wie das geht
Und dann sind wir für heute, glaube ich, auch fertig.
Also ich gehe mal, ich nehme vielleicht einfach mal
diesen konkreten String, gehe wieder in die App-Straight-Kontoklasse.
Kann man eigentlich alles sehen? Ich schiebe das noch mal
ein bisschen nach oben. Eigentlich ist ja nichts relevant,
was da drüber steht. Also ich paste das jetzt einfach
erst mal hier rein. Das ist jetzt erst mal ein
bisschen unsinnig. Also. Das, was hier steht,
also dass da später mal Girokonto oder Sparbuch stehen soll,
das weiß ich an dieser Stelle noch gar nicht.
Das heißt, ich müsste hier im Prinzip jetzt sagen,
ja, Typbeschreibung oder so oder Art des Kontos oder so.
Ich nenne das jetzt vielleicht einfach mal Beschreibung,
Okay, diese Methode gibt es noch gar nicht, ist aber nicht schlimm.
Ich stelle mir einfach vor, es würde sie geben.
Dann kann ich hier meinen konkreten String reintun. Da weiß
ich ja, dass der das sein soll. So,
und dann hier kann ich wieder sagen,
ich weiß eigentlich gar nicht genau, was es ist,
Beschreibung extra, Extras oder so, ja,
was auch immer, dieses Konto noch an Zusatzinformationen hat,
das soll es bitte rausgeben.
Ja, ihr seht also jetzt,
dass die Beschreibung aller Konten demselben Muster folgen soll,
ich will erstmal eine Beschreibung des Typs haben,
dann soll da stehen Saldo mit dem Smiley Smiley,
dann der Saldo oder ein Zeilenumbruch und dann die Beschreibung
der Extras
Diese Methoden gibt es aber jetzt gar nicht. Was mache ich
denn jetzt? Wo sollen denn diese Methoden jetzt herkommen?
Wo muss ich sie definieren? Wie muss ich sie definieren
? Ich habe nichts verstanden. In Girokonto und Sparbuch.
Das Problem ist, wenn ich die da jetzt reinschreibe,
dann werden die an dieser Stelle ja gar nicht bekannt sein.
Ja, ich kann ja in der Klasse Abstract Konto
nichts aufrufen, was vielleicht in irgendwelchen Unterklassen drinsteht.
Sehr gut. Also wir sind uns noch nicht 100 Prozent
sicher mit der Sichtbarkeit, aber Abstract war das Wichtige.
Ja, ich sage jetzt also an dieser Stelle,
ich kann ja erstmal die Sichtbarkeit weglassen, App Stract,
String, Beschreibung, Typ.
Und App Stract, String, Beschreibung, Extras.
Und jetzt ist der Compiler zufrieden. Auf Ebene der Klasse
App Stract Konto ist jetzt alles in Ordnung
Ich habe hier in einer abstrakten Klasse eine konkrete Methode.
Innerhalb dieser konkreten Methode rufe ich abstrakte Methoden von mir selber auf,
zu denen es jetzt noch gar keine Methoden gibt.
Also es sind ja in dem Sinne nur Operationen,
an der Einstellung und an der anderen Stelle.
Aber es ist in sich erst mal geschlossen und völlig okay.
Jetzt tauchen hier aber natürlich rote Xe auf,
denn Girokonto und Sparbuch sollen ja Apps-Rekonto implementieren.
Das heißt, da fehlt jetzt natürlich was. Der Compiler
wird mir jetzt sagen, hey, diese Methoden fehlen.
Schaut doch einmal ganz kurz, ob er mir das wirklich sagt.
Type Girokonto must implement inherited appstract method
Beschreibung Typ
Und wahrscheinlich noch weitere. Also ich sage jetzt wieder Steuerung 1,
add unemplemented methods und ihr seht sogar hier unten,
welche das sein werden.
Zack. Dann sind sie hier unten. Und jetzt müssen wir
uns natürlich darum kümmern, dass da was Sinnvolles steht.
Oh, ich sehe gerade den Quatsch von hier oben,
brauchen wir gar nicht mehr. Das nehme ich einmal ganz kurz,
oder ich lasse das einmal ganz kurz stehen.
Weil dann kann ich es mir nämlich einfach rauskopieren.
Also als Typ wollte ich ja das hier nehmen.
X. Zack. Und für die Extras wollte ich ja
das hier nehmen. Zack. Und dann kann ich
diese Methode hier löschen.
Und dasselbe machen wir jetzt in dem Sparbuch.
Der Compiler beschwert sich. Ich sage ihm,
dass er diese Methoden bitte hinzufügen soll. Und genau,
dann kopiere ich mir das hier einfach wieder raus.
Zack. Zack. Und dann haben wir hier den Sinssatz.
So, was habe ich da mit der Tastatur gemacht?
So, wir nehmen jetzt einfach mal an,
dass hier irgendein Quatsch steht.
Das mache ich noch mal einmal ganz kurz.
Ich lasse das mal extra drin. Ich würde euch
mal zeigen,
welchen Effekt das jetzt hat
Genau, ihr seht also, diese Beschreibung für Sparbücher,
ja, es ist zwar wunderbar,
dass ich in diesem Appstrack-Konto die Beschreibung so definiert habe,
aber es hindert euch ja keiner daran,
später in einer Subklasse, das nochmal zu überschreiben.
Und dann wird wieder diese Variante genommen. Wenn ihr das irgendwie ausschließen wollt,
wenn ihr sagt, nee, nee, nee, nee, nee,
so wie das hier steht, ist es schon richtig,
es darf auf keinen Fall in Unterklassen überschrieben werden,
dann könnt ihr die Methode als Final deklarieren.
Final heißt also in diesem Zusammenhang, nie wieder überschreibbar und
da wird sich der Compiler an dieser Stelle beschweren,
du darfst diese Methode nicht überschreiben.
Das hilft euch ganz, ganz schnell immer solche Bugs zu finden.
Ich wollte sie ja auch gar nicht überschreiben,
das war nur aus einer alten Version
So, jetzt können wir das hier wieder wegnehmen.
Und hier kann ich auch diese To-dos mal wegnehmen.
Und dann werden wir sehen, dass es hoffentlich funktioniert.
Genau. Und was ich jetzt nochmal einmal ganz kurz mit euch machen wollte,
ist da mal mit dem Debugger durchzusteppen, um nochmal dieses,
ja, Jojo oder Gummiband oder was auch immer im Prinzip mit euch zu verstehen.
Also wo ist in meiner abstrakten Klasse, hier ist ja diese Methode.
Ich sage jetzt hier, ich will hier einen Haltpunkt haben.
Ich würde sagen noch zwei, drei Minuten ruhig,
dann seid ihr entlassen.
Und dann debugge ich das jetzt mal als Java-Applikation.
Jetzt müsste irgendwas Spannendes passieren.
Ich hoffe, man sieht jetzt irgendwas
Okay. Also ihr seht hier oben in dem Dibagger-Window,
die SIS-Referenz hat den dynamischen Typ Girokonto.
Wir sind also gerade irgendwie in der, ja,
auf einem Objekt der Klasse Girokonto am Rumhühnern.
Der Debagger ist jetzt gerade hier in dem App-Strack-Konto.
Bei euch ist er hoffentlich klar, zur Laufzeit gibt es keine Exemplare von App-Strack-Konto.
Ja, wir sind jetzt also entweder gerade auf einem Exemplar
der Klasse Girokonto oder Sparbuch. In diesem Fall Girokonto,
weil das einfach das erste Konto ist, was ich erzeugt habe.
Okay. Und jetzt wollen wir diese komplizierte Zeile ausführen.
Jetzt guckt sich Java, geht ja immer von links nach rechts.
Okay, der Programmierer will also Beschreibung, Typ aufrufen.
Da würde ich jetzt mal reinspringen, mit welcher F-Taste geht das?
F5, das könnt ihr nicht sehen, oder?
Ach doch, guck mal, Step in Toy 5.
Das steppe ich jetzt also rein und jetzt seht ihr,
ich bin jetzt in der Klasse, Class-Loader,
weil er die Klasse Girokonto zum ersten Mal laden muss.
Dann gehe ich da mal wieder raus. Und wenn ich
jetzt reinsteppe, dann sind wir in der Klasse Girokonto.
Ja, das ist dieser Jojo-Effekt. Also wir waren ja
tatsächlich auf einem Girokonto und deswegen sind wir jetzt hier
in die Methode der Klasse Giro-Hanto gegangen.
Okay, dann gehen wir das mal zurück. Gib Saldo,
da können wir theoretisch auch einmal aus Spaß reingehen.
Warum nicht? Jetzt muss er die Klasse wieder laden.
Das ist so ein bisschen langweilig mit dem Glas-Loader.
Das tut mir leid. Noch mehr Glas-Geloader. Ich will
doch eigentlich nur in Gib Saldo reingehen.
Was macht er denn hier alles
Ah, da sind wir. Das war diese Streamconcat-Konkadinationsmagie im Hintergrund.
Es tut mir leid. Das war jetzt aber in unserer eigenen Klasse.
Wir sind ja quasi von hier gekommen. Das war jetzt
also keine Magie. Okay, springen wir wieder zurück.
Und jetzt mussten wir als letztes noch in Beschreibung Extras reinspringen.
Wir sind immer noch auf diesem Girokonto. Das heißt,
wir werden jetzt in der Methode von der Klasse Girokonto landen.
Ja, nachdem er da mit dem erpennt und so,
okay, ja, so.
Jetzt sind wir hier in den Beschreibungen Extras von dem Girokonto.
Ihr müsst euch immer daran erinnern an dieses Thema dynamischer Typ
Zur Laufzeit sind Objekte immer von konkreten Klassen.
Und selbst wenn ihr scheinbar abstrakte Methoden aufruft,
natürlich muss irgendwie eine Methode auf einer konkreten Methode gesprochen werden,
das geht gar nicht anders.
Okay. So, dann gehe ich mal wieder,
dann stoppe ich das hier einfach mal alles.
Und dann gehe ich, glaube ich,
nochmal als Abschluss, ja, genau, hierhin.
Also, das wird auch gerne verwechselt. Diese Methode nennt man Schablonen-Methode.
Okay. Und die Methoden, die ihr da aufruft,
die in dieser Klasse noch abstrakt sind, die nennt man Einschubmethoden.
Ja, das werdet ihr garantiert gefragt werden.
Deswegen wollte ich das einfach nochmal gesagt haben.
Okay, vielen Dank,
bis nächste Woche