Okay. Ja, herzlich willkommen zur achten Vorlesung von SR1.
Heute haben wir zwei relativ große Themen,
aber bevor ich da jetzt inhaltlich werde,
möchte ich erst mal ein paar Worte sagen zu den Leuten,
die hier das Ganze ermöglichen.
Es sieht ja immer so aus,
als wenn ich hier vorne die Show abziehe und die Person bin,
die das Ganze ermöglicht.
Aber das ist natürlich nicht ganz richtig. Hier stecken erst
mal sieben Jahre Arbeit drin. Das ist mindestens sieben Jahre.
Wir haben die Veranstaltung also schon 2005,
sechs das erste Mal durchgeführt
Aber wie gesagt, nicht nur mein Engagement steckt da drin,
sondern auch von sehr vielen anderen Leuten.
Inzwischen haben über tausend Studierende an diesem Modul teilgenommen,
also wahrscheinlich schon bald an die 2000.
Zahlreiche Betreuer, also wir haben sehr viele juländische Hilfskräfte,
wisst ihr ja auch, bei uns in der Veranstaltung.
Wir haben aber auch sehr viele Vimis, die da schon mitgemacht haben.
Und auch die haben dazu beigetragen, dass das Ganze hier
verbessert werden konnte. Ihr habt es ja wahrscheinlich mitbekommen,
dass ihr schon die Möglichkeit habt,
hier auch eure Meinung kundzutun, dass es das Komsi gibt,
dass ihr dieses Komsi auch nutzen solltet,
um Feedback zu geben zur Veranstaltung
Auch ihr könnt da eigene Diskussionsstränge aufmachen. Ihr solltet das ruhig nutzen.
Wir versuchen, diese Veranstaltung eigentlich sehr interaktiv zu gestalten.
Ihr könnt auch Feedback geben zu ihren Aufgaben.
Das macht ihr wahrscheinlich sowieso in den Übungen meistens,
wenn ihr sagt, die Aufgabe finde ich blöd.
Die Studierenden, die Betreuer in den Übungen,
nehmen das auch auf und werden entsprechend auch das wieder an uns weitergeben.
Das heißt, es ist eigentlich nicht so,
dass wir irgendwas ignorieren, was nicht gut läuft.
Dann gibt es eine ganze Menge Kolleginnen und Kollegen im Arbeitsbereich Softwaretechnik,
also da, wo ich arbeite,
wirklich auch über die Jahre ziemlich viele Leute, die das mitgemacht haben.
Ihr seht auch immer in den Methoden, in den Klassenköpfen
die Namen von sehr vielen Personen, die da dran mitgewirkt haben.
Vorne dranstehen momentan Christian Spä, den kennt ihr üblicherweise über die organisatorischen Fragen.
Aber er hat auch inhaltlich sehr viel beigetragen.
Und auch Fred Winkler hat die letzten Jahre extrem viel beigetragen.
Er hat extrem viele gute Ideen gehabt,
die Aufgaben zu verbessern, noch griffiger zu machen,
noch eingängiger für euch und auch für uns.
Genau, und dann ist es natürlich auch so,
Heinz Zürich Hofler, habt ihr bisher nicht kennengelernt.
Das ist halt auch eine wichtige Person,
denn der hat mit ermöglicht,
dass wir hier diese Veranstaltung so machen können
Und das ist mir einfach mal wichtig, dass ich das mal sage.
Das ist eben so, man kann eben nicht darauf hoffen,
dass irgendwie andere Leute einem das irgendwie alles gut machen.
Man muss da schon sehr viel Kommunikation investieren. Und ich würde sagen,
SE1 ist so eines dieser Beispiele, wo man sehen kann,
dass viel Kommunikation auch viel Verbesserung bewirken kann.
Okay, das nur so vorneweg. Wir sind jetzt
in Woche 8, das heißt laut dieser netten Übersicht,
hier haben wir die ersten sieben Wochen hinter uns.
Das heißt, wir haben zwei Stufen von SE1 hinter uns.
Auf der ersten Stufe ging es erstmal nur um simple Klassen,
simple Objekte von diesen Klassen. Exemplare dieser Klassen,
die dann in ihrem Zustand eigentlich üblicherweise nur aus primitiven Typen bestanden haben.
Auf der zweiten Stufe haben wir uns dann Referenzen angesehen.
Das ist ein ziemlich großer Schritt gewesen.
Ich habe jetzt in den Übungen das mehrfach erlebt,
dass Leute da wirklich sehr ahnungslos waren, was Referenzen anging.
Ziemlich unglücklich an dieser Stufe dieser Veranstaltung.
Oft sind es Leute, die ein bisschen später eingestiegen sind.
Das ist dann auch okay, wenn man ein bisschen hinten dran hängt.
Aber das Konzept von Referenzen ist immer wieder eine Hürde.
Und insbesondere wenn man spät einsteigt und denkt,
man kann mal eben schnell alles aufholen,
ist das nicht
so leicht
Das ist die Grundidee, dass einfach Objekte sich gegenseitig benutzen können.
Und in Java geht es halt nur über Referenzen. Das heißt,
wenn man Referenzen nicht versteht, kann man eigentlich auch nicht Java programmieren.
Und auch das, was heute diskutiert wird,
ist ohne das Verständnis von Referenzen kaum nachvollziehbar.
Ja, jetzt geht es also hier auf der nächsten Stufe,
da lichten sich jetzt die Nebel.
Wir haben jetzt zwei Wochen nur für Stufe 3 mit drei großen Themen,
einmal Interfaces, einmal testen, automatisiertes Testen.
Und das dritte Thema sind Sammlungen in der nächsten Woche.
Das ist Nicht ganz ohne. Auch in dem Sinne,
was wir jetzt machen, das ist für viele neu.
Also die Leute, die schon vorher programmieren konnten,
die konnten schon schleifen, programmieren, die konnten schon Bedingungen formulieren,
bulsche Ausdrücke, alles kein Problem.
Das gilt für viele, die ich vorher programmieren konnten,
ist das einfach gewesen. Jetzt kommen wir an den Punkt,
wo es ein bisschen schwieriger wird.
Wo wir tatsächlich neue Dinge einführen, die auch für viele,
die jetzt vielleicht in einer anderen Sprache programmieren gelernt haben,
noch nicht so klar sind.
Und außerdem machen wir jetzt in dieser Veranstaltung hier seit Jahren
etwas anders, was ihr so in keinem Lehrbuch findet.
Also das ist wieder mal so eines dieser Beispiele,
wo wir mit SE1 einfach Ich würde mal sagen, ziemlich weit vorne sind.
Wir führen nämlich Interfaces sehr früh ein.
Vor dem ganzen Vererbungsgehubere. Wenn ihr ein klassisches Java-Lehrbuch aufschlagt,
dann kommt auf Seite, was weiß ich, 120 kommt Vererbung.
Auf Seite 130 kommen abstrakte Klassen. Und auf Seite 140
kommen dann irgendwann Interfaces als Spezialfall von abstrakten Klassen, bla bla bla.
Das machen wir ganz anders. Wir führen jetzt hier heute
Interfaces ein. Ihr habt noch kein Wort von Vererbung gehört.
Und das funktioniert. Es funktioniert seit Jahren. Wir haben
das ja schon, wie gesagt, öfter gemacht hier.
Und das ist was Außergewöhnliches
Das ist sozusagen ein, kann man sagen,
ein Bonus dieser Veranstaltung. Man kann auch sagen, oh,
das ist blöd, ich kann das nirgends gut nachlesen.
Ihr seid also auf das Material angewiesen, was wir euch hier geben.
Das solltet ihr sowieso nutzen. Ich habe auch auf den Übungen das Gefühl,
dass nicht viele von euch vielleicht nochmal in dieses Skript reinschauen,
bevor sie in eine Übung gehen,
weil so manche Begriffe, die wir da so abfragen,
stehen ja im Skript und auf dem Aufgabenblatt drauf.
Also da solltet ihr auch nochmal so ein bisschen,
was ich momentan noch nicht versprechen kann,
ob wir den dritten Teil des Skriptes drucken dürfen.
Ich habe noch keine Zusage bekommen,
momentan eben nur als PDF verfügbar, tut mir leid.
Okay, dann habe ich aber nochmal was vorweg zu sagen,
auch nochmal, bevor ich jetzt inhaltlich werde.
Wir stehen jetzt am Anfang von Stufe
Und wir haben ja ein Zusatzangebot gemacht,
diesen Online-Selbsttest zu Stufe 1. Und eine zweite weiterer Tests zu Stufe 2 wird folgen,
auch zu 3 und 4 werden wir Tests anbieten,
aber nur circa 10 Prozent von euch haben dieses Angebot bisher wahrgenommen.
Wir haben irgendwie 480 Teilnehmer und wir haben irgendwie ungefähr 40,
50 Leute, die das mal gemacht haben,
die sich da überhaupt nur angemeldet haben bei diesem OLAT-Test.
Woran liegt das? Weil wir glauben schon,
dass es sehr schlau ist, diesen Test begleitend zu machen,
also möglichst früh diesen Test einfach mal durchzuführen,
eine halbe Stunde höchstens,
das ist ja nur eine maximale Zeit,
die er da braucht
Einfach die Fragen mal durchgehen und feststellen,
ob ihr Probleme hättet, die zu beantworten.
Denn wenn ihr Probleme habt, kriegt ihr sehr früh
das Feedback, dass ihr ihn möglicherweise nicht genug tut.
Vielleicht wollt ihr es gar nicht wissen,
dass ihr nicht genug tut, ich weiß es nicht.
Es kann auch daran liegen, dass ihr O-Lad nicht mögt,
dass man sich da irgendwie anmelden muss mit seiner Steam-Erkennung.
Ich weiß es nicht. Wir wissen es einfach nicht.
Und deswegen habe ich jetzt gedacht,
wir fragen mal wieder das Orakel von Storesi,
machen mal wieder eine kleine Umfrage.
Hier eine Abstimmung über das Thema Selbsttests
Ich starte hier schon mal die Umfrage. Ich zeige,
nee, erstmal zeige ich nochmal eben den Barcode für Leute,
die das nochmal eben abscannen müssen.
Auch für die zu Hause an den Bildschirmen.
Ich habe jetzt gehört, es gibt Leute, die gucken,
die Veranstaltungen zu Hause auf dem Sofa, auf dem Flatscreen.
Guten Appetit mit den Chips. Die können jetzt auch hier nochmal
diesen Barcode scannen möglicherweise und dann auch teilnehmen an dieser Umfrage.
Gut, und jetzt, ich starte das hier schon mal
und dann erkläre ich nochmal, welche Optionen es sind.
Also, wir haben hier vier Gruppierungen. Ich habe das versucht,
so ein bisschen zu formatieren, dass es ein bisschen Chep von der,
verrutschen hier die Buchstaben so ein bisschen,
aber Es gibt die Kategorie unter euch,
die haben den Test bereits vollständig durchgeführt.
Dann gibt es die Kategorie, die haben den Test mal angefangen,
aber nicht zu Ende gemacht. Dann gibt es die,
die haben den Test noch gar nicht gemacht.
Und es gibt die, die überhaupt kein Interesse daran haben.
Das sind so die vier großen Kategorien.
Und innerhalb dieser Kategorien könnt ihr euch jetzt, hoffentlich,
wenn ihr wisst, in welcher Kategorie ihr seid, mal einordnen.
Also entweder habt ihr es vollständig durchgeführt und es hat euch
gutes Feedback gegeben. Kann sein. Kann auch sein,
dass ihr ihn vollständig gemacht habt, hat euch gar nichts gebracht.
Auch wertvolles Feedback. Ihr habt den Test mal angefangen,
aber keine Zeit gehabt. Oder habt ihr ihn angefangen und
das war doof. Olat war doof,
die Formatierung war doof, der Browser war doof,
was weiß ich was
Da ist eine Frage. Bitte? Also das hier,
das B gehört hierzu und das C und das Degotiers und das E
gehört dazu. Das ist immer so ein bisschen.
Vier Antwortmöglichkeiten verfügbar. HBSD. In Storesie jetzt aktiv.
Aha, das ist ja ein interessanter Effekt.
Ich stoppe nochmal eben die Umfrage. Ja, ja,
ja, ich würde alles löschen.
Ja,
ja, ja
Das ist ja interessant. Ach, wisst ihr,
kein Problem. Wir gucken einfach mal eben hier rein.
Ich lade mal eben den Fragesatz. Das geht ja alles
. Dann seht ihr mal, wie das Ding funktioniert.
Hier ist doch toll. So, keine richtige Antwort.
Hier sind doch genügend Fragen drin. A bis H,
alles da gespeichert. Speichert das nochmal. Oh, es
könnte daran liegen. Oh, jetzt habe ich es.
Ich weiß, woran es liegt. Das ist ganz einfach
. Ich muss ins Netz gehen. Ja, tut mir
leid. Ich mache auch mal
einen Fehler
Ja, Dropbox Ordner, sehr schön, ja.
Ja, ja, ja, ja, ja, ja.
Dann beende ich das jetzt hier nochmal eben.
Einmal das Storesi und starte es nochmal.
Das könnt ihr es erklären. So, jetzt Storesi.
Wolf rauft sich wahrscheinlich die Haare zu Hause.
Eine Abstimmung bitte. Vorlesung 8. Laden. Zack. Jetzt
müsste das eigentlich sichtbar sein. Wenn ihr jetzt neu ladet,
dann müssten wir jetzt da auch die entsprechenden Optionen stehen.
Danke für das Feedback. Ich starte hier das nochmal und dann
könnt ihr jetzt, jetzt kommen auch viel mehr Stimmen rein.
Vorher waren es nur die Clicker, weil der Clicker ist hier natürlich in Hardware angeschlossen.
Das heißt also, Ja, jetzt kommt da doch was zusammen.
Sehr schön. Ich verschwende hier Zeit. Ja, also 160 Stürmen,
das war letztes Mal, war so die Grenze bei 150.
Ich glaube, das kann ich dann auch schon wieder beenden,
weil ich habe ja ausführlich erklärt, welche Optionen es hier gibt.
Ich beende das mal und dann blenden wir mal ein,
was jetzt hier ein Ergebnissen rausgekommen ist.
Aha, okay. Ja, das ist ja mal eine Harde,
eine ganz klare Aussage. Die meisten Antworten kommen zu,
ich habe den Test noch nicht gemacht,
aber ich will ihn noch machen
Das ist beruhigend. Also wenn ihr jetzt gesagt hättet,
das interessiert uns überhaupt nicht, dann,
also unsere Empfehlung von Timo und von mir macht den Test möglichst früh,
weil demnächst kommt der nächste Test und je früher ihr merkt,
was ihr nicht wisst, desto besser.
Aber das ist ja sehr erfreulich hier. Jetzt funktioniert ja auch Storesi wieder,
ich muss ja nur im Netz sein, ist ja klar.
Gut, sehr schön. Also, jetzt will ich inhaltlich
werden. Es geht jetzt im ersten Teil um Interfaces.
Das heißt, wir werden uns Klassen ansehen und Tüten und Interfaces.
Das sind so drei Begriffe, die jetzt nochmal sehr intensiv angesprochen werden.
Also, wir haben gesehen, das ist eine Klasse,
ein Typ definiert, das haben wir schon ein paar Mal besprochen.
Die öffentlichen Methoden einer Klasse bilden die Operationen des Typs und
die Exemplare der Klasse bilden die Wertemenge.
Und eine Klasse hat eigentlich zwei zentrale Eigenschaften,
sie definiert einen Typ, also sie bietet irgendetwas an,
einen Umgang und sie liegt die Implementation dieses Typs über ihre Methoden und Felder fest.
Ein Interface, das gucken wir uns heute an,
in Java definiert ausschließlich ein Typ und legt gar keine Implementation fest.
Und der Zusammenhang, der ist ganz interessant.
Im Kern von allem. Ich könnte sehr,
ich habe schon mal vielleicht gewitzelt,
dass ich diese Folie am liebsten jedes Mal auflegen würde,
in jeder Vorlesung, weil sie so zentral ist.
Das ist einfach immer das Grundverhältnis bei der Objektorientierung.
Ein Klient nimmt von einem Dienstleister, Dienstleistungen in Anspruch.
Ich habe zufällig ein Bluejay-Projekt dabei,
um das hier noch ein bisschen zu illustrieren.
Das kennt ihr hoffentlich von Blatt 5. Wir haben eine Klasse
Konto, diese Klasse Konto sieht relativ harmlos aus.
Ihr kennt auch das schon
relativ gut
Man hat drei Methoden einzahlen, auszahlen und gib Saldo,
also zwei verändernde Methoden und eine sondierende Methode,
um hier mit Exemplaren von so einem Konto umgehen zu können.
Also ein Klient kann das benutzen. Also das ist
schon mal ein Beispiel für Klient und Dienstleister.
Das Konto bietet Dienstleistungen an in Form von Methoden,
die kann man aufrufen. Dann haben wir hier einen Überweiser.
Denn Überweise habt ihr auch schon gesehen, nämlich auf Platz 5.
Da solltet ihr von einem Konto zu einem anderen etwas überweisen.
Die Schnittstelle sieht so aus. Er hat hier eine Methode überweisen mit zwei Parametern,
Konto, Quellkonto, Kontozilkonto und einen Betrag,
der von dem Quellkonto auf das Zielkonto überwiesen werden sollen.
Einen Aufruf dieser Methodebeweis, den angegebenen Betrag vom Quellkonto auf das Zielkonto.
Sehr schön. Das ist genau dieser Zusammenhang hier.
Ich kann diese Schnittstelle des Überweisers, kann ich jetzt benutzen.
Ich kann also mir jetzt hier zwei Konten erstellen,
also hier Konto, Konto 1, ja,
und dann mache ich mir noch ein Konto, Konto 2,
dann habe ich zwei Konten.
Beide haben erstmal einen Saldo von null und ich zahle auf
dem einen jetzt mal was ein,
also irgendwie hier so
Euronen. Und dann sage ich jetzt dem Überweiser, hey,
du, mach doch mal überweis doch mal von dem einen
Konto auf das andere Konto, irgendeinen Betrag zum Beispiel 30 Euro.
Und wir sehen hier die Objektinspektoren, das heißt,
es müsste irgendwann 70, 30 da gleich stehen,
wenn das geklappt hat.
Und siehe da, so ist es auch, 70, 30.
Ich kann auch jetzt hier natürlich sondierend rausfinden, wie viel ist denn da?
Dann sagt er hier 30 und der andere sagt,
70. Gut. Was ich jetzt hier gemacht habe,
ich bin aufgetreten als Klient,
als interaktiver Klient in Blue Jay,
von dem Dienstleister Überweiser
Das heißt, ich habe hier diese Dienstleistung in Anspruch genommen,
so wie an einem Geldautomaten oder ähnlichem, an so einem überweisenden Bankabautomaten.
Hier mach mal eine Überweisung von da nach da.
Geht ja an solchen Automaten. Diese Dienstleistung habe ich
interaktiv in Antwort genommen, der Dienstleister, der Überweiser,
hat dazu dann weitere Klienten, weitere Klassenobjekte benutzt.
Also in diesem Fall hier zum Beispiel zwei Konten.
Das heißt, der Dienstleister ist selber wieder Klient bei weiteren Dienstleistern.
Ganz elementarer Zusammenhang. Okay,
was wir uns jetzt ein bisschen ansehen wollen,
ist ein Konzept, das in der Objektorientierung sehr zentral ist,
nämlich die Idee der Kapselong.
Das heißt, Erstmal technisch, programmiersprachentechnisch heißt das erstmal nur,
ich kann Dinge kapseln, verstecken.
Ich habe das gesehen, ihr könnt Methoden private machen in Java.
Ihr könnt Felder, also Zustandsfelder, könnt ihr privat deckeln.
Sollt ihr sogar privat deklarieren. Die sind also versteckt,
die sind gekapselt. Das ist ganz hübsch. Also wenn
man jetzt zum Beispiel sich hier mal diesen Überweiser ansieht,
dann ist das hier die Sicht auf die Schnittstelle.
Ich sehe also nur, was er so an Dienstleistungen mir anbietet.
Ich kann aber auch tatsächlich hineinsehen und dann noch sehen,
wie es denn konkret macht. Ich kann also jetzt hier
den Feldtext mal angucken und sehe dann, wie das hier durchgeführt wird.
Und dann sieht man hier auch den ganzen Kommentar nochmal ein bisschen ausführlicher.
Hier steht also relativ deutlich drin, wie die Parameter beschaffen sein müssen.
Also hier sagt er zum Beispiel, das Konto,
von dem der Betrag abgehoben wird, muss ungleich nall sein.
Also der sagt hier ganz deutlich, lieber Klient,
ich will hier ein vernünftiges Konto haben, eine gültige Referenz auf dem Konto.
Auch beim Zielkonto soll das so sein, der Betrag,
der überwiesen werden soll. Dieser Betrag ist vom Typ Int,
hier deklariert Int, aber hier steht im Kommentar,
der Betrag muss größer Null sein.
Also auch da sagt der Dienstleister,
wenn das und das erfüllt ist,
dann mache ich hier
auch meine Aufgabe
Hier sehe ich also jetzt zum einen die Schnittstelle,
die habe ich auch in der Schnittstellensicht gesehen,
aber hier sehe ich jetzt auch,
wie er das implementiert, wie er es denn macht.
Das will ich vielleicht manchmal gar nicht wissen. Manchmal will ich
gar nicht wissen, wie ein Dienstleister seine Dienstleistung erbringt.
Wenn ich beim Pizza-Service eine Pizza bestelle, möchte ich besser
nicht wissen, wie die Pizza zustande gekommen ist.
Ganz häufig möchte man das nicht so genau wissen.
Also ich übertreibe ein bisschen, aber ihr wisst,
was ich meine. Das ist hier so ähnlich. Also
eigentlich möchte man das als Blackbox betrachten, so ein Dienstleister.
Bring mir die Dienstleistung und ich will gar nicht
so genau wissen,
Das Ausblenden von Details vereinfacht die Benutzung. Also ich muss
mir über viele Dinge keine Gedanken machen als Klient.
Und was ein Vorteil ist, der Dienstleister kann seine Dienstleistung auch seiner Umsetzung ändern.
Er kann die Zutaten ändern, die werden dann billiger.
Und naja, das Pizzabeispiel wollen wir nicht strapazieren.
Was man dann einfach sieht, ist schon allein hier in Java,
habt ihr Bluejek das gerade gesehen,
ich kann ja im Editor umschalten zwischen der Schnittstellensicht und
der Implementationssicht der Quelltextsicht
Es ist einfach ganz wichtig, sich immer klarzumachen,
so eine Klasse hat ja so eine Doppelrolle,
so ein bisschen wie Jackaland Hyde.
Auf der einen Seite gibt es die Sicht für einen Klienten,
der guckt auf so eine Klasse drauf,
welche Operationen kriege ich an einen Artrufen, welche Dienstleistungen werden mir angeboten.
Welche Typen haben die Parameter, was sagt die Dokumentation,
diese Kommentare, was sagen die, über die Benutzung?
Das ist die Außensicht, also ich als Client von draußen sehe irgendwie,
was sind die öffentlichen Eigenschaften, was sind die Dienstleistungen.
Das nennt man auch die Schnittstelle, haben wir auch schon
drüber gesprochen. Und dann gibt es die andere Sicht,
wie ist das Ding denn implementiert?
Das ist eine ganz andere Blick,
ein ganz anderer Blick auf dieselbe Sache
Und da guckt man dann tatsächlich in die Methoden Rümpfe rein.
Da guckt man rein, naja, wie ist denn das hier konkret gebaut?
Welche Exemplar war, Jan und Felder definiert die Klasse?
In diesem Fall hier der Dienstleister, Überweise, hat gar
keinen Zustand selber. Gut, kann man auch sehen dann
. Welche privaten Hilfsmethoden stehen in der Klasse zur Verfügung?
Also diese ganzen Fragen, das ist dann eher die Innensicht.
Und es gibt einfach bei so einer Klasse immer wirklich einmal die Sicht,
ich möchte das Ding benutzen und einmal geht es die Sicht,
ich muss da irgendwas dran ändern, ich muss das programmieren,
ich muss es erweitern, was auch immer.
Und das sind einfach zwei verschiedene Sichten. Und die kann
man ganz gut trennen. Das haben wir schon gesagt,
man kann das begrifflich trennen, Schnittstelle, Implementation oder in Bluejoy
im Editor heißt es dann, Wie heißt das hier?
Dokumentation und Quelltext. Die Dokumentation ist die Schnittstelle und der Quelltext
ist die Implementation. Und die Unterscheidung kennt ihr auch von
den Strings. Letzte Woche habt ihr die String-API gesehen.
Das heißt also, hier die Java-API ist hier benannt.
Nächste Woche gucken wir uns noch ein bisschen ausführlicher an.
Da werden wir ein bisschen mehr darüber lernen, wie diese
API beschaffen ist. Aber es geht erst mal darum,
dass ihr auch da gesehen habt,
ihr habt die Schnittstelle von Strings euch ansehen müssen,
um sie vernünftig benutzen zu können.
Ihr habt das vielleicht mitbekommen. Substring kann man mit einem oder mit
zwei Parametern benutzen. Viele haben in der Übung geglaubt,
der zweite Parameter von Substring würde die Länge Substrings angeben.
Ist aber nicht richtig. Der zweite Parameter gibt die Endposition
an, bis zu der der Substring ausgeschnitten werden soll.
Dafür muss man nachlesen, das steht in der Dokumentation drin.
Das heißt also, diese Außensicht auf einen Dienstleister,
auf eine Klasse, die ist sehr, sehr wichtig.
Wenn man aber erstmal jetzt diesen Schritt gemacht hat, gesagt,
ja gut, ich kann tatsächlich ja so eine Schnittstelle und Implementation trennen,
dann kann man sich auch überlegen, hm,
diese Dienstleistung, die diese Klasse mir bringt,
die kann ich auch auf verschiedene Art und Weise implementieren.
Man muss nicht immer alles genauso machen, wie es teilweise passiert.
Also das Beispiel, was wir gerade gesehen haben hier,
klasse Konto, bietet an ihrer Schnittstelle Operation ein,
Zahlen, Auszahlen, Gibsaldo.
Die ist tatsächlich auch so gebaut hier, diese Klasse.
Ich zeige sie ganz schnell. Man sieht hier,
es gibt also eine Exemplarvariable Saldo, das kennt ihr.
Es gibt einen Konstrukt, der den Saldo auf Null initialisiert.
Dann gibt es ein Einzahlen, damit nur geprüft auf der Betrag
größer Null ist. Es wird also nicht irgendwie geprüft,
ob das irgendwie auch gedeckt ist, das konnte beim Auszahlen.
Es wird nur geguckt, auch der Betrag größer Null ist jeweils und der Saldo wird rausgegeben.
Also eine ganz einfache Klasse, klar, so kann man
diese Dienstleistungen implementieren. Aber wenn wir uns jetzt überlegen,
abstrakt jetzt für so einen Klienten,
der will ein Konto haben, der will wissen,
was ist der Kontostand und der will was eins haben,
was auszahlen.
Das muss nicht
so gebaut sein
Es muss nicht so gebaut sein. Man kann es auch
anders bauen. Ich kann tatsächlich mir folgende Lösung ausdenken,
gleiche Schnittstelle, Einzahlen, Auszahlen gibt Saldo.
Und intern merkt sich diese Klasse, sämtliche Ein- und Auszahlung,
führt so eine Art Protokoll über die Ein- und Auszahlung und
der Saldo wird gar nicht gespeichert. Und jedes Mal,
wenn jemand gib Saldo aufruft, dann wird der Saldo berechnet,
in dem einfach alle alle bis dahin getätigten Transaktionen aufaddiert werden.
Von dem Anfangssalto des Kontos aus. Das kann man so
machen, in einigen buchhalterischen Kontexten passiert das sogar so.
Das ist also jetzt nicht aus der Welt gegriffen.
Ich habe so ein Projekt jetzt hier mal mitgebracht,
wo das ein bisschen angedeutet ist. Man sieht hier kontosimpel.
Das ist sozusagen auf der Folie hier die simple Implementation.
Also kontosimpel ist genau, wie in diesem Beispiel hier,
das ist erstmal nur diese Klasse kontosimpel. Mit einem Saldo,
eigentlich genau die gleiche Klasse, die ihr gerade gesehen habt.
Gut. Und jetzt habe ich die Klasse Konto anders.
Die sieht ein bisschen anders aus. Die hat tatsächlich,
und das werden wir uns nächste Woche ansehen,
hier eine Liste von Intejust, gut,
also eine Liste von mehr oder weniger ganzen Zahlen.
Das sind diese Buchungen. Eine Liste kennt ihr noch nicht,
aber ihr könnt euch vorstellen,
was eine Liste ist,
eine Liste von Buchungen
Und dann wird hier im Konstrukt diese Liste angelegt,
die ist dann erstmal leer. Beim Einzahlen wird einfach in diese Liste
dieser Betrag, der hier übergeben wird, einfach hinzugefügt.
Genauso beim Auszahlen, da wird nur sozusagen der negative Betrag
hier hinzugefügt. Ja, und wenn man Gibsaldo aufruft,
dann wird hier folgendes gemacht, damit der Saldo erstmal auf
Null und dann wird irgendwie durch diese ganzen Buchungen durchgelaufen.
Das ist eine erweiterte Vorschleife, gucken wir uns auch nächste
Woche an. Alle Buchungen werden einfach aufaddiert. So,
und am Ende kann man den Saldo dann berechnet rausgeben.
Das heißt, der Saldo ist nicht immer da im Zustandsfeld,
sondern er wird bei Bedarf, wenn jemand ihn abfragen will,
Kann man so machen. Ist auf alle Fälle denkbar. Das heißt,
wir sehen hier zwei verschiedene Kontroimplementationen, die dieselbe Dienstleistung erbringen.
Und das ist hier dargestellt über ein sogenanntes Interface.
Das heißt, wir haben hier ein Interface-Konto. Das hat,
wie ihr seht, hier, sieht ein bisschen anders aus.
Also mal gibt es hier Schlüsselwort Konto. Und dann gibt
es hier so Methodenköpfe. Also statt eines Rumpfes steht dann
da am Ende ein Semikolon. Und davon haben wir hier
drei Stück, nämlich ein Zahlen, Auszahlen gibt Salo.
Das, was ihr schon 10.000 Mal gesehen habt.
Wunderbar. Drei Operationen, die hier jetzt stehen in
diesem Interface-Konto
Was ganz lustig ist, ist, ich kann jetzt einen Klienten hernehmen.
Ich kann jetzt also tatsächlich aus dem anderen Projekt,
ich kann hier sagen, im Bluedrain kann man sagen,
klasse aus einer Datei hinzufügen.
Ich nehme jetzt genau dieses Konto-Einfachbeispiel, also dieses andere Projekt,
da nehme ich jetzt hier die Klasse überweiser.
Die füge ich jetzt hier hinzu und die benutzt das Konto.
Vorher hat die die Klasse Konto benutzt, jetzt benutzt sie das Interface-Konto.
Die heißen zufällig gleich. So, mal sehen,
ob sie das übersetzen lässt, lässt sich übersetzen.
Ich kann also jetzt tatsächlich Überweiser sieht ja immer noch genauso
aus, wie ihr ihn kennt.
Er kriegt also hier etwas vom Typ Konto überwiesen, übergeben.
Zwei Parameter vom Typ Konto. Und nimmt von dem einen
was runter und zahlt es aufs andere ein. So,
kann also jetzt hier wieder mir ein Konto simpel erzeugen.
Dann zahle ich hier mal irgendwie mir meine 100 Euro wieder ein.
Okay, mache das Fenster ein bisschen kleiner.
Jetzt schiebt den Objektinspektor herüber. Dann erzeuge ich mir hier
ein Konto anders. Juhu. Da kann ich auch fragen,
gib mir mal den Saldo, da sagt er null,
ist ja noch nichts passiert.
Da einen Doppelklick drauf machen, dann sieht man hier sowas
Das ist eine Referenz. Hier haben wir natürlich jetzt keinen Saldo drin,
sondern wir haben ja nur eine Referenz auf diese Liste der Buchung.
Diese Liste ist momentan leer. Es ist einfach eine Referenz hier. So,
und jetzt kann ich einfach wieder meinen Überweiser anschmeißen und sagen,
hey, über Weiser, überweis doch mal bitte was von Kontosimpel auf Konto anders.
Und das funktioniert genauso. Jetzt kann ich hier wieder meine
30 Euro überweisen. Und dann wird tatsächlich hier bei Kontosimpel
das auf 70 stehen. Und wenn ich jetzt bei Konto
anders frage, wie viel hast du denn?
Dann sagt er mir,
er hat 30.
Das funktioniert alles
Zwei verschiedene Implementationen, völlig verschiedene Implementationen, aber genau der gleiche Umgang.
Der Überweiser benutzt die Schnittstelle eines Kontos. Und das ist
hier als Interface formuliert. Und das ist etwas,
was hier jetzt neu kennenlernt, das ist das Konzept eines Interfaces.
Das ist ein Sprachkonstrukt, das Java anbietet,
mit dem man ausschließlich eine Schnittstelle definieren kann.
Also nicht eine Implementation, sondern nur eine Schnittstelle.
Und das nennen wir hier auch benannte Schnittstelle,
aber wir werden im Folgenden hier die Dinger immer Interfaces nennen.
Also ihr wisst, was die Schnittstelle einer Klasse ist,
das sind genau die Methoden, die öffentlich sind,
die bilden die Schnittstelle
Und ein Interface ist tatsächlich der Sprachmechanismus in Java.
Wir können das im Deutschen ganz gut unterscheiden. Wir können einfach sagen,
das Ding aus Java heißt Interface und das allgemeine Konzept einer Schnittstelle
, nennen wir einfach Schnittstelle.
Die Engländer müssen da so ein bisschen jonglieren.
Das ist beides Interface, ganz schön doof. Gut,
und für das Kontobeispiel haben wir halt genau diese benannte Schnittstelle,
Konto einzahlen, auszahlen Gipsalo. Das ist genau das,
was man auch hier in der Klasse sieht,
in diesem Interface jetzt hier.
Hier einzahlen, auszahlen gibt es alle. Und mit den
Kommentaren dazu, da kann man also durchaus beschreiben,
was denn
diese Dinge tun sollen
Ja, und das ist schon eigentlich alles, was man
so verstehen muss. Man gibt einfach Methodenköpfe an und definiert
damit Operationen eines neuen Typs. Ihr habt ja gesehen,
im Überweiser konnte ich ja wirklich dieses Interface als Typ verwenden.
Gut, so ein paar zentrale Eigenschaften von Interfaces.
Interfaces sind Sammlungen von Methodenköpfen. Alle Methoden sind implizit Public
. Ihr könnt nicht irgendwas private machen. Es gibt keine
Idee einer Implementation, etwas, was man verstecken kann.
Interfaces sind sehr öffentlich. Keine Methodenrunde, keine Implementation,
keine Felder, nicht instanzierbar.
Ich kann auch kein Exemplar erzeugen von dem Interface-Konto.
Und sie werden von Klassen implementiert
Das haben wir uns noch gar nicht so genau angeguckt,
aber Interfaces werden durch Klassen implementiert.
Also irgendjemand muss ja dann mal ein echtes Exemplar,
ein echtes Exemplar anbieten, dass diese Schnittstelle auch wirklich ausfüllt.
Und zum Beispiel hier, klasse, konto simpel, Implements, Konto.
Damit deklariert eine Klasse, dass sie das tut.
Da sieht man auch hier, also konto simpel,
da steht irgendwo ganz oben Implementskonto.
Das ist dieser Hinweis, ich implementiere diese Schnittstelle dieses Interface.
Genauso bei Konto anders, da steht das genauso drin.
Und jetzt ist das das Coole, dass ich an allen Stellen,
an denen ein Objekt mit einem bestimmten Interface erwartet wird,
eine Referenz auf ein Exemplar einer implementierenden Klasse übergeben werden will.
Das haben wir gerade gesehen. Der Überweiser erwartet etwas vom Konto und ich konnte
ein Exemplar einer Klasse übergeben, die dieses Interface implementiert.
In dem Fall sogar von zwei verschiedenen Klassen.
Das Coole ist auch, wir haben gesehen,
die Auswirkungen auf den Klienten sind eigentlich bei der Benutzung völlig unverändert.
Wir konnten diese Klasse einfach reinkopieren,
diesen Überweiser und der hatte hier irgendwie Konten gekriegt,
die Nutzung ist komplett unverändert
Quelle auszahlen, Ziel einzahlen, geht alles prima.
Nur in dem Moment, wo ich natürlich echte Exemplare erzeugen will,
da muss ich wirklich dann Klassen benennen.
Ich kann immer nur Exemplare von Klassen erzeugen in Java.
Also muss ich dann wirklich kurz sagen,
entweder kontosimpel oder konto anders oder was auch immer.
Aber für den Klienten ist das komplett transparent gewesen.
Er hat das überhaupt nicht richtig gemerkt hier. Er sieht das
ja auch. Der Benutzungsfall geht hier nur auf dieses Interface,
der kennt eigentlich kontosimple und konto anders gar nicht.
Könnte jetzt noch drei weitere Varianten mir ausdenken,
wie ich ein Kontointerface implementiere
Okay, was hier passiert ist, dass die Dinge voneinander entkoppelt werden.
Das ist etwas, was in der Softwareentwicklung extrem hilfreich ist,
dass man Dinge unabhängiger voneinander bekommt. Dass tatsächlich dieser Überweiser
nicht wissen muss, welche Arten von Konten es gibt.
In irgendeiner Bank kann ich jetzt irgendwie sagen,
alle möglichen Konten schwirren da rum,
solange die ein bestimmtes Interface implementieren können,
bestimmte Teile der Software einfach mit diesen Konten was machen.
Diese Entkopplung geht sogar so weit. Ich könnte theoretisch jetzt
dieses Projekt hier speichern, die Klassenkonto simpel und Konto anders,
einfach rausschmeißen das würde sie trotzdem noch übersetzen lassen,
weil der Überweiser ja nur Konto benutzt.
Dann kann ich zwar nichts mehr tun,
weil ich dann keine Konten mehr habe,
aber der Überweiser kann trotzdem schon programmiert werden.
Das ist ziemlich erstaunlich. Ich kann also ein Klienten programmieren,
bevor überhaupt irgendwas da ist, was benutzbar ist.
Extrem nützlich, um Schnittstellen zwischen verschiedenen Teams,
die gemeinsam programmieren, abzusprechen. Kann man wirklich sagen,
hier, wir bieten die Schnittstelle an und ihr könnt da
schon mal darauf hinprogrammieren.
Wir implementieren das dann später.
Und was wir jetzt hier gerade gesehen haben, ist,
ich habe bei einem konkreten Konto gesagt,
das ist denn eigentlich der Umgang, was ist denn die Schnittstelle?
Naja, so und so sieht das Interface aus.
Habe ich also aus einer konkreten Klasse ein Interface abgeleitet.
So kann man es machen, geht aber auch umgekehrt.
Ich kann einfach sagen, ich schreibe zuerst ein Interface,
das heißt, ich lege zuerst fest, was ich haben will.
Ich sage, die und die und die Methoden soll das Interface anbieten,
dann kann ein Client die auch schon benutzen.
Und dann muss hinterher irgendjemand das Ding implementieren.
Und dann wird so ein Interface zu einer Spezifikation,
dann ist das Ding sowas wie eine Sollbeschreibung
Folgende Operationen sollen angeboten werden. Mit den und den Eigenschaften
. Und das ist ein weiterer, ziemlich zentraler Begriff in
der Softwareentwicklung. Das ist der, der einer Spezifikation.
Dass man wirklich beschreibt, was eine bestimmte Einheit leisten soll.
Ohne festzulegen, wie sie das macht.
Wenn man zu viel in einer Spezifikation darüber schreibt,
wie etwas gemacht wird, nennt man das auch gerne Überspezifikation.
Wenn jemand eine Spezifikation schreibt und sagt, ja,
also das Ding soll die Dienstleistung eines Kontos anbieten und
das wird in einer Exemplarvariablen-Saldo gespeichert
Sollte man nicht in eine Spezifikation reinschreiben. In einer Spezifikation sollte drinstehen,
beim Konto kann ich einzahlen, auszahlen, den Saldo abfragen.
Mehr muss ich nicht sagen. Und dann kann irgendjemand anders
sich überlegen, wie man das implementiert.
Für dieses einfache Beispiel ist es jetzt ein bisschen konstruiert,
aber das ist generell für Spezifikationen ganz zentral.
Sie sollten sagen, was passiert, aber auf keinen Fall,
wie es im Detail funktioniert.
Da gibt es verschiedene Formen von solchen Spezifikationen.
Also der Spektrum geht so von natürlich sprachlichen Spezifikationen,
wo einfach dann so wie hier in diesem Beispiel,
wo einfach Text dasteht und gesagt wird,
hier, man kann da was einzahlen,
man kann da
was auszahlen
Bis hin zu formalen Spezifikationen. Also es gibt da in
der Informatik ganz viele Forscher, die sich damit beschäftigen,
wie kann ich denn solche Spezifikationen möglichst formal kriegen,
mathematisch exakt formulieren, damit wirklich eindeutig klar ist,
was denn die Semantik ist, was da passieren soll.
Ich bin der Meinung, eine Spezifikation sollte Freiheitsgrade lassen,
sonst ist es keine Spezifikation mehr. In dem Moment,
wo ich in der Spezifikation alles ein eindeutig beschreibe,
spezifiziere ich nicht mehr, sondern ich implementiere,
dann programmiere ich schon, weil dann alles komplett festgelegt ist.
Aber da kann man drum streiten. Was man sieht ist,
dass wir auch sowas haben wie semi-formale Spezifikationen. Also zum Beispiel hier unser Überweiser,
wenn wir uns das jetzt mal angucken, hier nochmal die Schnittstelle,
dann sieht man hier beim Überweisen diese ganzen Hinweise,
also erstmal steht hier Konto, Konto, Int.
Das ist schon mal stark formalisiert. Also man kann da
jetzt nicht eine Banane in Apfel übergeben,
sondern man muss da wirklich zwei Konten übergeben.
Also schon ziemlich klar eingeschränkt und festgelegt. Und dann ist aber
hier natürlich nur informell festgelegt, was denn die Semantik ist.
Also was passiert denn da? Also hier überweist von Zielkonto
zwei Konten aufs Zielkonto, muss ungleich nall sein.
Also das sind so Dinge, die kann man dann hier nur so hinschreiben,
weil die Programmiersprache mir nicht die Möglichkeit gibt,
zu erzwingen, dass etwas ungleich nall übergeben wird.
Es gibt Sprachen, die können das, Java kann das nicht.
In Java können solche Referenzvariablen auch den Werten nall enthalten.
So, das heißt, ich muss es als Kommentar schreiben,
was so eine moderne Form von Armwedeln ist.
Gut, dann habe ich schon gesagt,
ich will diesen Zusammenhang herstellen zwischen Klassen und Interfaces,
im Sinne von, dass beides Typen sind.
Also wir haben schon gesagt, eine Klasse definiert in Java
einen Typ. Schnittstelle, Operationen und die Menge ihrer Exemplare
, das ist die Wertemenge
Bei einem Interface kann man das eigentlich so ähnlich sehen.
Ich kann bei einem Interface auch sagen, naja,
es hat eine Schnittstelle, ganz offensichtlich, dafür ist es da.
Und die Menge der Exemplare, aller Klassen, die das Interface implementieren,
die bilden quasi die Wertemenge von einem Interface.
Das heißt also, alle konnten,
anders konnten oder simpel konnten oder wie auch immer,
alle diese Konten, die es so ruppen gibt in der Welt,
die implementieren, gehören also zur Wertemenge dieses Typs zum Beispiel hier in dem Beispiel Konto.
Das heißt, für einen Typ im objektorientierten Sinne ist vor
allen Dingen wichtig, welche Objekte gehören zur Wertemenge des Typs?
Vor allem,
welche Operationen sind zulässig
Das ist nicht wichtig, wie die Operation implementiert sind.
Damit kommen wir dann so ein bisschen hier wieder den Typbegriff,
den erweitern wir jetzt ein bisschen. Ihr erinnert euch,
dass wir den guten Herrn Horror vor ein paar Wochen zitiert haben,
was denn eigentlich ein Typ ist in einer Programmiersprache.
Da haben wir gesagt, naja, der Typ definiert eine Menge an Werten,
die eine Variable oder einen Ausdruck annehmen kann.
Jeder gehört genau zu einem Typ oder zu genau einem Typ.
Typinformation ist statisch aus dem Quelltext ermittelbar und ein Typ definitiv
zulässigen Operationen. Das haben wir jetzt auch schon gesehen,
wenn ich eine Variable habe, vom Typ Konto,
dann ist klar, ich kann da nur ein Zahlen auszahlen,
gib Saldo drauf aufrufen und nicht irgendwie gib Namen
oder gib Anzahl der Buchungen oder sowas
Dieser erweiterte Objekt und der Typbegriff geht jetzt dahin,
dass er sagt, ein Typ definiertes Verhalten von Objekten
durch eine Schnittstelle, ohne die Implementation festzulegen.
Das ist das, was wir in der Objektorientierung eigentlich wollen.
Wir wollen möglichst exakt beschreiben, was getan wird,
aber nicht, wie es getan wird. Das heißt,
was wir dann haben in der Objektorientierung, insbesondere mit Java,
ein Objekt ist immer genau von einer Klasse,
da gibt es einfach keine Alternativen dazu.
Aber da eine Klasse auch mehrere Interfaces erfüllen kann,
kann ein Objekt zu mir als einem Typ gehören.
Aber da muss man noch gar nicht mehrere Interfaces haben,
also es ist in Java so, Objekt kann tatsächlich mehrere,
also eine Klasse kann mehrere Interfaces implementieren.
Wir haben jetzt hier nur den Fall,
dass beide Klassen hier nur ein Interface implementieren.
Aber wenn kontosimpel eine Klasse ist, dann ist kontosimpel auch ein Typ.
Ist immer so, müsst ihr euch merken.
Eine Klasse hat einen Typ, ganz eindeutig. Das heißt,
ein Exemplar von Kontosimpel, hat den Typ kontosimpel,
hat aber auch den Typ Konto.
Also mindestens schon mal zwei verschiedene Typen. Das heißt also
ein Exemplar gehört nicht genau zu einem Typ,
im klassischen Typ Sinne,
sondern in der Objektorientierung ist es durchaus so,
dass ein Objekt zu mehreren Typen gehören kann
Und dann, wenn man erst mal den Schritt gemacht hat,
jetzt wird es ein bisschen, sagen wir, technisch,
aber ich glaube, dass es eigentlich ganz gut nachvollziehbar ist.
Wir müssen jetzt unterscheiden bei einer Avarayablen,
bei einer Referenzvariablenz zwischen dem statischen und dem dynamischen Typ der Variablen.
Das ist eigentlich ganz einfach. Statisch und dynamisch habt ihr
schon kennengelernt inzwischen als Begriffspaar bei uns hier, alles,
was zur Übersetzungszeit auf dem Quelltext schauend klar ist,
das ist statisch und alles, was erst zur Laufzeit,
wenn das Programm läuft, irgendwie klar ist,
das ist dynamisch.
So, der statische Typ einer Variablen
Wird durch ihren deklarierten Typ definiert. Also wenn ich hier
dieses wunderbare Beispiel habe, dann habe ich hier jetzt,
wenn ich sage, Konto K, sage ich erstmal,
das ist jetzt mal null.
So, jetzt habe ich hier mein Konto K,
meine Variable K vom Typ Konto.
Dann ist diese Variable vom statischen Typ Konto,
sehe ich, so habe ich sie deklarierten.
Das steht so im Quelltext. Und der legt natürlich
die Operationen fest, die aufrufbar sind.
Ich kann jetzt also zum Beispiel auf dieser Variablen einzahlen aufrufen.
Da wird der Compiler auch nicht meckern. Wenn ich K,
Punkt, ändere Namen aufrufe, wird der Compiler meckern.
Er wird einfach statisch schon feststellen, es gibt keinen
änderen Namen auf einem Konto.
Aber ein Zahlen gibt es
Das sind die Dinge, die man statisch feststellen kann.
Der dynamische Typ, und jetzt kommt genau dieser Zusammenhang her,
der dynamische Typ hängt davon ab,
auf was für ein Objekt die Referenzvariable zur Laufzeit verweist.
Und deswegen muss man wissen, was eine Referenz ist,
weil sonst kann man das nicht verstehen.
Wenn ich jetzt sage, K ist gleich Konto-Sim1,
das kann ich hier einfach so hinschreiben. So,
jetzt habe ich hier tatsächlich K, ist eine Objektreferenz,
also K hat jetzt eine Objektreferenz zugewiesen bekommen,
auf Kontosimpeln
Jetzt kann ich aber auch schreiben, K ist gleich Konto, Ant 1.
Das sind diese beiden Exemplare, die hier liegen.
Die sind ja hier auch benannt, deswegen kann ich dir einfach zureisen.
So, jetzt kann ich mir nochmal K anzeigen lassen,
die jetzt sagte, es ist Konto anders.
Ich kann das ja mal ein bisschen größer machen,
habe das nicht so gequetscht ist. Was ich bisher getan habe,
ich habe es einfach eine variable deklariert vom Typ Konto, statischer Typ Konto.
Und hier hat sie jetzt einmal auf ein Exemplar von Kontosimpel gezeigt,
das heißt der dynamische Typ dieser Variablen ist zu diesem Zeitpunkt
kontosimpel gewesen und zu einem anderen Zeitpunkt anders.
Das ist komplett erst zur Laufzeit entscheidbar, worauf denn das Ding wirklich zeigt.
Und es kann zur Laufzeit auch geändert werden.
Also dynamisch in zweierlei Hinsicht. Erst zur Laufzeit überhaupt ermittelt
werden und dann kann sich auch ändern. Ich kann erst
eine Referenz auf das eine Objekt zuweisen und dann Referenz auf ein anderes Objekt.
Es bleibt ein Konto. Das konkrete Exemplar, also die Dinger,
die hier dahinter klemmen, hier hinter diesen Referenzen,
die bleiben immer entweder Konto anders oder kontosimpel.
Das ändert sich nicht. Aber die Referenz, mit der
man darauf verweist, die können ihren dynamischen Typ ändern.
Das nennen wir den dynamischen Typ einer Variablen
Das Interessante ist jetzt, von dem dynamischen Typ hängt ja ab,
welche Methode ausgeführt wird. Wenn ich jetzt auf K hier
aufrufe, K.1 zahlen, einzahlen, 30, 30.
So, das kann ich jetzt aufrufen. Dann wird auf
Konto anders, werden jetzt irgendwie 30 Euro eingezahlt.
Jetzt mache ich die Zuweisung wieder, irgendwie K ist gleich kontosimpel,
die Referenzvariable ändert dynamisch, ihren dynamischen Typ.
Jetzt kann ich das Gleiche nochmal aufrufen,
einzahlen 30 und es wird auf ein anderes Konto
was eingezahlt
So. Aber es ist nicht nur so, dass auch verschiedene Objekte
was eingetauft wird, sondern es werden verschiedene Methoden aufgerufen.
Bei dem einen wird jetzt irgendwie der Saldo nur erhöht,
bei dem anderen wird irgendwie diese Liste von Buchung irgendwas eingetragen.
Zwei völlig verschiedene Methoden. Aber es ist dieselbe Operation.
Der Typkonto sagt, es gibt eine Operation einzahlen und welche
konkrete Methode dahinter steht, das entscheidet dann der dynamische Typ.
Und das ist dieser ganz fundamentale Zusammenhang. Deswegen sind wir
jetzt an dieser Stelle in der Veranstaltung an dem Punkt,
wo wir zwischen Methoden und Operationen unterscheiden
Methoden sind immer das, was in einer Klasse steht.
Methoden haben immer einen Rumpf. Das kennt ihr aus den
bisherigen Beispielen immer. In einer Klasse stehen Methoden,
die haben einen Rumpf, da stehen irgendwelche Anweisungen drin.
Eine Operation ist ein bisschen was Abstrakteres. Eine Operation gehört
zu einem Typ. Mein Typ ist ja auch ein bisschen
was Abstrakteres. Und da ist es zum Beispiel so,
der primitive Typ in, hat so Operationen wie addieren und multiplizieren.
Und der Referenztyp Konto zum Beispiel hat Operationen wie Einzahlen,
Auszahlen, Gipsaldo. Und welche konkrete Methode dann wirklich aufgerufen
wird, hängt vom dynamischen Typ ab.
Das heißt, für eine dieselbe Operation kann es
verschiedene Methoden geben
Ja. Und damit diese Zusammenhänge benennbar sind,
machen wir halt diese sehr genaue Unterscheidung zwischen Operation und Methode.
Eine Operation, mehrere Methoden. Okay, dann machen wir
das jetzt noch ein bisschen rund. Dann erzähle ich noch so
ein paar andere kleine Details dazu, die ganz lustig sind.
Ich kann nämlich tatsächlich eine Referenzvariablen nach ihrem dynamischen Typ fragen.
Das heißt, ich habe jetzt hier gerade zuletzt
der Variablen K vom Typ Konto, statischer Typ Konto,
dieses simple Konto zugewiesen.
Ich kann jetzt fragen, hey K,
bist du eine Instanz von Konto simpel
Ja. Instance Off ist ein Operator in Java mit zwei Operanten,
nämlich eine Referenzvariablen oder im Ausdruck von einem Referenztyp und anderem Klassennamen.
Bist du ein Exemplar der Klasse Kontosimpel? Dann sagt er,
jo, bin ich. Kann auch fragen, bist du
ein Exemplar der Klasse-Konto anders? Nö, bin ich nicht.
Ich kann tatsächlich also eine Referenzvariabl nach ihrem dynamischen Typ fragen.
Das ist schon mal ganz nützlich. Was noch ganz schön ist,
wenn ich hier mal ganz kurz noch was zeigen darf,
hier haben wir diese drei Operationen in diesem
In diesem Interface, einzahlen, auszahlen, gib Saldo.
Und Konto anders ist ja diese Sache mit der Liste.
Und die haben hier irgendwie sich was ganz Schlaues außer,
die haben außerdem noch eine weitere Methode, gib Anzahl der Buchungen.
Weil der zählt ja die Buchung, ist ja ganz einfach.
Und da kann man einfach sagen, von einer Sammlung kann man fragen,
wie viele Elemente hast du denn da drin?
Das heißt, also man kann die Frage sehr leicht
beantwortbar machen, wie viele Buchungen schon stattgefunden haben.
Diese Methode kann ich nicht aufrufen. Solange ich das
über K mache. Wenn ich jetzt also wieder sage,
K, die weiß ich jetzt mal, Konto anders zu.
Also ich wieder die Referenz variable K kriegt jetzt eine Referenz
auf das andere Konto
Jetzt kann ich hier ja fragen,
bist du ein Exemplar für ein Konto anders?
Und er sagt, ja, bin ich jetzt?
Ich verweise direkt auf so ein Konto anders.
Aber ich kann jetzt nicht aufrufen, K, Punkt,
wie heißt die Operation, was habe ich gerade gesagt?
Sorry, muss man eben einmal nochmal nachgucken.
Gib Anzahl Buchungen, da kann ich gleich mal kopieren.
K, Punkt, Gib Anzahl Buchungen, kann ich hier
nicht aufrufen. Weil K ist vom statischen Typ Konto.
Can out Fine Simple,
Method gib Anzahl Buchungen
Jetzt kommt der nächste Trick ins Spiel.
Ich kann, eine Typzusicherung machen. Ich kann,
wenn ich weiß, dass eine Referenz auf einen dynamisch,
also einen dynamischen Typ hat, Konto anders,
dann kann ich wieder eine Variable von diesem Typ deklarieren und dann eine Zuweisung machen.
Das heißt, ich kann tatsächlich, was hier auf der Folie steht,
eine Variable vom Typ Konto simpel deklarieren, KI.
Und dann mache ich so eine Typzusicherung. Die sieht sehr ähnlich aus
wie die Typumwandlung für primitive Typen und macht was völlig anderes.
Gerne verwechselt von vielen Leuten. Ich kann also tatsächlich jetzt
hier sagen,
Konto anders
K.A. ist gleich und jetzt schreibe ich hier Konto anders von K.
So, ich deklariere eine neue Variable, K vom Typkonto
anders und weise ihr jetzt, die Referenzzukunft von Konto.
Das ginge jetzt nicht ohne diese Typzusicherung. Ich weiß aber,
dass es tatsächlich so ist und deswegen kann ich jetzt,
und jetzt kann ich aufrufen,
K.A. gib Anzahl von Buchungen und dann sagt er mir irgendwie,
wie viele Buchungen da jetzt schon stattgefunden haben.
K.A. kann das, weil der Typ Konto anders hat
diese Schnittstelle, diese Operation. Ich kann da tatsächlich mir
die Schnittstelle anzeigen lassen und dann hat der tatsächlich vier Operationen.
Gib Anzahl Buchungen ist ja hier mit drin
Okay. Das einfach noch, dieser Zusammenhang, Typ Zusicherung,
dann einfach noch mal ganz kurz UML, wie ist die Darstellung?
Interfaces werden üblicherweise mit so einem Stereotype markiert. Man schreibt also
so mit diesen doppelten, größer kleiner Zeichen hier Interface dran.
Keine Attribute üblicherweise, ja. Also deswegen sind hier sozusagen zwei Striche.
Und dann gibt es hier die Methoden und eine Klasse kann deklarieren,
dass sie das realisiert, dass sie sozusagen das implementiert.
Das ist so die Notation,
wie sie in der UML vorgesehen ist,
wie sie auch in Blue Jay ja dann in dem Diagramm auch zu sehen ist.
Also eine konkrete Klasse implementierende Interface,
so eine gestrichelte Linie mit einem weißen Dreieck an der Spitze
So, und das war das,
was wir jetzt erstmal uns zu Interfaces angehört haben.
Also Trennung von Shitsteller-Implementation, zentrales Konzept in der Softwaretechnik.
Wenn ich diese Trennung machen kann, dann kann ich mir
auch vorstellen, eine bestimmte Schnittstelle unterschiedlich zu implementieren.
Interfaces in Java erlauben mir so eine Schnittstelle zu benennen,
Interfaces können noch als Spezifikation eingesetzt werden und nur im Umgang mit Interfaces
muss man plötzlich unterscheiden zwischen dem statischen und dem dynamischen Typ einer Referenzvariable.
Gut, und da wir hier gerade, gibt es erstmal Fragen jetzt dazu.
Meistens seid ihr an dieser Stelle Zuhörer mal sind etwas gesandtstrahlt.
Weil das ziemlich viel neues Zeug ist. Und es ist
insbesondere etwas, was die meisten noch nicht gehört haben.
Es ist jetzt keine konkrete Frage da.
Dann habe ich nochmal eine kleine Verständnisfrage. Storesi,
was wir jetzt gerade so erzählt haben, mal sehen,
ob das jetzt auch gut hängen geblieben ist.
Folgende Situation. Welche der folgenden Aussagen gilt für diesen Quelltext,
wenn er von einem Java-Compiler als korrekt akzeptiert wird?
Also ich habe eine Methode M,
die bekommt einen Parameter vom Typ Konto und dann wird hier irgendwie
K1-Zahlen aufgerufen und dann wird irgendwie Karl Gibsaldo aufgerufen und so.
So, dann sind hier ein paar Aussagen,
da sollte ich jetzt einfach mal sagen,
welche dieser Aussagen ist richtig
Also drei, die nicht richtig sind und eine ist richtig.
Ich starte erst mal hier die Umfrage, dann könnt ihr da jetzt
erst mal das in Ruhe lesen. Also erste Antwort,
der Typ Konto muss drei Operationen definieren, einzahlen, auszahlen,
gib Saldo, B, der dynamische Typ des Format Parmet,
das ist hier nicht ersichtlich, C,
Konto kann kein Interface sein, da die Operation einzahlen,
gerufen werden kann und D-Konto muss eine Klasse sein,
der Aufruf von M, ein Objekt als Parameter übergeben wird.
So, und dann machen wir jetzt fünf Minuten Pause und
ihr könnt in Ruhe drüber nachdenken.
Ich mache den Freeze wieder aus,
dann sieht man das inzwischen doch schon über 150 Stimmen
da sind,
ja,
sehr schön
Beteiligen sich immer noch Personen an dieser Umfrage.
Das ist großartig. Ich werde irgendwann noch mal fragen,
wer nicht im Hörsaal ist, aber das machen wir heute jetzt nicht.
So, ich beende das jetzt. Dann gucken wir erst
mal auf die Verteilung, was so die Meinung war.
Ja, 56 Prozent, also die deutliche Mehrheit sagt,
der dynamische Typ des formalen Parameters ist hier nicht ersichtlich.
Okay, dann sagen einige doch fast ein Viertel oder über ein Viertel
Konto muss eine Klasse sein,
da beim Aufruf von M ein Objekt als Parameter übergeben wird
Einige Leute sagen auch, Konto kann kein Interface sein,
da die Operation einzahlen gerufen werden kann und nur ganz wenige sagen,
das Konto drei Operationen definieren muss, einzahlen, auszahlen und Gibsaldo.
Also, wenn man sich die richtige Antwort ist,
natürlich das hier, also erfreulich,
dass die überwiegende Zahl von euch das richtig eingeschätzt hat.
Ich zeige nochmal die eigentliche Frage. Hier sieht man,
dass irgendeine Referenzvariable übergeben wird vom Typ Konto.
Und wir haben eben das Beispiel gesehen,
das kann natürlich auch ein Interface sein.
Wir haben das live eben mehrfach gesehen,
dass da tatsächlich auch einfach
das ein Interface sein könnte
Und das Einzige, was hier gefordert wird in diesem Quelltext,
ist ein Zahlen und Gibsaldo. Also Auszahlen steht hier nichts davon.
Also kann man schon mal nicht sagen, dass das muss.
Das ist wieder, wenn man zu viel Kontext mit einbezieht.
Der dynamische Typ des Formatplanes ist ja tatsächlich nicht ersichtlich.
Ich weiß ja nicht, was der Klient übergeben hat als
konkretes Objekt. Das könnte ein Konto anders sein,
das könnte kontosimpel sein oder irgendein anderes Konto.
Und Konto kann natürlich ein Interface sein, also es ist zehn
nicht richtig und ein Konto muss auch keine Klasse sein.
Es kann ein Interface sein. Also es sind sozusagen
die beiden, schließen sich sogar eigentlich aus.
Gut, dann Haben wir das auch geklärt?
Sehr schön. Gab es da eine Frage dazu?
Ja? Ja. Was kann auch Programmiersparung, das ist
ja auch ein Jahr, hat man ja völlig vertört.
Das braucht man in der Praxis, die da Predigt steht.
Die Frage, die ihr stellt, ist die folgende?
Braucht man in der Praxis überhaupt Interfaces, wenn es sowas gibt wie Vererbung?
Und die Antwort gebe ich spätestens in SE2,
weil wir da ausführlich auf Vererbung eingehen werden.
Die Frage stellt sich heute andersrum. Die Frage stellt sich
heute so rum, braucht man überhaupt noch Vererbung,
wenn es Interfaces gibt
Also die Bedeutung von Schnittstellen ist deutlich höher einzuschätzen, als die Bedeutung der Vererbung.
Die Vererbung ist eines der stark überschätzten Konzepte der Objektorientierung.
Es ist lange Zeit viel zu sehr gehypt worden.
Also insbesondere Implementationsvererbung ist etwas, was ziemlich viele Probleme
mit sich bringt und nicht immer wirklich auf Vorteile bringt.
Aber das ist ein Thema, das werde ich ausführlich in SE2 behandeln,
weil Vererbung ist einfach ein relativ komplexes Thema,
noch ein häufig unterschätztes Thema.
Und deswegen würde ich genau das andersrum beantworten. Also tatsächlich mal
ein Interface, das würde ich nicht darauf verzichten wollen.
Es gibt letztendlich jetzt neue Sprachansätze, die sagen,
Interface und sowas
braucht man
Implementationsvererbung wahrscheinlich nicht. Es gibt Sprachen,
die lassen es schon wieder weg. Spannende Sache,
also können wir sicherlich auch nochmal privat nochmal drüber diskutieren.
Gut, jetzt wollen wir and now to something completely different.
Einführendes systematische Testen. So, jetzt haben wir noch ungefähr
eine halbe Stunde Zeit. Es ist nichts completely different,
sondern es geht jetzt tatsächlich letztendlich um das Gleiche.
Wir werden jetzt tatsächlich eigentlich das gleiche Thema nochmal etwas
anders beleuchten.
Zufällig habe ich hier
ein Bluejay-Projekt
Und da sehen wir einen Überweiser. Ihr habt diesen Überweiser in Aufgabenblatt 5
programmieren müssen. Und wenn wir uns hier die Implementation angucken,
dann sehen wir, wie das hier gemacht ist.
Also der kriegt hier diese beiden Konten, dann prüft er erstmal,
ob die auch nicht Nall sind und ob der Betrag auch richtig ist.
Also es ist wirklich auch größer, null ist,
weil sonst sagt er hier Throw You Illable, Illable Argument Acception.
Dass man eben hier meine Dropbox etwas beruhigen, weil Sonst
sich da mal irgendwelche Meldungen reinkriege. Also hier wird eine Überprüfung gemacht,
wenn eins von beiden Nalles oder wenn der Betrag keiner gleichen Nalle null ist,
dann wird hier eine Acception geworfen,
wenn das Salto das nicht hergibt von dem einen Konto,
dann sagt er, Überziehen ist nicht möglich.
Und erst dann, wenn alles das geklärt ist,
macht hier Quellkonto auszahlen und Zielkonto einzahlen.
So, sehr schön. Wenn man sich das so anguckt,
sieht das eigentlich ziemlich gut aus.
Was ich hoffe ist, dass wenn ihr so etwas programmiert,
dass ihr das auch testet
Was wir hier schon gemacht haben, ist tatsächlich,
ich habe hier ja einfach mal so ein paar Aufrufe gemacht.
Das ist ja die elementarste Form des Testes, den ihr
machen könnt. Ihr benutzt einfach das Ding und seht,
ob es das macht, was es tun soll.
Was ihr üblicherweise tut, ist, ihr manipuliert irgendwie,
ihr stellt zwei Konten,
lasst von dem einen auf das andere was überweisen und guckt nach,
ob dann die Saldi, ob die Salden stimmen.
Nichts Wanderes macht man, wenn man systematisch testet.
BlueJ unterstützt automatisierte Tests. Ich kann also tatsächlich hier in
den Einstellungen jetzt sagen,
unter diverses Testwerkzeuge sollen angezeigt werden
Also jetzt hier, Testwerkzeuge anzeigen lassen, dann hat man
hier so ein paar mehr Elemente in der Oberfläche.
Und ich kann jetzt außerdem zum Beispiel mir für diese Klasse hier eine Testklasse erzeugen lassen.
Ja, ein neuer Menüeintrag hier, Testklasse erzeugen. Juhu,
das rufe ich jetzt auf und ich bin glücklich,
weil ich jetzt eine Testklasse habe.
Das ist so ein bisschen integriert hier in Blueget,
das heißt, wenn ich jetzt diese klassische bewege,
dann wandert die Testklasse auch mit, die klebt da so ein bisschen dran.
Die gehört also sehr eng dazu, denn diese Klasse soll
diese Klasse testen.
Was Blue Jay erzeugt, ist ein Gerüst, ein Testgerüst für J. Unit.
Wir werden uns J. Unit gleich noch ein bisschen genauer ansehen.
Das sieht dann irgendwie so aus, dass hier so eine
Reihe von Importanweisungen stehen, die man erstmal nicht verstehen muss.
Und dann steht hier alles mögliche Zeugtrend.
Das schmeiße ich mal alles weg. Eigentlich reicht es schon,
wenn man sich vorstellt, dass das hier, ah,
ich mache das mal hier so wieder ein bisschen anschmeißt,
das alles raus, was da an diesem Template drinsteht,
kann das jetzt, glaube ich, übersetzen.
Ja, kann ich übersetzen. Das ist schon mal eine minimale Testklasse.
Ich habe jetzt auch ganz zufällig Hier eine Testklasse mal dabei.
Diese Testklasse ist jetzt hier, funktioniert nach J. Unit 4.
Also das heißt, es ist eine bestimmte Version von diesem J. Unit.
So, wie sieht das jetzt hier aus? Ich habe
jetzt hier in der Klasse Überweiser-Test, habe ich eine Methode,
die habe ich hier mal Tests überweisen genannt.
Und was macht die? Naja,
die erzeugt sich im Konto simpel, dann zahlt sie was ein,
dann erzeugt sie noch ein Konto simpel,
dann erzeugt sie sich ein Überweiser,
dann wird was überwiesen
Von dem einen Konto auf das andere Konto.
Und dann, und jetzt wird es spannend,
dann wird geprüft, ob hinterher auch alles passt.
Das heißt, hier wird jetzt irgendwas aufrufen,
was sich Athird Equels nennt, mit irgendeiner Fehlermeldung.
Es wird nämlich dann geprüft, ob K. Gibsaldo den gleichen Wert hat wie 70.
Also es wird sozusagen gesagt, wenn ich kam,
wenn ich von K1 30 abhebe, dann sollte es,
weil ich vorher 100 eingezahlt habe, sollten noch 70 drauf sein.
Also K. Gibsaldo wird aufgerufen und es wird in Beziehung gesetzt zu
einem erwarteten Wert. Also das, was man bekommt,
zu dem beziehungsweise jetzt, was man erwartet.
Und wenn das nicht zusammenpasst, also wenn das nicht gleich
ist, dann wird diese Fehlermeldung geworfen
Das ist Teil so, wie man in Jay Unit Testfälle schreibt.
Das ist ein Testfall, eine Methode, die einen bestimmten Fall abtestet.
Was man auch prüfen sollte, ist natürlich,
ob dann beim Zielkonto das auch angekommen ist,
sollte sowohl beides getestet werden, dass es weg ist bei dem einen und da ist beim anderen.
Das sind also zwei Überprüfungen, die hier gemacht werden.
Und was in Jay Unit 4 gemacht wird,
dann muss man halt so eine Annotation ranschreiben.
Syntax braucht ja nicht so genau verstehen.
Ihr werdet wahrscheinlich sowieso in SR1 keine Testklassen selbst schreiben.
Wir werden ganz viele Tests
euch immer vorgeben
Aber nur, dass es schon mal seht, also so
eine Testmethode wird annotiert mit so einer komischen Annotation hier.
Und dann kann tatsächlich hier auch das ausgeführt werden.
Wenn ich das jetzt übersetze, dann geht das wunderbar.
Und jetzt ist das hier auch wieder eine richtig angeklebte Testklasse.
Und jetzt kann ich tatsächlich diesen Test ausführen. Kann jetzt
sein, fangen alle hier alles testen. Ich kann auch
nur diese eine Testmethode jetzt auf und ich habe nur eine Testmethode,
also kann ich die einzelnen, ist das das Gleiche,
wie alles testen.
Ich rufe dich jetzt mal auf. Und dann sagt er
hier unten, Teste überweisen war erfolgreich.
Toll. Ich kann das jetzt nochmal so machen,
dass ich hier alles testen aufrufe
Und dann kommt hier so ein Fenster. Das ist etwas
, was Blue Jay anbietet. Das ist die Ausgaben von
J. Unit anzeigt. Und was man hier sieht,
ist, hier kommt ein grüner Balken. Das heißt,
alle Tests, die gemacht wurden, sind nicht fehlgeschlagen.
Also es hat nicht irgendwo Alarm gegeben. Das heißt,
es ist alles so, wie man es erwartet hat.
Ich kann das auch hier starten, weil es könnte ja sein,
dass ich jetzt so ein Projekt habe mit mehreren Klassen und jede Klasse hat eine Testklasse,
also zum Beispiel für Konto anders könnte ich ja auch eine Testklasse haben.
Dann kann ich hier tatsächlich alle Tests starten in dem Projekt.
Auch das kann ich jetzt hier machen,
dann passiert das Gleiche. Das Schöne ist jetzt,
das, was ihr mühsam von Hand gemacht habt,
um zu sehen, ob das Ding wirklich funktioniert,
euer Überweiser
Das macht man jetzt mit einem Klick. Mit einem Klick wird geprüft,
ob das alles passt. Ob das wirklich alles passt,
werden wir gleich sehen, weil wir jetzt sehr aufmerksam war,
hat gesehen, dass die Überweiser-Implementation fehlerhaft war.
Ich vermute, dass das den meisten von euch entgangen ist,
aber wir werden, glaube ich,
mit unglaublich schlauen Mitteln des Testens rausfinden, wo dieser Fehler liegt.
Ich hoffe, dass wir das hinkriegen. Okay,
dann machen wir das Ding mal zu hier,
das brauchen wir nicht mehr. Das habe ich ja nur
dafür benutzt. Das brauchen wir eigentlich auch fast nicht mehr
, können wir eigentlich auch schon zumachen hier.
Ich habe keine weiteren Fragen mehr
So, testen. Warum überhaupt testen? Ja,
also es gibt immer diesen einen spektakulären Fall,
mindestens den einen, diese unbemannte Raumrakete, die Ariane 501,
die circa 40 Sekunden nach dem Staat explodiert ist.
Ich vermute, es waren 42 Sekunden,
aber das ist nur ein Geheimtipp von mir.
Die Ursache für die Explosion war ein Programmierfehler.
Tatsächlich haben die irgendetwas nicht sauber getestet.
Durch ausführlichere Tests der Steuerautomatik hätte dieser Fehler entdeckt werden können.
Da gibt es auch einen Link dazu,
wo das alles ein bisschen beschrieben ist
Das war damals ziemlich teuer. Also wenn so eine Raumradkette in
die Luft geht, das war damals eine Milliarde D-Mark.
Vielleicht kennen einige von euch die D-Mark noch.
Vielleicht kommt sie ja auch bald wieder.
Testen kann sich also auf jeden Fall lohnen.
Man kann eine Menge Geld sparen, wenn man vernünftig testet.
Testen ist eine Maßnahme zur Qualitätssicherung. Das heißt,
was man macht, ist, man stellt fest,
ob eine Software ihren Dienst auch erfüllt.
Das dient üblicherweise zum Aufzeigen von Fehlern.
Ein erfolgreicher Tester ist der, der möglichst viele Fehler findet.
Wer gut ist im Testen, der entdeckt viele Fehler.
Aber man kann natürlich nicht die Korrektheit einer Software nachweisen
Das heißt, was ihr macht, ist geplant und strukturiert, Programmcode ausführen.
Was wir gerade gesagt gemacht haben, hier diesen konkreten Anwendungsfall.
Ich möchte wirklich sehen, ob der Überweiser das richtig macht,
indem ich ihn einfach mal mit einem bestimmten Szenario teste.
Das sollte ganz selbstverständlicher Bestandteil bei der Softwareentwicklung werden.
Ihr solltet immer alles, was ihr programmiert, testet.
Ungetesteter Code ist der größte Schmerz, den man sich vorstellen kann.
Das gehört einfach dazu. Witzig ist aber,
dass in der Informatik das Testen eine Zeit lang ziemlich verpönt
war und dass es mal wieder einem guten Herrn zuzuschreiben,
dem Herrn da extra,
den haben wir ja schon in der Veranstaltung
hier kennengelernt
Er hat diesen berühmten Satz gesagt, Program Testing can at
best show the presence of Aeros but never they app sense.
Ich kann also nur zeigen, dass ein Fehler da ist.
Ich kann aber nie beweisen, dass kein Fehler da ist, mit Testen.
Richtige Aussage, Herr Dextra, genau. Aber damit,
und er wollte eben halt darauf hinweisen, dass es eigentlich viel schöner wäre,
wenn man die Korrektheit von Software beweisen würde.
Er war mathematisch sehr involviert und wollte eben halt die
ganze Informatik eigentlich eher mathematisch aufziehen
Das war relativ verhängnisvoll, weil ganz viele Leute ihn natürlich zu ihm aufgeschaut haben.
Er ist ja wirklich ein ganz großer der Informatik gewesen.
Und damit hat er aber eigentlich die Informatik so ein bisschen ausgebremst,
weil das ganze Genre testen ist sehr in Verruf geraten.
Also wer getestet hat, war eigentlich meinte, oh,
der traut sich nicht an die Korrektheit,
an die Verifikation von Software, der testet nur.
Das waren sozusagen die Handwerker der Informatik, die getestet hatten.
Gab aber auch schon sehr prominente Gegenstimmen, also zum Beispiel der Herr Knus,
ein mindestens genauso wichtiger Informatiker wie der Herr Deichstra,
der hat damals schon geschrieben wie Ware of Bugs in the ABBA,
in the ABBA code, I have only proved it correct,
not try it.
Also ich habe nur bewiesen, dass er korrekt ist,
aber auch habe ich es nicht.
Also das ist dem geschuldet, dass zu der Zeit,
als diese Programmverifikation ganz groß diskutiert wird oder eine Informatik,
Programmbeweise erschienen sind, die waren fehlerhaft.
Toll. Das nützt mir dann einen Programmbeweis,
dass etwas korrekt ist, wenn der Beweis falsch ist.
Also Menschen machen Fehler und auch Beweise sind irgendwie immer schwierig.
Klar, wir wissen, in der Mathematik kann man
auch Beweise natürlich hinterfragen und so weiter.
Ich will nicht das beweisen in Frage stellen.
Ich will nur deutlich machen, das ist so eine Diskussion
gewesen und lange Zeit war das Testen verrufen.
Kleiner Ausflug, Korrektheit von Software.
Das ist auch mal so ein Lieblingsthema von mir
Korrektheit ist ein ziemlich klar definierter Begriff in der Informatik.
Eine Software ist korrekt in Relation zu ihrer Spezifikation.
Den Begriff der Spitzwerke haben wir gerade festgestellt,
irgendeine Beschreibung, wie etwas sein soll. So, und
eine Softwareeinheit ist korrekt, wenn sie ihre Spezifikation erfüllt.
Okay. Der formale Nachweis, dass das so ist, ist ziemlich schwierig.
Also es ist eines der großen ungelösten Probleme der Informatik.
Nur dann, wenn die Spezifikation extrem formal ist in
einem Kalkül formuliert ist oder ähnliches,
dann kann ich möglicherweise die Korrektheit
auch formal nachweisen
Kann ich zeigen, dass eine Software tatsächlich diese Spezifikation erfüllt.
Das ist sehr, sehr selten. Da gibt es nur
sehr wenige Gebiete in der Informatik, wo das geht.
Aber es gibt sie natürlich. Aber meistens ist es so,
dass die Spezifikationen, die wir so haben, natürlich sprachlich sind.
Und da kann ich mich noch so bemühen, da kann ich
keinen formalen Beweis führen, dass es die Spezifikation erfüllt.
Nicht formal. Da braucht man einen gesunden Menschenverstand. Und
da haben wir ein bisschen wenig von manchmal in der Informatik.
Okay, das heißt, das ist ein ganz schwieriges Thema.
Beweisbarkeit von Software und wir bleiben einfach jetzt mal so ein bisschen geerdet und sagen,
naja, in der Praxis der Softwareentwicklung gehört testen einfach dazu.
Das gehört einfach dazu. Ist Handwerkszeug und man kann damit
das Vertrauen erhöhen, wenn ich also vernünftig meine Testfälle wähle,
kann ich schon ziemlich viel überprüfen bei einer Software.
Und Nützlichkeit einer Software wisst ihr auch, gerade interaktive Software,
die zeigt sich sowieso erst im Gebrauch.
Erst dann kann ich das aussagen, ob das wirklich funktioniert,
weil Korrektheit bei interaktiver Software ist ein relativ schwieriges Thema.
Aber testen hat einige Tücken. Testen ist schwierig. Das
werdet ihr auch hier feststellen. Wir haben das über die Jahre festgestellt,
wir haben inzwischen beschlossen,
das Thema Testen runterzufahren in SE1. Wir führen sie hier ein jetzt,
ich jetzt hier und werden auch euch Testklassen immer wieder vorgeben,
aber ihr werdet kaum noch gefordert sein in SE1,
Testklassen selbst zu schreiben.
Schade, schade, aber es ist einfach sehr schwierig und
in SE1 schien uns das dann doch so ein bisschen zu früh zu sein.
Aber es ist auch insbesondere bei grafischen, oberflächensprüchlich,
also so ein Test, wie wir ihn gerade geschrieben haben, einfach.
Wenn ich jetzt der Programmierer von BlueJ bin und jetzt zum Beispiel hier sowas habe wie neue Klasse,
dann wird so ein Dialog aufgemacht und dann kann ich hier einen Klassennamen eingeben und dann wird ja hier eine neue Klasse erscheinen.
Dann möchte ich natürlich, wenn ich BlueJ automatisiert testen wollte,
würde ich dann jetzt wissen wollen,
ob ich jetzt hier auf OK klicke,
wirklich da eine neue Klasse erscheint, in dem Diagramm,
mit dem richtigen Namen und ob ich die dann auch manipulieren kann und so weiter.
Das ist extrem schwierig zu testen. Also so grafische Oberflächen,
dass da wirklich das Richtige passiert, das ist richtig schwierig.
Tests braucht Zeit, also Testen braucht Zeit,
das heißt, in den meisten Projekten ist es so,
wir müssen unseren Code zum Laufen bringen, wir haben keine Zeit zum Testen.
Wir müssen programmieren, wir müssen neue Features bauen und testen
ist, das ist Zeit ist knapp.
Fällt den meisten Leuten dann irgendwann auf die Füße,
wenn man die Zeit in Tests sich frühzeitig investiert,
dann merkt man später, wenn man irgendwas ändert an der Software,
dass man dann irgendwie große Probleme kriegt.
Das ist also nicht etwas, was wir uns hier ausdenken,
dass es einfach live die Erfahrung aus ganz vielen Projekten.
Tests müssen gut vorbereitet sein, man muss einen Testplan haben, Tests müssen wiederholt werden.
Das heißt, man möchte sie eigentlich wiederholbar haben, das,
was wir gerade gesehen haben, war so ein automatisierbarer Test.
Ich kann diesen Test immer wieder ausführen, ich brauche nur da draufklicken,
es wird immer der gleiche Test nochmal ausgeführt.
Das heißt, wenn ich dann die Implementation ändere,
kann ich sehen, ob das noch funktioniert.
Aber auch psychologisch gibt es einige Probleme. Also wenn man Programmierer ist,
dann neigt man dazu, nur das zu testen,
was man selber weiß, dass es funktioniert.
Also diese absurden Randfälle mitzutesten, da muss man schon ein
gewisses Talent für haben. Es gibt so Leute in der Informatik,
die haben das wirklich unheimlich drauf,
so diese ganzen figelinischen Randfälle mit zu bedenken und dann ist es oft besser,
wenn man andere Leute die eigene Software testen lästet, testen lässt.
Dann ist das, wie gesagt, stark beeinflusst zu den Programmiererfahrungen.
Wer viel Programmiererfahrung hat, testet automatisch besser. Das heißt,
wir können nicht erwarten, dass ihr jetzt hier nach den
paar Wochen Programmierausbildung schon gute Tester sein könnt,
aber wir wollen euch zumindest die Mittel an die Hand geben.
Und was auch psychologisch schwierig ist, dass häufig nur positiv
getestet wird.
So,
was heißt das jetzt
Hat erstmal nix mit True & Falls zu tun,
auch häufiges Missverständnis. Positives und negatives Testen.
Ist eine ganz wichtige Unterscheidung bei der Formulierung von Testfällen.
Positives Testen sagt, ich habe einen Testfall, der überprüft,
ob das Ding das hält, was es verspricht.
Das heißt, mal auf unser Beispiel hier bezogen.
Wenn der Überweiser in seiner Schnittstelle sagt,
wenn mir hier zwei gültige Referenzen übergeben werden auf Konten und
der Geldbetrag auch auf dem einen Konto wirklich drauf ist,
dann garantiere ich, dass das hier auch wirklich passiert.
Das heißt, das, was wir gerade geschrieben haben,
war ein solcher Positivtest, das,
was das Ding hier verspricht, hält es das auch.
Also hält es sich in seiner eigenen Spezifikation, wenn das hier die Spezifikation ist.
Das ist ein Positivtest. Ein Negativtest ist hingegen ein Test,
wenn ich jetzt hier an dieser Stelle sagen würde,
der sagt jetzt hier als Kommentar, das Konto,
was übergeben wird, muss ungleich nahe sein.
Jetzt wollen wir doch mal sehen, was passiert denn?
Wenn ich als Klient, jetzt ich ja nur als Klient,
ich gucke da so als Klient,
jetzt übergebe ich mal eine richtige Referenz und eine Nall-Referenz.
Sozusagen das Quellkonto ist nicht Nall und das Zielkonto ist Nall.
Dann wird ja bei dem einen Konto was abgebucht und beim
anderen Konto kann nichts drauf gebucht werden.
Das wäre ja ziemlich blöd, wenn der das wirklich
tun würde
Das ist dann aber ein Negativtest, weil der hier in seiner Spezifikation,
in seiner Dienstebeschreibung sagt, ich funktioniere nur mit zwei gültigen Referenzen.
Wenn ich dann als Klient bewusst was falsch mache,
um zu sehen, ob das Ding auch wirklich sich korrekt verhält,
nicht korrekt, sondern ob es sich robust verhält,
sinnvoll verhält, dann teste ich die Robustheit.
Und das ist das, was hier auf der Folie auch steht,
also erwartete gültige Eingabewerte,
da wo ich wirklich mich an die Spezifikation halte und nur sehen will,
ob das Ding wirklich das macht, was es verspricht, Positivtest.
Wenn ich irgendwie sehen will, das macht er,
wenn ich was Falsches übergebe, geht das dann immer noch,
was geht da schief, da das Konto einfach belastet,
obwohl es gar nicht belastet werden dürfte.
Das sind Negativtests. Positivtests sind deutlich wichtiger. Man muss erst mal sehen,
ob das Ding das tut, was es tun soll.
Diese Robustheit-Tests, die sind halt, sagen wir so,
ein zweiter Gedanke. Also Positivtests erhöhen das Vertrauen in
die Korrektheit, Negativtests, das Vertrauen in die Robustheit.
Dann gibt es die Unterscheidung zwischen statischen und dynamischen Tests.
Das ist wieder statisch dynamisch, ihr kennt die Begriffe, statisch zur Übersetzungszeit.
Ich schaue auf den Quelltext und kann ja sehen,
was da steht. Da gibt es einfach Testverfahren,
wo man einfach nur den Quelltext ansieht.
Reviews heißen die
Man guckt einfach mal so, was da steht und bespricht das.
Oder man lässt es einfach laufen. Man führt die Software
aus, das sind dann dynamische Tests.
Dann gibt es den Begriff der vollständigen Tests,
will ich jetzt nicht zu lange darauf eingehen.
Vollständige Tests sind eigentlich, dann liegen dann vor,
wenn man alle gültigen Eingabewerte testet.
Die werden auch erschöpfende Tests genannt. Wenn ich jetzt mir
hier dieses Beispiel angucke, multipliziere, X und Y,
zwei Parameter vom Typ Int,
Int kann vier Milliarden
verschiedene Werte haben
Bei X und bei Y,
wenn ich da jetzt alle Kombinationen durchprobieren will,
also einen vollständigen Test machen will, programmiere mich einen ziemlichen Wolf.
Das ist wohl kaum sinnvoll. Das heißt,
was man häufig macht beim Testen, man bildet so,
man sagt so, ja, bestimmte Werte sind aus einem ähnlichen Bereich,
da muss ich nur einen Stellvertreter wählen.
Also das würde hier, wie gesagt,
sehr lange dauern, so ein vollständiger Test.
Aber Folie könnt ihr euch in Ruhe angucken. Es gibt
auch Möglichkeiten, wo man tatsächlich was vollständig testen kann.
Das ist aber eher so eine Randinformation.
Also es ist nicht völlig unmöglich, vollständig zu testen.
In manchen Fällen geht das meistens aber nicht.
Meistens ist es nicht sinnvoll,
alles zu testen
Explodiert oft sehr schnell von den verschiedenen Möglichkeiten. Was ganz
wichtig ist, ist diese Idee von Äquivalenzklassen und Grenzwerten.
Das heißt, wenn ich etwas teste, überlege ich mir,
aus welchem Bereich dürfen jetzt die Parameter sein, wenn ich das Ding aufrufe.
Und dann versuche ich wirklich genau diese Grenzen auch auszutesten. So,
und jetzt kommen wir mal wieder auf unser konkretes Beispiel hier,
der Überweiser, da habe ich ja meine Testklasse,
jetzt mache ich mal folgendes, ich kopiere mal diesen Testfall hier,
in diesem Moment, diesen Testfall, den kopiere ich mal,
habe jetzt hier einen zweiten Test
Und da mache ich jetzt mal Teste, überweisen zwei,
nicht die jetzt einfach mal. Die mache ich das genauso
. Aber was dir hier in dem Testfall passiert ist,
der eine hat 100 und der andere hat null.
Und jetzt rufe ich aber auf, gib Saldo,
jetzt rufe ich bei dem einen,
ziehe ich 30 ab und beim anderen Artikel 30 drauf.
Das ist also ein Wert, der mittendrin liegt zwischen 0 und 100.
Ich kann ja nur irgendwie irgendwas größer und null und kleiner
gleich 100 überhaupt überweisen. Jetzt nehme ich mal mal so
ein, was ist denn jetzt ein Grenzwert?
Was ist jetzt ein vernünftiger Grenzwert, den man hier testen sollte?
Wir erwaffen mir mal eine Zahl an den Kopf
Das wäre hier der Höchstwert, den man irgendwie wählen könnte zum Überweisen.
Ja. Eins. Eins und ein Integer-Max-Value,
sagt er. Gut. Da würde ich jetzt sagen,
das wäre dann kein Positivtest mehr,
weil in der Spezifikation von dem Überweiser steht deutlich drin,
der Betrag muss kleiner sein als der Saldo, kleiner gleich dem Saldo.
Ihr könnt das nochmal zeigen, Moment hier,
über Weiser steht der Betrag, der bewiesen werden soll,
muss größer Null sein und kleiner gleich dem Saldo des Quellkontos.
Wenn ich mich daran nicht halte, mache ich
einen Negativtest
Bitte? Ja, das ist richtig, aber den Saldo
habe ich ja selber bestimmt. Hier in dem Test,
in der Testklasse steht ja genau das drin.
Ich habe ja hier gesagt, Konto simpel, dann zahle ich 101.
Das heißt, ich weiß ja hier, als derjenige,
der den Test schreibt, dass 100 drauf sind.
So, worauf ich hinaus will ist,
dieses Konto hat 100 drauf. Der Überweiser sagt,
man darf höchstens so viel von dem einen Konto abheben,
wie drauf ist. Also mache ich doch jetzt nicht mehr
folgendes. Ich versuche mal 100 Euro zu überweisen.
Das ist ja eindeutig ein Grenzfall in dieser Situation hier
Ja, weil ich meine, 70 liegt mittendrin,
aber 100 ist ja das, was hier mal getestet werden sollte.
Dann würde ich erwarten, dass wenn ich von einem 100
von dem einen abhebe, das dann hinterher null drauf ist,
in dem Moment Quellkonto, nicht Belastung.
Also das Quellkonto sollte null haben hinterher und das Zielkonto sollte doch üblicherweise 100 haben.
Das kann ich jetzt übersetzen und jetzt habe ich zwei Testfälle.
Das habe ich tatsächlich hier in meiner Test-Dings da,
zwei solche Tests. Jetzt kann ich wieder alles testen.
Also ich habe jetzt einen Testfall, wunderbar und das schlägt fehl.
Und jetzt sieht man das Schöne hier,
jetzt kann ich hier nämlich draufklicken und dann zeigt er mir tatsächlich hier unten an,
an welcher Stelle das jetzt irgendwie schief gegangen ist,
überziehen nicht möglich, sagt er hier.
Aha, oha, jetzt kann ich mir den Quelltext anzeigen lassen.
Und dann sagt er hier, if Quellkonto, Gibsaldo,
kleiner Gleichbetrag, dann soll er eine Acception schmeißen.
Wo ist der Fehler jetzt? Wo ist der Denkfehler? Genau,
das Problem ist, dass kleiner Gleich. Hier steht,
der Betrag muss kleiner gleich dem Saldo sein, damit es funktioniert.
Das heißt, der Fehlerfall ist größer. Das heißt also,
wenn das Quellkonto gibt, Saldo, kleiner den Betrag ist,
Dann soll er hier eine Exception schmeißen. Das heißt,
wir haben genau diesen Grenzfall getestet, der hätte noch gehen müssen,
aber weil das Ding hier falsch implementiert war, das heißt,
durch das Denken über Grenzfälle, nachdenken über den Ritter,
haben wir festgestellt, an der Stelle sollten wir jetzt tatsächlich diesen Grenzfall testen.
Und wir haben durch den Test einen Fehler aufgedeckt.
Das heißt, jetzt haben wir den Fehler behoben.
Das heißt, wenn ich das jetzt hier nochmal ausführe,
übersetzen und den Test nochmal ausführe, dann ist wieder alles grün.
Beide Testfälle funktionieren jetzt. Und so kann man eben halt
auch noch den anderen Grenzfall testen, welcher andere Grenzfall,
welchen anderen Grenzfall gibt es noch?
Was ist der kleinste sinnvolle Wert hier
Null ist nicht richtig. Das ist der kleinste Wert,
den wir wählen können hier.
Na, eins. Weil der Betrag muss größer Null sein.
Das steht auch in der Spezifikation hier an dieser Stelle,
der überwiesen werden soll, muss größer Null sein.
Also dann wäre der andere Extremfall, wäre jetzt hier die Eins.
So, das einfach nur, dass ihr nochmal diese Begriffe gesehen habt.
Also diese Grenzwerte sind wichtig und dann ist es aber auch wichtig,
irgendwie aus der Mitte irgendwie auch einen Testfall zu haben.
Also irgendwie so alle Werte zwischen 5 und 95 sind irgendwie so,
glaube ich, so fallen in einer Äquivalentklasse.
Das ist fast wurscht, welchen ich dann nehme.
Da muss ich nur vielleicht ein oder zwei Kandidaten nehmen.
Die sind dann
alle äquivalent
Gut, dann gibt es verschiedene Begriffe zum Test noch.
Sowas wie Modultests und Integrationstests. Brauchen wir uns jetzt nicht
intensiver ansehen. Was wir uns hier primär ansehen sind, Modultests.
Das heißt, wir haben einzelne Einheiten in einem Softwaresystem,
einzelne Klassen, einzelne Methoden. Und die testen wir.
Also in diesem Fall haben wir jetzt gerade gesehen,
dass man hier mit einer Testklasse eine andere Klasse testen kann.
Dann gibt es die Unterscheidung in Blackbox, Whitebox und Schreibtischtests
. Da gibt es also da verschiedene Begriffe.
Blackbox-Tests sind solche, wie wir sie gerade auch gemacht haben.
Wir haben die Schnittstelle gesehen von dem Überweiser,
haben gesagt, na gut,
wir testen das jetzt mal
Das heißt, ich verlasse mich nur auf die Schnittstellenbeschreibung und versuche, das Verhalten zu testen.
Das ist das, was man mindestens haben sollte.
Kann man sowohl positiver als auch negativ ausführen. Whitebox-Test,
da gucke ich wirklich in den Quelltext rein. Dann sehe ich,
gucke ich in die zu testende Klasse rein oder im
zu krassen Testen eine Methode und sehe die Kontrollpfade,
die da so laufen und sagt dann, oh,
ich möchte jeden Kontrollpfad wirklich mal ausgeführt sehen.
Dann formuliere ich meine Testfälle auch durchaus noch ein bisschen anders.
Also deutlich stärker technisch orientiert. Schreibtischtest ist ganz wichtig im
Sinne von extrem erfolgreich,
also extrem nützlich
Die meisten Fehler werden eigentlich aufgedeckt, dadurch,
dass sich Leute konzentriert hinsetzen und auf den Kalttext drauf gucken und drüber reden.
Also einfach durch Lesen des Quelltextes, nicht durch Ausführen,
sondern tatsächlich durch Lesen von Quelltexten, Reviews, Code-Reviews.
Ganz, ganz effektiv. Also in der Testtheorie ist das mit das Beste,
was man machen kann, irgendwie organisiert mit mehreren Leuten auf denselben Quelltext gucken.
So, und jetzt kommen wir eigentlich schon so in die Richtung von dem,
was wir jetzt hier schon live gesehen haben.
Deswegen die letzten Folien werde ich jetzt noch mal so ein bisschen kurz anticken,
aber im Grunde genommen habe ich das meiste schon gesagt,
es sollte zu jeder Klasse eine Testklasse,
zu jeder Guttestbahn, dass eine Testklasse existieren.
Zum Beispiel zu diesem Interface ist es relativ schwierig,
eine Testklasse zu schreiben, aber zu konkreten Klassen kann man Testklassen schreiben.
Zu Gui-Klassen, die ihr noch gar nicht kennenlernen werdet in SE1,
aber da zu solchen grafischen Benutzungsoberflächen ist es relativ schwierig,
Tests zu schreiben.
Aber insgesamt sollte man immer versuchen, an seinen zu testen,
also testbaren Klassen, auch Testklassen zu haben.
Und dadurch, dass man die eben halt immer wiederholen kann,
werden die dann zu sogenannten Regressionstests.
So, Werkzeugunterstützung gibt es, die sollte eingebunden sein in die Entwicklungsumgebung.
Jay Unit ist hier in Blue Jay eingebunden.
Es gibt Jay Unit für fast jede Entwicklungsumgebung,
auch für Clips
Das ist gut, damit man leicht Tests anstoßen kann.
Die Entwicklungsumgebung sollte einen dabei gut unterstützen und eben halt
auch die Ergebnisse gut visualisieren, haben wir gerade gesehen.
Jay Ulith ist so ein Beispiel dafür, sehr speziell für Java gebaut,
von Cant Back und Erich Gamma unter anderem.
Ist selbst in Java geschrieben, hat mal jemand gesagt,
es ist noch nie, haben so viele Menschen so wenigen Zeilen Code,
so viel zu verdanken gehabt, weil Jay Ulith tatsächlich einfach die Sicht auf Testen in der Java-Welt verändert hat.
Der Umgang mit Jay Ulith ist ganz einfach, was man machen muss,
man muss eine Testplasse erstellen und da drin Testfälle haben.
Haben wir gerade schon gesehen, also die Testklasse haben wir
geschrieben,
Testfälle sind einfach Methoden
Und in diesen Methoden wird eben halt die zu testen,
eine Klasse irgendwie manipuliert und getestet.
Strukturen eines Testfalls haben wir auch gesehen. Also wir machen irgendetwas,
er zeugen ein Konto, zahlen etwas ein und stellen dann fest,
ob das wirklich geklappt hat.
Immer dieses Überprüfen mit dem Assert. Die Folien,
die ich jetzt hier zeige, sind überarbeitet.
Was im Skript steht, ist leider veraltet.
Wir haben noch alte Folien gehabt, wo wir noch von
J. Unit 3, 8 ausgegangen sind.
Inzwischen kann Blade Blue Jay auch mit J. Unit 4.0 umgehen.
Die Unterschiede sind relativ klein, aber es wäre gut,
wenn ihr jetzt nochmal was nachlesen wollt,
wenn ihr die Folien aus der Vorlesung
heute nehmt
Die sind aktueller als das, was um Skripts steht. Ausführende Testfälle,
habt ihr jetzt auch gesehen, passiert tatsächlich so,
dass für jede Testmethode in dieser Testklasse,
wir haben hier zwei Testmethoden, man kann das hier sehen,
zwei Testmethoden, was passiert ist,
es wird für jede Testmethode ein Exemplar erzeugt und dann wird diese Methode an dem Exemplar aufgerufen.
Das heißt, es sind immer wieder frische Exemplare für jeden neuen Testfall.
Und deswegen auch dieser Begriff Testcase in Jay Unit 3.8,
aber das ist ja, wie gesagt, ein bisschen veraltet.
Was noch interessant ist, ist,
als letzter Punkt hier fehlschlagen von Testfällen,
nicht bestehen versus Fehler,
das zeige ich nochmal eben an diesem einen Beispiel
Letztes bald, letztes Beispiel. Wenn ich jetzt hier erwarte,
dass da 20 rauskommt, das ist jetzt falsch,
es wäre jetzt,
aber wenn ich in dem Assert etwas erwarte und das Testobjekt liefert nicht das,
was ich erwarte, dann nennen wir das einen fehlgeschlagenen Test.
Wenn ich das jetzt übersetze, das lässt sich natürlich übersetzen und ausführe,
dann gibt es hier einen Fehler und dann wird hier,
steht hier nicht bestanden, ein nicht bestandener Test.
Es ist ein Assert-Fehlgeschlagen. Also das zu testen,
Objekt hat sich anders verhalten, als ich es in meiner Testklasse erwartet habe.
Das kann ich auch wieder korrigieren. Also das ist ja
dann hier die richtige Erwartungswerte, ist ja 30.
Ich kann aber auch hier statt New Konto simpel einfach schreiben, null.
Das ist immer noch übersetzbar. Da habe ich einen Fehler
gemacht in meinem Test. Und jetzt wird es einfach eine
null Pointer Acception geben, wenn ich jetzt die Tests ausführe.
Und dann sieht man hier, es ist ein Fehler.
Das ist also tatsächlich hier genau zu sehen,
dass das unterschieden wird in Jay Unit,
ob ein Asserd fehlgeschlagen hat oder ob es irgendeinen anderen Fehler gegeben hat.
So ein Arror ist üblicherweise ein Fehler beim Testschreiben Failure ist,
wenn der Test richtig ist, ein Hinweis,
dass die zu testende Klasse nicht in Ordnung ist.
Gut, Struktur von J. Unit, 3.8 und 4.X
könnt ihr in Ruhe nochmal nachlesen, ist, wie gesagt, eigentlich nur Syntax.
Was ganz wichtig ist, dass Tree Unit diese ganzen Prüfmethoden mitliefert,
also so ein Assert Equals und Assert same, a third true,
a third null assert, not null und ähnliches,
mit dem man eben halt dann dafür sorgen kann,
dass dann meine Erwartung mit dem gelieferten Wert abgeglichen wird und wenn das nicht passt,
gibt es eine Exception.
Ja, und das ist auch schon die Zusammenfassung.
Das heißt also, testen Maßnahmen zur Qualitätssicherung.
Gutes Testen ist nicht leicht
Testen gehört zum Handwerkshund. Ihr solltet das erlernen,
ihr solltet es ernst nehmen und auch machen.
Und bei einem OO-System sollte man immer versuchen,
eigentlich eine Testklasse zu haben zu jeder testbaren Klasse.
Das Nachdenken übergeeignete Testfälle führt häufig schon zu besserer Software,
wenn man sich mit Testen gut auskennt,
ist man einfach genauer beim Programmieren.
Es gibt gute Testwerkzeuge, die uns viel Arbeit abnehmen können.
Und Jay Unit ist eben halt das bekannteste Werkzeug für Regressionstests
in Java. Wenn nicht noch Fragen sind,
dann machen wir Schluss für heute.
Vielen Dank