Okay. Ja, das hier ist ein Beitrag,
Trittle Graphics aus dem letzten Jahr, Trittle Graphics Creativity Contest.
Es ist einem YouTube-Video nachempfunden, Nike Cat. Ich glaube,
ich könnte hier auch noch mal kurz da
ein bisschen was zu an Sound vorspielen,
damit das da auch dazu passt
Ja,
komme frei
Nikett. Ja, herzlich willkommen zur achten Vorlesung,
SE1. Wie immer ein paar Worte vorweg.
Das war ein Beitrag vom letzten Jahr.
Den können wir auch jetzt beenden. Tortographics Creativity Contest.
Also, der Turtle Graphics Creativity Contest läuft immer noch.
Noch könnt ihr Beiträge einreichen. Wir haben schon Beiträge bekommen
. Also mindestens eine Woche habt ihr noch Zeit.
Die Gewinner werden dann in der Vorlesung vor Weihnachten vorgestellt.
Gut, dann haben wir noch weitere SE1 Clips fertiggestellt.
SE1 Clips, ihr erinnert euch daran. Das waren diese Clips,
in denen wir so typische Situationen beim Programmieren im Paar,
mit unserem Übungsbetrieb, sagen wir, ja, typische Situationen inspiziert haben.
Und hier sind, die sind auch tatsächlich im SR1,
kommen sie inzwischen zu finden. Also hier SR1-Clips,
da findet man einen Hinweis,
dass hier unter diesem Link diese Videos zu finden sind,
auch Lecture to go, ich kopiere mir mal hier dieses Passwort,
das braucht man momentan noch dazu,
dann kann ich das jetzt hier auch noch mal zeigen,
gebt gleich noch ein weiteres Video.
Ich hoffe mal, das dauert jetzt hier nicht zu lange.
Lalala, Lecture to go, ja, genau,
Passcode eingeben, dann nehmen wir den mal hier mit rein,
ja, meinetwegen speichern.
So, und dann haben wir hier tatsächlich inzwischen fünf Videos,
ja, und ich will jetzt noch mal im 1 zu 1 so anreißen,
damit ihr es noch mal sehen könnt.
Moin, was gehst du denn da rein aus?
Ich habe Blutsche, ich habe Blutsche, das ist nicht
dein Herz. Obwohl es dann schon auch, ja,
hier gibt es keinen Bock und so, es macht alles.
Ja, Hilfe, was ist das nächste,
was ich schon verstehen kann
Ja, es gibt trotzdem noch irgendwelche Interferenzen. Was ich beachtet sehen möchte,
ist, dass es hier diese beiden Balken gibt.
Das hat einen gewissen Sinn und Verstand bei unseren Videos.
Also es gibt so einen Lerneffekt und einen Bremsfaktor.
Und immer wenn jemand irgendwie den anderen ausbremst,
dann kriegt er irgendwie hier was auf den Balken drauf.
Oder wenn jemand tatsächlich ein bisschen was tut inhaltlich,
dann kriegt er hier den Balken was drauf.
Das ist ganz nett, das zu beobachten.
Und am Ende gibt es dann immer ein Ergebnis.
Und ja, diese Videos sind ganz nett, wie wir finden.
Wir haben Spaß gehabt, die zu machen.
Die Frage ist natürlich, ob die irgendwas bringen.
Ob die irgendwie euch was nutzen oder euch was,
die euch zumindest Spaß machen.
Also wenn ihr da Feedback zugebt, dann macht das gerne.
Im Kommensee könnt ihr ja auch das Material kommentieren und ähnliches.
Gut, jetzt endlich mal, jetzt geht es mal voran.
Wir sind jetzt hier in Woche 8. Ich habe jetzt schon
sieben Vorlesungen gehalten. Wir haben Level 1 und Level 2
schon hinter uns gebracht. Level 1 so auch einfache Objekte,
einfache Klassen. Dann auf Level 2 wurde es ein bisschen komplizierter mit den Referenzen,
dass Objekte auch über Referenzen miteinander verbunden sein können.
Dieses Konzept von Referenzen und Referenzvariablen
Zentral, auch heute wieder sehr wichtig,
damit man das verstehen kann, was ich hier erzähle.
Wir haben ein bisschen was über OML gelernt, Rekursion.
Wir haben also Wiederholungen programmiert, sowohl mit Rekursion als auch mit Schleifen,
also iterativ. Haben uns Drinks angeguckt und reguläre Ausdrücke,
Sichtbarkeit und Lebensdauer mal so ein bisschen besprochen.
So, und jetzt geht's weiter. Wir sind am Rande einer nächsten Stufe.
Und jetzt ist es ein bisschen so, diejenigen,
die programmieren konnten, die sollten jetzt ein bisschen aufpassen,
weil das ist klassisch der Punkt,
wo wir erfahrungsgemäß aus den letzten Jahren gehört haben,
oh Mensch, da habe ich den Anschluss verpasst.
Ich wusste gar nicht, dass jetzt so viel Neues kommt
Das heißt also für diejenigen, die schon programmieren können,
jetzt kommen Sachen, die sind wahrscheinlich vorher noch nicht so auf eurer Agenda gewesen.
Also das sind so Dinge, die nicht ganz so selbstverständlich sind.
Wir machen jetzt ein paar Dinge, die nicht Standard sind, sagen wir mal so.
Und auch nichtsdestotrotz sehr wichtig. Für die Programmieranfänger heißt das,
dass wenn sie gut aufgepasst haben, das ist überhaupt kein Problem ist,
weil das alles ja so schön aufeinander aufbaut, das nachzuverziehen.
Das ist das, was ich jetzt erzähle.
Aber bis jetzt nicht gut aufgepasst hat,
er hat jetzt gute Chancen, sehr schnell abgehängt zu werden.
Also das ist jetzt so ein Punkt,
Bergfest der Veranstaltung, die Hälfte ist rum.
Jetzt geht es ein bisschen ein bisschen schneller voran und wir
bauen immer auf dem auf, was wir schon vermittelt haben.
Okay, also heute geht es um, zum einen Interfaces,
ein Konzept in Java, um Schnittstellen zu beschreiben.
Das sehen wir uns ausführlich an vor der Pause.
Und nach der Pause werde ich dann auf Testen eingehen und auf automatisiertes Testen.
Und in der nächsten Woche sehen wir uns dann an,
die man in Java sehr schön sammlungskonzepte,
die in der Bibliothek vorgegeben sind, benutzen kann,
um Probleme zu lösen,
die mit Sammlungen ganz gut lösbar sind.
Heute geht es um zuerst um Klassentypen und Interfaces.
Und was ich in den letzten Wochen erzählt habe,
ist, dass eine Klasse auch ein Typ definiert.
Also irgendwie in Vorlesung 5 habe ich gesagt,
eine Klasse definiert auch einen Typ. Da habe ich
ja vorher schon gesagt, was überhaupt ein Typ ist.
Naja, es ist irgendwie eine Anzahl von Operationen und
eine Wertemenge, die zu dem Typ dazugehört.
Und die öffentlichen Methoden einer Klasse sind genau die Operationen.
Und die Exemplare bilden die Wertemenge. Das heißt,
so eine Klasse ist ein Typ, ich kann also
eine Variable deklarieren mit dem Namen als Typ einer Klasse.
Ich kann es schreiben, K, Konto, K,
dann ist Konto eine Klasse oder Quadrat hatten wir auch bei diesem Zeichnerbeispiel.
Das sind Klassenname, die konnte ich aber als Typennamen
verwenden.
Also Klassen sind Typen
Das heißt, eine Klasse definiert einen Typ,
aber sie legt auch eine Implementation fest. Das heißt,
eine Klasse ist immer auch sofort die Umsetzung dessen,
was da in dem Typ versprochen wird.
Und ein Interface definiert ausschließlich einen Typ und enthält keine Implementation.
Und das gucken wir uns jetzt näher an.
Und was wir uns dazu wieder mal klarmachen müssen,
dass im Kern von allem, was wir hier so machen,
und das ist tatsächlich nicht nur für die Objektorientierung,
sondern allgemein für Softwareentwicklung.
Es gibt immer dieses Verhältnis hier. Irgendein Klient nimmt einen
Dienstleister in Anspruch. In der Objektorientierung sind das Objekte,
in der imperativen Programmierung kann das sein,
dass eine Prozedur eine andere Prozedur aufruft,
wie auch immer
Es ist immer so, dass irgendwo
eine Dienstleistung angeboten wird und jemand nimmt sie in Anspruch.
Das Beispiel, was ich hier benutzen will,
jetzt auch durchgängig heute in der Vorlesung, ist das hier.
Das habt ihr in Vorlesungen in Woche 5 in Übungen programmiert. Ihr habt,
das Konto habe ich immer in der Vorlesung schon mehrfach gezeigt,
dass es hier diese Klasse Konto sehr einfach definiert.
Man kann was auszahlen, man kann was einzahlen und
man kann sich den Saldo geben lassen von einem Konto.
Das solltet ihr jetzt mehrfach gesehen haben, das solltet ihr
für euch jetzt in der Schnittstelle keine Überraschung sein.
Ihr habt doch gesehen, wie man das implementieren kann
Und dann hattet ihr ja eine Aufgabenplatz 5. Solltet ihr einen
Überweiser schreiben. So ein Überweiser sollte eine Dienstleistung anbieten.
Das ist etwas, was ihr üblicherweise auch,
wenn ihr an so ein Bankturmle geht, gerne habt,
dass ihr sagen könnt, ich möchte jetzt was überweisen.
Eine Überweisung machen. Und so die Software dazu könnte so aussehen,
also ein Überweiser. Da muss man jetzt hier mal eben
auf die Dokumentation umschalten, weil die ist mir erstmal wichtiger.
Das ist die Schnittstelle. Das heißt,
wenn ich jetzt das Ding benutze, ich bin ein Klient,
ich trete hier interaktiv gleich als Klient auf von dem Überweiser,
dann möchte ich von einem Quellkonto auf ein Zielkonto einen Betrag überweisen.
Das ist das, was dieser Überweiser mir als Dienstleistung anbietet
Okay, das ist erstmal für sich genommen ganz einfach.
Ich kann also tatsächlich jetzt hier mir einen Überweiser erzeugen
und kann dann an diesem jetzt Überweisen aufrufen.
Wenn ich das jetzt tun will, kann ich das
natürlich nicht, wenn ich nicht schon konkrete Konten habe.
Ich müsste mir also jetzt erstmal hier für die Demonstration Konten erzeugen.
Ihr wisst ja, wie man sich konnten, erzeugt.
Man erzeugt sich einfach Exemplare und übergibt dann an den Überweiser
hier diese Referenzen, die sich daraus ergeben.
Okay, soweit so Hut. Das ist An der Stelle
immer noch mal wieder ein guter Punkt zu sagen,
erinnert ihr euch noch, dieses mit den Dienstleistern,
der Überweiser, wenn ich den jetzt hier interaktiv benutze,
ich benutze jetzt hier als interaktiver Klient den Überweiser,
der Überweiser ist ein Dienstleister für mich.
Aber der muss, um seine Dienstleistung zu erbringen,
wieder die Dienstleistung von anderen Klassen in Anspruch nehmen.
Also er hat wieder andere Dienstleister,
wie zum Beispiel hier die Klasse Konto, die entsprechend weiterhilft.
Das heißt also, wenn das hier der Überweiser ist,
dann benutzt er hier wieder in seiner Implementation,
um seine Dienstleistung zu erbringen,
andere Dienstleister.
Soweit so gut
Was jetzt sozusagen ein bisschen stärker in den Fokus rückt,
in dem, was ich erzählen will, ist der Begriff der Kapselon.
Und Kapselon ist eigentlich erst mal nur eine Art Mechanismus,
um in einer Programmiersprache Dinge zu verstecken.
Kapseln ist so, ich will das wegkapseln. Das heißt,
ich kann mit Public und Private ja steuern,
welche Anteile einer Klasse für Klienten sichtbar sind.
Das kennt ihr. Private Methoden, private Exemplarvariablen.
Mit Private kann ich Dinge verstecken. Damit sind sie gekapselt
. Also vor dem Zugriff von außen erst mal geschützt.
Das heißt, was wir wollen, Wir wollen,
dass man Klassen im Sinne von diesem Dienstleister als Blackbox betrachten kann.
Dass ich wirklich sage, hier, Überweiser,
du kriegst von mir zwei Referenzen auf zwei Konten und einen Betrag.
Mir ist egal, wie du das machst, mach das einfach.
Das ist eine Dienstleistung. Das wollt ihr auch,
wenn ihr an so einem Kontoautomaten steht, wollt ihr nicht wissen,
wie das jetzt hinter den Kulissen alles funktioniert.
Blackbox. Vorteile von Kapsel und allgemein sind erstmal das Ausblenden
von Details vereinfacht, die Benutzung. Das heißt,
wenn ihr euch nicht Gedanken machen müsst, was dahinter passiert,
sondern einfach nur sagt, hier, Konto 1,
Konto 2 muss ich irgendwie angeben und dann noch einen Betrag, wunderbar.
Das hat aber noch einen weiteren Vorteil,
dass der Dienstleister seine Implementation ändern kann,
ohne dass man, dass die Klienten davon betroffen sind.
Also man kann die Schnittstelle beibehalten, kann aber ändern, wie etwas umgesetzt ist.
Das sind erstmal so die allerersten Vorteile. Das heißt,
wenn ich jetzt mir hier den Quelltext vom Überweiser mal angucke,
ja, das ist die Dokumentation, das ist die Außensicht,
die Schnittstelle auf die Klasse Überweiser.
Und wenn ich jetzt in den Quelltext reingehe,
dann sehe ich hier, wie das konkret gebaut ist.
Das heißt, ich sehe hier zum Beispiel einen Methodenrumpf.
Ich sehe also tatsächlich, wie die Dienstleistung umgesetzt ist.
Das sehe ich ja in der Schnittstellesicht nicht.
Was man an der Stelle dann einfach sieht, ist,
dass so eine Klasse in der Objektorientierung eine ganz starke Doppelrolle spielt.
Also eine Klasse ist aus der Sicht eines Klienten,
ist es irgendwie ein Dienstleister. Ein Klient guckt da
drauf und sagt, welche Operationen kann ich dann aufrufen?
Welchen Typ haben die Parameter? Da muss ich natürlich genau hingucken,
wie sind die Kommentare, was steht da drin,
wie ich das Ding so zu benutzen habe?
Wie ist das also dokumentiert? Wie ist das in der Benutzung?
Das ist für einen Klienten interessant, das ist die Außensicht.
Die öffentlichen Eigenschaften, das sind die Dienstleistungen,
die angeboten werden. Das ist die Schnittstelle,
das sind alles so Begriffe, die ihr schon mal jetzt
gehört haben solltet
So, und wenn ich jetzt die andere Sicht einnehme,
also die Innensicht, jetzt gucke ich sozusagen in die Klasse rein,
haben wir gerade gemacht,
die haben wir in den Quelltext reingesehen und im Quelltext sehe ich dann mehr.
Ich sehe natürlich auch alles das, was nach außen angeboten wird,
ja, ist ja logisch, aber ich sehe mehr,
ich sehe die Operationen, wie die in den Methodenrümpfen konkret umgesetzt sind.
Ich sehe, welche Exemplar war ja, wenn da
definiert sind, das sehe ich als Klient erstmal nicht.
Ich sehe auch, welche privaten Hilfsmethoden möglicherweise da sind,
kann es ja alles geben. Das ist dann die Innensicht,
das sind die privaten Eigenschaften, das ist die Implementation.
Das sind wirklich zwei ganz unterschiedliche Rollen,
die so eine Klasse da spielt und wir müssen uns daran gewöhnen,
als Entwickler, dass wir ständig quasi diesen Spagat hinkriegen müssen.
Aus welcher Sicht gucke ich da jetzt gerade drauf,
als Klient oder als Wartungsprogrammierer, der rein muss in die Klasse,
in den Quelltext. So, was man dann sieht,
ist, dass das tatsächlich, man kann das trennen.
Ich kann das begrifflich trennen. Ich kann sagen,
es gibt sowas wie die Schnittstelle einer Klasse, das,
was für Klienten wichtig ist.
Und ich sehe das, was für einen Wartungsprogrammierer wichtig ist.
Begrifflich und technisch. Und dass das tatsächlich auch technisch unterstützt wird,
haben wir ja gesehen. Im Blue Jay kann ich ja
hier jetzt im Editor tatsächlich für eine Klasse mir zum einen
den Quelltext anzeigen lassen oder die Dokumentation,
also die Schnittstelle oder die Implementation, kann man es auch nennen.
Dokumentation ist die Schnittstelle und Quelltext ist die Implementation. Und wenn ihr euch die Java-ARPI anguckt,
das habt ihr schon gemacht für die Klasse String,
wir werden da noch nächste Woche noch mehr drauf eingehen auf diese API,
dieses Application-Programming-Interface, also die Schnittstellen, die Java so anbietet,
das sind alles so Dienstleistungen, die man benutzen kann.
Und die sind eben halt auch in dieser Dokumentationsansicht dargestellt.
Das heißt, da müsst ihr auch nicht reingucken,
wie es gebaut ist, ihr müsst einfach nur sehen,
was bietet mir das Ding eigentlich an
So, wenn man jetzt erst mal sich darauf eingelassen hat,
auf diese Trennung und sagt, naja,
ich kann das also erst mal unterscheiden, sauber auseinanderhalten,
dann kann man sich auch den nächsten Schritt überlegen,
könnte es nicht sein, dass man diese Dienstleistung,
die da noch einem Klienten gegenüber versprochen wird,
dass man die auf verschiedene Art und Weise implementieren kann.
Und das ist erst mal gar nicht so schwer,
sich das vorzustellen und ich will es an einem konkreten Beispiel machen.
Wir haben jetzt hier bisher gesehen, dass diese Klasse Konto,
ich mache jetzt hier mal ein weiteres Projekt auf,
da sieht das
ein bisschen anders aus
Bisherige Implementation, die ihr kanntet,
die ihr gut kennt, ist, es gibt die Operation
Einzahlen, Auszahlen gibt es halt auch, Schnittstelle. So,
und die Implementation benutzt eine Exemplarvariable, die den Saldo hält
und jede eine und Auszahlung verändert den Wert dieser Variablen.
Ganz einfach. Man könnte es aber auch anders machen. Es ist einfach,
man kann sich überlegen, so ein Konto,
diese Grundfunktionalität kann auf verschiedene Art und Weise implementiert werden und
eine andere Möglichkeit ist hier mal so andiskutiert,
die Information über jede eine Auszahlung werden in einer Liste gespeichert.
Also irgendwie hält so ein Konto intern eine Liste,
was eine Liste ist, werden wir uns nächste Woche
noch angucken,
Man kann die in Java programmieren, aber erst mal gehen
wir jetzt davon aus, man kann die einfach benutzen.
Und der Saldo, wenn man Gibsaldo aufruft,
wird dann einfach berechnet, indem alle diese Ein- und Auszahlung aufsummiert werden.
Das ist in manchen buchhalterischen Kontexten, wird das tatsächlich so gemacht,
dass man nur sozusagen die Kontobewegungen protokolliert.
So, und für einen Klienten würde sich erst mal nichts ändern.
Der ruft in beiden Fällen die sichtbaren Operationen auf,
einzahlen, auszahlen und erhält auch die gleichen Ergebnisse.
Ich kann das hier einfach mal zeigen.
Hier ist jetzt eine klasse Konto anders, die auch
ein Konto darstellt
Hält intern hier eine Liste von, naja,
das ist jetzt Details für nächste Woche, eine Liste von Intigern.
Also ihr könnt euch grob merken, das ist eine Liste von ganzen Zahlen.
Und das sollen einfach diese ganzen Buchungen sein. Im Konstruktor,
ja, im Konstruktor, werden, wird diese Liste jetzt
initialisiert auf eine Leereliste, da ist erstmal nichts drin.
Und immer wenn man ein Zahlen aufruft oder auszahlen,
dann wird einfach in dieser Liste der entsprechende Betrag in diese Liste mit reingefügt.
Also hier der positive Betrag beim Euinzahlen und der negative Betrag beim Auszahlen.
Also als wären sozusagen positive und negative Zahlen gespeichert,
die sozusagen sich auf den Kontostand da irgendwie drauf verdieren
Ja, und wenn jetzt hier Gibsaldo aufgerufen wird,
dann wird eben halt, das Ding hat keine Exemplarvariable.
Hier ist keine Exemplarvariable für den Saldo, sondern der Saldo
wird jetzt on Demand erst berechnet. Also wenn jemand gibt,
sei der Aufruf, dann wird er gesagt, ja,
Saldo ist erstmal null, damit fangen wir immer an.
Und dann gehen wir durch alle Buchungen durch. Das ist eine erweiterte Vorschleife,
sehen wir uns auch nächste Woche an,
müsst ihr erstmal jetzt nicht im Detail verstehen,
aber ich gehe jetzt einfach durch alle diese Buchungen durch,
addiere die drauf und am Ende kriege ich dann den tatsächlichen Kontostand.
Kann man so machen. Es sind einfach verschiedene Implementationen
derselben Dienstleistung. Einzahlen, Auszahlen, Gibshaltung
Hier gibt es jetzt noch eine weitere Methode,
gib Anzahl Buchungen, die bietet sich an,
sowas anzubieten im Sinne von, naja,
wenn ich jetzt sozusagen tatsächlich die Anzahl der Buchungen ja sowieso habe,
ich weiß ja, wie viele Elemente meiner Liste drin sind,
dann kann ich das ja ganz leicht rausgeben.
Kann das andere Konto zum Beispiel nicht.
Also ich hatte die andere Implementation jetzt tatsächlich auch Vorteile,
dass je mehr Dienstleistungen anbieten kann nach außen.
Okay, und diese beiden Implementationen, die sind in ihrer Schnittstelle gleich.
Und genau das kann man modellieren in Java,
indem man ein sogenanntes Interface programmiert. Das heißt,
hier haben wir jetzt eine ein neues Konstrukt von Java,
Interface. Das ist ein, hier ist das Schlüsselwort Interface
und da stehen jetzt genau diese Dienstleistungen drin beschrieben.
Aber nur die Köpfe der Methoden. Also hier steht jetzt
nur so ein bisschen eine Beschreibung,
was das Ding machen soll und wie die Signatur aussieht.
Also wie hier tatsächlich welche Parameter das Ding bekommen soll,
aber ansonsten steht hier nur ein Semikola.
Und was man jetzt sieht ist, diese beiden Klassen,
die haben hier so einen komischen Pfeil und dieser Pfeil
besagt,
sie implementieren dieses Interface
Also beide, konto simpel und Konto anders,
implementieren die Dienstleistungen, die das Interface-Konto verspricht.
Okay. Das ist das, was man hier erstmal sich jetzt klarmachen muss.
Also Java hat so ein spezielles Sprachkonstrukt und die kann man
benannte Schnittstellen nennen. Benannte Schnittstellen, weil tatsächlich ja diese
Schnittstelle eines Kontos wird hier explizit mit einem eigenen Mechanismus benannt.
Falsches Projekt, hier ist es. Interface-Konto. Okay.
Und dann steht da nicht viel drin,
jetzt sind hier auch die Kommentare weggelassen,
man sieht hier,
ist wirklich
sehr knapp
Und was wir jetzt hier begrifflich machen,
einfach um es uns leichter zu machen, ist,
wenn wir über die Schnittstelle einer Klasse reden,
weil eine Klasse hat immer eine Schnittstelle,
also sowohl Konto simpel als auch Konto anders,
haben ja eine Schnittstelle, also das,
was sie nach außen anbieten, die öffentlichen Methoden,
kennt ihr schon als Konzept, nennen wir weiterhin Schnittstelle.
Und diese Dinger hier, diese benannten Schnittstellen,
also diese eigenen Konstrukte aus Java, die nennen wir einfach Interfaces,
benennen wir genauso wie die in Java, wie der Mechanismus heißt.
Im Englischen ist das ein bisschen schwieriger, die müssen dann zu beidem interfaces sagen.
The interface of a class und Interface als Mechanismus,
wir können das unterscheiden,
wir nennen dieses Konstrukt Interface und die Schnittstellen einer Klassen an Klasse sind,
sind sein Begriff, den ihr kennt.
Gut, jetzt haben wir hier zentrale Eigenschaften, erstmal,
dass wir uns nochmal klar machen, was macht so ein Interface überhaupt?
Also sind Sammlungen von Methodenköpfen? Habt ihr gerade gesehen?
Da stehen einfach Methodenköpfe drin. Alle Methoden in einem Interface
sind implizit Public. Es gibt keine privaten Eigenschaften in Interfaces.
Das sind keine Methodenrümpfe drin, haben wir gerade gesehen.
Da steht einfach nur immer so ein Simikolon statt eines Methodenrumpfes.
Man darf auch keine Felder definieren. Es gibt keine Felder
in Interfaces und man kann auch keine Exemplare davon erzeugen.
Ich kann also jetzt hier nicht von diesem Interface jetzt
irgendwie mehr ein Exemplar erzeugen.
Geht nicht
Interfaces sind nicht instanziierbar. Kann man sich natürlich fragen,
wofür sind die überhaupt gut, wenn da gar nichts drinsteht?
Steht da gar kein Code drin, ist ja irgendwie,
ist das nicht beliebig sinnlos? Es ist nicht sinnlos,
weil man zum einen tatsächlich jetzt eine Dienstleistung beschreiben kann und diese Dienstleistung auch anbieten kann gegenüber einem Klienten.
Das ist ganz hübsch zu sehen, aber erstmal gucken wir uns an,
wie so ein Interface implementiert wird,
nur dass ihr es einfach nochmal gesehen habt,
wenn eine Klasse sagt, ich implementiere dieses Interface,
also ich, die Dienstleistungen, die da das Interface verspricht,
die kann ich tatsächlich anbieten,
die habe ich
wirklich implementiert
Dann wird das so hingeschrieben, dass eine Klasse in ihrem Kopf sagt, Implementskonto.
In diesem Fall also sieht man das auch an dieser Stelle.
Ich habe hier Kontosimpel und da steht, steht obendrin, Implementskonto.
Und weil das da steht, ist auch dieser Pfeil hier
im Diagramm drin. Das ist so ein ganz spezieller Pfeil,
so mit so einer weißen Pfeilspitze und gestrichelt.
Das heißt, in der UML so viel wie Implements.
Also dieses Klasse implementiert dieses Interface. Und bei Konto an,
da steht das genauso drin. Hier oben, Implementskonto.
Wunderbar. Diese beiden Klassen implementieren das Interface und Was ich jetzt machen kann,
ist, überall da, wo jemand ein Konto erwartet,
kann ich jetzt ein Exemplar dieser beiden konkreten Klassen angeben.
Das heißt, was ganz Spannendes, die Auswirkungen auf einen Klienten sind,
praktisch nicht erkennbar, bis auf ein paar Kleinigkeiten.
Ich zeige das jetzt mal,
indem ich aus diesem Projekt hier, ja, diesen Überweiser,
der benutzt ja hier in diesem Projekt eine Klasse Konto.
Der benutzt diese Klasse Konto. Und er kann eben halt
tatsächlich Überweisungen machen von einem Konto auf ein anderes.
Jetzt mache ich das mal folgendermaßen. Ich nehme aus diesem,
in diesem Projekt kann ich jetzt sagen, klasse aus Datei
hinzufügen und jetzt gehe ich hier in dieses Projekt rein,
was ich hier eben aufhatte
Und nehme jetzt hier den Überweiser, diese Klasse,
in mir ein Projekt hier mit rein, hinzufügen.
Schwupps ist die da und die benutzt ja in ihrem Quelltext Konto.
Na und hier benutzt die jetzt passiert das Interface-Konto.
Und das kann ich sogar übersetzen. Hier benutzt der Überweiser das Konto.
Das heißt, im Quelltext hier von dem Überweiser stehen jetzt
so Dinge wie Konto und Konto und das ist aber tatsächlich ein Interface.
Oha. Und das funktioniert trotzdem. Ich kann jetzt mir
hier ein Konto simpel erzeugen, KS,
und kann da jetzt was einzahlen.
Also ich sage mal hier,
dann zahle ich mal
So, dann sehe ich ja hier Objektinspektor, da sind 100 drauf.
Und ich kann aber auch mir so ein Konto anders erzeugen.
Und das nenne ich mal KA. So,
und auch da sehe ich den Saldo leider nicht direkt,
aber wir wissen ja eigentlich,
es ist auch gar nicht so schlau, da reinzugucken,
weil das ist ja eigentlich eine Blackbox.
Ich gucke mir einfach die Schnittstelle an, naja,
gib Saldo, was sagt er? Null. Genau.
Und der hier sagt, gib Saldo 100. Genau.
Das ist das, was wir erstmal so ganz gut kennen.
Und jetzt kann ich tatsächlich mir hier einen Überweiser erzeugen
Zack. Und dem kann ich jetzt sagen, hey,
überweise doch mal. Von dem Quellkonto, also von KS,
auf das Zielkonto, Konto anders, sagen wir mal 30 Euro.
30, was auch immer, für eine Währung. So,
und jetzt könnte ich das ausführen. Und dann, ja,
dann wüsstet ihr, dass auf dem 71 drauf sind,
auf dem anderen 30. Mal gucken, ob das auch stimmt.
Ich führe es mal aus, hat geklappt.
Ich habe diesen Überweiser überhaupt nicht angefasst, wo er vorher
eine Klasse benutzt hat, benutzt er jetzt ein Interface.
Und ich schummel dem hier, sozusagen unter der Maske eines Interfaces,
schummel ich dem jetzt hier zwei verschiedene Exemplare von verschiedenen Klassen unter.
Und das funktioniert. Wunderbar. Jetzt kann ich also hier gucken,
Gibsaldo, der hat 70 drauf und der hier Gibsaldo,
hat 30 drauf. Es hat also tatsächlich funktioniert. Okay,
das heißt, die Nutzung von so einem, über so
ein Interface ist komplett unverändert. An die einzige Stelle,
an der man eben tatsächlich nicht dran dran vorbeikommt,
auch macht, wirklich da konkrete Klassen zu benutzen,
ist bei der Objekterzeugung.
Also ich musste ja tatsächlich jetzt hier sagen,
New Conto Simple und New Conto anders,
um wirklich Exemplare zu bekommen
Ja, aber dann konnte ich die Benutzung ganz normal weitermachen.
Was wir jetzt gesehen haben, ist,
dass man diese Schnittstelle und die Implementation trennen kann.
Also wir haben jetzt gesehen, die Schnittstelle kann ich
in einem Interface beschreiben, Implementationen in verschiedenen Klassen.
Die Klientenklasse benutzt die Dienstleistung des Interfaces und ist dann von der konkreten Anbieterklasse entkoppelt.
Das heißt, der Überweiser weiß nicht genau,
dass es da jetzt eine Klasse Kontosimpel gibt oder eine Klasse Konto anders.
Muss er nicht wissen. Okay,
jetzt haben wir in diesem Beispiel gerade gesehen,
wir haben Dieses Konto, was ihr kanntet, die Klasse Konto,
da haben wir dann das Interface rausgezogen und haben gesagt,
ja, wir beschreiben das Interface, die Schnittstelle als eigenes Ding.
Und da haben wir das Interface aus der Klasse abgeleitet.
Zuerst war die Klasse da, dann haben wir das Interface definiert.
Das geht aber auch umgekehrt. Ich kann tatsächlich zuerst ein Interface schreiben,
nur mit Methodenköpfen und viel Kommentar, weil ich dann beschreibe,
was da eine implementierende Klasse leisten soll.
In dem Moment, wo man etwas schreibt in der Softwareentwicklung,
wo man sagt, das soll so sein,
ist das eine Spezifikation
Das ist ein Begriff, den ihr jetzt zum ersten Mal
hört und der ziemlich zentral ist in der Softwareentwicklung.
Die Spezifikation beschreibt üblicherweise das, was gebaut werden soll.
Es beschreibt nicht, wie das gebaut wird,
sondern es beschreibt nur, was als Dienstleistung angeboten werden soll.
Wenn wir uns die Dienstleistung von dem Überweiser angucken,
jetzt nur mal die Schnittstelle von dem Überweiser, würde man sagen,
ein Überweiser soll etwas von einem Konto auf ein anderes überweisen,
unter bestimmten Randbedingungen.
Diese Randbedingungen, die sind ja tatsächlich schon erläutert.
Der Aufruf dieser Methode beweist den angebenden Betrag vom Quellkonto
auf das Zielkonto
Da sind die aber schon so ein paar Randbedingungen formuliert.
Also Konto muss ungleich nall sein. Da darf nicht ein Nall-Korpel übergeben,
wenn eine Nall-Referenz. Zielkonto muss auch ungleich nall sein.
Und bei dem Geldbetrag gibt es auch Randbedingungen. Hier ist irgendwie gesagt,
muss größer als null sein, weil ein Betrag,
kleiner Null, ist eben komisch und sollte aber auch kleiner sein,
dem Saldo des Quellkontos.
Also ich darf nicht mehr überweisen, als auf dem Quellkonto
drauf ist. Das sind so formale Randbedingungen. Und solche
formalen Randbedingungen sind klassisch Spezifikationen. Das heißt,
ich könnte auch für den Überweiser jetzt erstmal ein Interface hinschreiben,
wo nur dieser Kommentar drin steht und dieser Methodenkopf mit den
drei Parametern
Und dann sagen, ja, jetzt implementiert das doch mal.
Hätten wir euch auch so theoretisch vorgeben können in Woche 5
, da wollten wir bei Interfaces noch nicht einführen.
Und dann ist ein Interface plötzlich eine Spezifikation.
Also sie schreibt vor, wie etwas implementiert werden soll.
Und immer ganz wichtig bei einer Spezifikation, sie beschreibt,
was eine Einheit leisten soll, aber nicht, wie sie das macht.
Ja, und da kann man eben unterscheiden, informelle Spezifikation,
das hier bei dem Überweiser ist eine informelle Spezifikation.
Da steht in natürlicher Sprache, was passieren soll.
Gibt aber auch formale Spezifikationen, also mit einem mathematischen Kalkül hinterlegt.
Das sind Dinge fortgeschrittene Themen, die wir hier nicht
behandeln werden
Aber uns geht es erst mal darum,
dass ihr dieses Konzept einer Spezifikation versteht. Also ihr versteht,
dass es sowas gibt wie eine Beschreibung dessen,
was sein soll, was implementiert werden soll.
Das ist wirklich ein ganz wesentliches Konstruktionsprinzip in der imperativen Ort der Objekte und Hidden Premiere,
dass man trennt zwischen der Beschreibung einer Dienstleistung und der tatsächlichen Implementation.
Und außerdem in jeder professionellen Softwareentwicklung wärt ihr sowas ständig finden.
Okay. So, jetzt haben wir gesehen,
eine Klasse definierten Typ, das kennt ihr schon.
Eine Klasse definierten Typ. Also ich kann eine Klasse einfach
als Typ für eine variable Deklaration verwenden,
den Klassennamen
Bei einem Interface ist das genauso. Ich kann tatsächlich,
und das habt ihr ja hier gesehen in diesem Projekt,
der Überweiser hat in diesem Projekt,
gibt es ein Interface-Konto und der Überweiser konnte hier schreiben, Interface-Konto.
Konnte den also als Typ verwenden. Und also definiert auch
einen Typ in dem Sinne, dass es eine Schnittstelle gibt,
also genau die Operation, die man aufrufen kann.
Das ist ja klar. Und alle Exemplare von Klassen,
die dieses Interface implementieren, die bilden dann die Wertemenge
dieses Typs
So, das heißt also,
wenn wir jetzt mal uns diesen Typ-Begriff ein bisschen genauer angucken,
so im objektorientierten Sinne, dann ist für uns wichtig,
welche Objekte gehören zur Wertemenge des Typs,
also hier zum Beispiel zur Wertemenge des Typs,
Konto gehören alle Exemplare von Konto anders und von Kontosimpeln.
Und welche Operationen sind auf den Objekten zulässig? Ja klar,
die, die auch in dem Typ Konto definiert sind,
aber es gehört nicht dazu zu der Typ-Information, wie das implementiert ist.
Eine oder andere Art und Weise, das ist nicht wichtig.
Und das ist so ein bisschen so jetzt so ein Schritt,
den wir jetzt weggehen von dem klassischen Typbegriff,
den ich vor ein paar Wochen vorgestellt habe,
da habe ich gesagt,
so ein Typ definiert eine Menge an Werten
Die einen barriablen auch so annehmen kann. Jeder Wert gehört zu genau einem Typ.
Die Typinformation ist statisch aus dem Quelltext ermittelbar, genau.
Und ein Typ definiert die zulässigen Operationen. Ganz wichtig. So,
und hier ist es jetzt tatsächlich, wir erweitern diesen Typbegriff und sagen,
naja, in der Objektantürung ist es so,
ein Typ definiert das Verhalten von Objekten durch eine Schnittstelle,
ohne die Implementation festzulegen, ohne vorzuschreiben,
wie das gebaut werden soll.
Und in der Folge ist es dann so,
wenn ich jetzt tatsächlich konkrete Klassen habe, die ein Interface implementieren,
also ein Typ implementieren, dann habe ich immer genau
Eine Klasse, von der ich ein Objekt erzeuge.
Also wenn ich jetzt hier mir diese Dinge angucke,
hier Konto simpel und Konto anders, das sind konkrete Exemplare von konkreten Klassen.
Und das behält auch meistens, also so ein Objekt bleibt immer ein Exemplar seiner Klasse.
Aber da eine Klasse auch mehrere Interfaces erfüllen kann,
kann ein Objekt zu mehr als einem Typ gehören.
Das heißt, dieses Exemplar hier gehört zum Typ Konto simpel,
wenn Konto simpel ist eine Klasse.
Die Kontosimple ist eine Klasse, also ist es ein Typ.
Konto ist auch ein Typ. Und Konto simpel implementiert sowohl
den Typ Konto simpel, das ist einfach trivial,
aber auch den Typ Konto. Das heißt, mein Objekt,
das mehrere Typen oder ein oder auch eine Klasse,
die mehrere Typen zu mehreren Typen gehört.
Und das ist eben in der Objektorientierung ein bisschen spezieller.
Da muss man sich ein bisschen klar machen.
Aber das ist alles, glaube ich, sagen wir,
gut nachvollziehbar im Sinne von den Begriffen, die wir bis jetzt eingeführt haben.
So, dann muss ich jetzt, um das Ganze rund
zu machen, so ein paar Begriffe noch einführen.
Ein ganz wichtiger Begriff, wenn man mit Referenzvariablen umgeht.
Und deswegen war es mir so wichtig,
dass ich in den vorigen Wochen die Referenzvariablen so betont habe und immer gesagt habe,
ihr müsst das verstehen mit diesen Referenzvariablen,
weil sonst wird alles, was danach kommt,
sehr schwierig.
Wenn ihr tatsächlich sowas habt wie eine Referenzvariable von einem Klassentypen
oder von einem Interface-Typen
Zum Beispiel vom Typ Konto. Und Konto ist ja hier erst mal,
das ist einfach eine Variablendeklaration, wie sie irgendwo im Quelltext stehen kann.
Ich kann die ja auch genauso jetzt hier hinschreiben.
Ich kann also jetzt tatsächlich hier in meinem Projekt,
in der Direkteingabe hinschreiben, Konto
K. Zuerst, sage ich mal, hat die den Werdenall.
Kann ich so hinschreiben. Jetzt habe ich also eine Variable deklariert.
Die hat den statischen Typ Konto. Das heißt,
ich kann jetzt über diese Variable zum Beispiel aufrufen, K.1 zahlen.
Das wird nicht funktionieren jetzt, weil diese Referenz ja oft
kein Objekt vorweist. Das heißt, wenn ich die Methode
aufrufe,
dann kracht das
Keine Überraschung. Aber ich könnte jetzt rein,
der Compiler würde jetzt feststellen, k.einzahlen,
Konto ist der Typ, sehe ich statisch,
hat die Operation einzahlen, auszahlen und so weiter.
Und dann kann er eben prüfen, ob das passt.
Ich kann jetzt hier nicht zum Beispiel aufrufen, Konto,
ändere Farbe. Macht sich so viel Sinn bei einem Konto.
Und da kann der Compiler sofort statisch sehen, naja,
der Typ Konto hat das nicht, gibt eine Fehlermeldung.
Okay. Das ist erstmal ganz wichtig. Das ist also der statische Typ,
legt fest, was kann ich im Quelltext für Operationen hinschreiben
und der Compiler hilft mir dann und sagt, du,
du versuchst da gerade eine Methode aufzurufen,
die ist bei dem Typ
gar nicht definiert
Ja. Okay, das ist statisch. Also immer dieser Unterscheidung statisch,
dynamisch, ihr habt diese Unterscheidung in den letzten Wochen schon von mir gehört.
Statisch ist immer, man guckt auf den Quelltext drauf,
man sieht was, man kann etwas erkennen,
ohne dass das Programm ausgeführt werden muss.
Der dynamische Typ einer Referenzvariablen. Das ist dann,
worauf zeigt denn die Referenz zur Laufzeit? So,
jetzt kann ich hier an dieser Stelle ja einfach sagen,
K soll mal KS werden.
Also ich weise jetzt meiner Referenzvariablen K vom Typ Konto,
weise ich die Referenz zu,
die gerade auf das Konto
simpel zeigt
Das kann ich so machen. Und jetzt kann ich
sagen, K.1 zahlen. 20. Da sind,
glaube ich, jetzt momentan, was ist da drauf?
70 sind da drauf, also dürften dann hinterher 90 drauf sein,
wie ich das jetzt mache.
K.1 zahlen. So, jetzt sind da 90 drauf.
Wunderbar. Jetzt kann ich aber diese Referenzvariablen auch,
die Referenz auf K.A. zuweisen. Dieselbe Referenzvariable vom Typ Konto
verweist jetzt auf ein anderes Konto, auf Konto anders.
Andere Implementation,
aber auch ein Konto
Und wenn ich jetzt sage, K.1 zahlen,
dann gucken wir uns mal an hier,
hier, Gibsaldo, ist irgendwie 30. Das heißt,
wenn ich da jetzt K.1 zahlen, 20 Aufrufe,
sollten da 50 drauf sein.
Und wir gucken mal, hier, Gibsaldo, yo, ist 50.
Das heißt, ich habe über dieselbe Referenzvariable zwei verschiedene Konten manipuliert.
Ist jetzt für euch keine Überraschung, dass man das kann.
Aber spannend ist jetzt, dass es tatsächlich von verschiedenen Klassen ist.
Über dieselbe Variable, über dieselbe Referenzvariable,
vom Typ Konto, diesem Interface.
Über dieses Interface kann ich
die beiden komplett gleichartig benutzen
Okay. Das ist der dynamische Typ an der Variable.
Also wenn ich eine Referenzvariable habe,
dann kann ich den dynamischen Typ nur zur Laufzeit sehen.
Deswegen heißt der dynamisch. Dynamisch ist immer alles das,
was zur Laufzeit ist. Erstens weiß ich den überhaupt erst
zur Laufzeit. Und zweitens haben wir gerade gesehen,
selbst zur Laufzeit kann ich den dynamischen Typ ja noch ändern.
Erst war der dynamische Typ Konto simpel,
dann war der dynamische Typ Konto anders.
Konnte ich einfach ändern. Das Objekt, was dahinter steckt,
bleibt immer das Objekt einer bestimmten Klasse,
aber die Referenzvariable kann ihren dynamischen Typ ändern
So, und ein Detail will ich noch jetzt hier mit einbringen,
was irgendwie nochmal wirklich, was man sich klarmachen muss.
Ich habe jetzt hier zweimal, gerade live aufgerufen, K.1 zahlen.
K.1 zahlen. Und in dem einen Fall wurde die Einzahlenmethode aus
der einen Klasse aufgerufen und im anderen Fall die Einzahlenmethode der anderen.
Das sind verschiedene Methoden. Also ein Konto simpel gibt es
eine Methode Einzahlen, die sieht so aus.
Hier, das ist die Methode. In Konto anders gibt es
auch eine Methode Einzahlen, die sieht aber ganz anders aus.
Völlig anders implementiert
Aber hier habe ich die völlig gleichartig aufgerufen. Aber es
wurde tatsächlich dann dahinter den Kulissen eine andere Methode aufgerufen.
Je nachdem, welche konkrete Implementation dahinter steckt. Und das
ist genau der Punkt, wo wir jetzt unterscheiden können.
Und das machen wir erst jetzt. Früher haben wir
das früher gemacht, das hat nichts gebracht.
Bisher haben wir euch immer gesagt, alles,
was da so in den Klassen drinsteht, sind Methoden.
Und auch in so einem Interface stehen Methodenköpfe drin.
Jetzt sind wir ein bisschen genauer. Jetzt unterscheiden wir Methoden und Operationen.
Ich habe ja immer gesagt, ein Typ definiert Operationen.
Das heißt, der Typ Konto, das Interface-Konto,
definiert die Operationen, Einzahlen auszahlen,
gibt Salon
Und diese beiden Klassen, die dieses Interface implementieren, die haben wirklich konkrete Methoden.
Also immer wenn wir über eine Methode reden, zukünftig,
dann ist das so ein Ding,
was wirklich im Rumpf einer Klasse steht und wirklich eine Rumpffahrt.
Also wirklich, da steckt Code drin, da stehen Anweisungen im Rumpf einer Methode.
Dann ist es eine Methode. Wenn es sowas ist,
was in so einem Interface steht, wo eigentlich kein Rumpf dabei ist,
wo nur die Idee dabei ist, das soll die Operation sein,
die der Typ anbietet, dann ist es eine Operation.
Und was wir jetzt einfach begrifflich sehr gut sagen können,
ist, es gibt eine Operation, sowas wie Einzahlen,
Aber die kann mit zwei verschiedenen,
es können auch noch mehr Methoden sein,
es kann ja mehrere Implementationen noch geben.
Mehrere Methoden, die alle dieselbe Operation implementieren. Also eine Operation,
mehrere Methoden. Dazu gibt es noch mehr Beispiele in SE1.
Also das ist jetzt hier harter Tobak, sehr viel Theorie.
Das werdet ihr aber in den nächsten Wochen immer noch mal
wieder zu sehen bekommen. Gut, dann muss ich das
jetzt noch mal so ein bisschen abschließen hier das Thema.
Zum einen ist es so, dass ich habe eben schon gesagt,
wenn ein Exemplar in der Klasse erzeugt wird,
dann bleibt es immer ein Exemplar seiner Klasse
Also Java-Objekte vergessen niemals ihre Klasse. Jedes dynamisch erzeugte,
teilweise Objekt ist ein Exemplar von genau einer Klasse.
Das ändert sich nicht. Ich kann tatsächlich aber,
wenn ich jetzt dieses Spiel hier habe, mit diesen Referenzen,
hier habe ich jetzt ja die Variable K,
und jetzt möchte ich gerne den dynamischen Typ rausfinden.
Den statischen Typ kenne ich,
den gucke ich in den Quelltext rein, sehe ich K,
ist vom Typ Konto, kann man ja direkt ablesen.
Aber wenn ich jetzt zur Laufzeit rausfinden will,
worauf verweist denn jetzt gerade diese Referenzvariable?
Dann kann ich tatsächlich sagen, ist das ein Instance of Konto.
Frei erstmal kontosimpel. Wir haben gerade die Referenz auf Konto anders zu gewesen.
Also Car Instance of Konto simpel, kann ich jetzt einfach fragen.
Das ist ein, dieses Instance of ist ein Infix-Operator,
wo ich eine Referenz variable und einen Klassennamen angeben kann.
Und dann kann ich feststellen, hey, verweis du auf
ein Objekt dieser Klasse. Und dann sagt er hier,
Fords. Ist ja kein Exemplar der Klasse Konto simpel,
sondern wir wissen, K ist ein,
verweist momentan mit seinem,
die Referenz verweist auf Konto anders.
Da müsste jetzt Schuhe rauskommen
Das heißt, der dynamische Typ ist ermittelbar mit solchen Instance auf Abfragen.
Hier kommt True raus. Wunderbar. Wenn ich frage,
er fragte, was ist, wenn ich jetzt nur nach Konto frage?
Car, Instance of Konto. Kann man ja auch fragen
. Ist nicht so sinnvoll, weil,
also jetzt im Quelltext wäre das in einer Klasse,
wäre das nicht so sinnvoll, weil man ja sehen kann,
von welchem Typ K deklariert ist.
Aber man kann das trotzdem fragen. Ist das ein Typ
, ist es eine Instance of Konto?
Ja, das ist auch. Das ist also eher eine
Frage, ob es von dem Typ ist
Aber das ist tatsächlich eine gute Frage, die man an
der Stelle stellen kann. Okay, das lässt sich damit
prüfen. Andersrum. Ihr habt eben möglicherweise mitbekommen,
ich habe hier gesagt, dieses klasse Konto anders,
die hat eine Methode hier unten, gib Anzahl Buchungen.
Das heißt, in der Schnittstelle, wenn ich mir
jetzt hier ein Konto anders mir die Schnittstelle anzeigen lasse,
na komm schon.
Maus. Dann steht hier eine Methode,
gib Anzahl Buchungen, steht in der Schnittstelle dieser Klasse
Ja, aber über diesen Typ Konto steht das Ding nicht zur Verfügung.
Obwohl jetzt K auf ein Exemplar von Konto anders verweist,
kann ich nicht aufrufen, K,
jetzt muss ich mal ganz kurz gucken,
wie ist das Ding jetzt hier, diese komische Methode?
Gib Anzahl Bochum, okay. Jetzt könnte ich ja
hier aufrufen, K, gib Anzahl Buchungen. So,
ich weiß ja, K verweist auf ein Konto anders.
Das Ding hat diese Methode
Aber das, was ich hier gerade hinschreibe, ist ein Operationsaufruf.
Und ein Operationsaufruf wird immer geprüft, ob er zu dem statischen Typ hat.
Der statische Typ ist Konto. Und Konto hat nicht
diese Methode oder diese Operation, gib Anzahl Buchung.
Da kommt hier eine Fehlermeldung, cannot fine simple,
method gib Anzahl Buchung. Es gibt aber einen Trick,
wie man da jetzt wieder rankommt, wie ich sozusagen jetzt wieder,
ich weiß vielleicht, irgendwie zur Laufzeit,
dass meine Referenz war ja BK vom statischen Typ Konto auf ein Konto anders verweist,
dann kann ich mit einer sogenannten Typ-Zusicherung das Ding wieder rausholen.
Ich kann sozusagen dem Compiler sagen, hallo, ich weiß genau,
dass es ein Konto anders ist und lass mir die Referenz jetzt mal als Konto anders Referenz geben.
Das sieht dann so aus, ich kann hierhin schreiben,
Konto anders, KA2, also einfach nur,
damit das jetzt nicht in Konflikt kommt mit der anderen Variablen da,
links, Konto anders, KA2, soll jetzt,
ja, jetzt sage ich hier, Konto anders.
Das sieht so ein bisschen aus wie diese Typumwandlung,
die ihr gesehen habt bei den primitiven Typen,
weil da auch so eine Klammer davor steht mit einem Typ da drin,
sieht so ähnlich aus,
wird auch gerne Typecast genannt,
sind zwei
völlig verschiedene Dinge
Also bitte nicht verwechseln, das hier nennen wir hier sehr bewusst, ist eine Typzusicherung.
Ich sage dem Compiler, K vom Stück, vom Typ Konto,
interpretiere das mal als etwas vom Typ Konto anders.
Kann ich so machen. Und jetzt kann ich tatsächlich über KA2,
vom Typ Konto anders, diese Methode, warte mal,
ich kann jetzt hier anders machen, da gib Anzahl Buchungen,
dann kann ich jetzt hier KA2 schreiben und der gibt mir dann tatsächlich was raus.
Da sind also jetzt schon zwei Buchungen drauf gelaufen.
Also diese Typinformationen kann ich wiederkriegen mit
so einer Typzursicherung
Wird das dann schon bei dieser Umwandlung erkannt? Könnt ihr laut Zeit,
wenn ich jetzt was rufen kann, was das geht?
Ah, okay, er fragt, was ist denn,
wenn diese Typumwandlung zur Laufzeit eventuell schiefgeht? Weil es könnte ja sein,
dass ich eine Referenz habe auf dem Konto simpel und dann mache ich mit einer Zugzusicherung,
sage ich, mach mir mal ein Konto anders drauf.
Das wird zur Laufzeit fehlschlagen. Das heißt,
wer sowas macht, wer soll eine Typzusicherung hinschreibt,
sollte genau wissen, was er tut,
weil er den Compiler sagt, Relax, ich weiß,
was ich tue und wenn das in die Hose geht,
gibt es eine Acception zur Laufzeit.
Dann sagt er, du versuchst gerade
ein Konto simpel als ein Konto anders zu benutzen,
das kann ich nicht zulassen
Da gibt es eine Exception in Java. In Java ist
das sehr sicher. Okay, hier zur Vollständigkeit nochmal,
wie denn Interfaces in der UML dargestellt werden. Ja,
also man schreibt das üblicherweise hier mit so einem Stereotype.
Das sieht man auch hier in dem Bluejay-Projekt. Hier sieht man
das, genau. Also Interface mit diesen spitzen Pfeilen,
links und rechts. Das nennt man einen Stereotype,
also sozusagen eine Annotation, wie man etwas genauer benennen kann,
typisieren kann. Und keine Attribute, also keine Exemplarware,
wir haben keine Felder, gibt es ja nicht, aber natürlich Methoden,
in dem Sinne eigentlich jetzt Operationen, die hier stehen.
Und dann kann es Klassen geben, die dann diese Dinge implementieren,
das ist die Realisierung oder die Implementation.
Das ist tatsächlich der Typ von Pfeil, der in der UML dafür verwendet wird.
Hier ist eine alternative Darstellung nicht ganz so wichtig.
Gut. Ja, das war erstmal das, was ich
zu Interfaces erzählen wollte. Das ist, wie gesagt,
das ist erstmal neu, es ist erstmal anders,
möglicherweise auch nicht so ganz leicht nachzuvollziehen, aber ich glaube, es ist bewältigbar.
Da ist eine Frage. Wir haben ein Interface,
aber mehr Schnittstelle eigentlich als eine der Klassen,
auch die er jetzt vorweist. Und das geht ja auch,
da ist das so ein Wertschütschermann, als es Interfaces umgestellt,
aber er wird abgeprüft die waren ja auch auf der Sita-Face oder alle,
Schlüssel, die haben das angezogen.
Ja, das ist eine sehr gute Frage.
Er sagt, man hat gerade gesehen, dass eine Klasse
mehr Methoden anbieten kann, als das Interface fordert.
Wenn eine Klasse sagt, ich implementiere das Interface,
da sind drei Operationen. Ich habe vier Methoden hier.
Geht. Was ist, wenn das andersrum ist?
Wenn in dem Interface vier Operationen gefordert sind und eine Klasse
liefert weniger. Die Antwort ist, das geht nicht.
Der Compiler meckert. Der Compiler sagt, du behauptest,
du implementierst das Interface, dann musst du auch wirklich für
alle Operationen eine Methode haben.
Gibt es sonst noch Fragen dazu
Cool. Dann ist ja alles klar. Hier ist das
nochmal zusammengefasst. Trennung von schnittsche Implementation, zentrales Konzept.
Wenn man erstmal sich darauf einlässt, das zu trennen,
dann kann man sagen, eine Schnittstelle kann verschieden implementiert werden.
Das kann man mit Interfaces beschreiben. Dieses Konzept einer Schnittstelle,
dann kann man da auch verschiedene Klassen haben, die das implementieren.
Dann werden Interfaces zu Spezifikationen, die vorschreiben,
wie etwas sein soll. Und wenn wir mit Interfaces umgehen,
dann kommen wir plötzlich in die Situation,
dass wir zwischen dem statischen und dem dynamischen Typ einer Referenzvariable
unterscheiden müssen.
Kleine Storesi-Frage
So, ich hoffe, ihr könnt das sehen jetzt.
Ich hoffe, das funktioniert. Ich bin im Netz, soweit ich weiß.
So, welche der folgenden Aussagen gilt für diesen Quelltext,
wenn er von einem Jabba-Compiler als korrekt akzeptiert wird?
Also hier, Public Void M, Konto K,
K.1 zahlen 100 und dann Into Saldo ist gleich K.,
Gib Saldo.
Das stehen hier vier Aussagen und eine davon ist nur richtig.
Also Aussage A ist,
der Typ-Konto muss drei Operationen definieren,
einzahlen,
auszahlen und Gibsaldo
Aus AGB, der dynamische Typ des formalen Parameters, ist hier nicht ersichtlich.
Aussage C, Konto kann kein Interface sein,
da die Operation einzahlen, gerufen werden kann.
Aussage D, Konto muss eine Klasse sein,
da beim Aufruf von M ein Objekt als Parameter übergeben wird.
So, jetzt müsst ihr euch entscheiden für eine dieser Aussagen.
Nur eine ist richtig
Mein Vorschlag, ich denke noch in Ruhe drüber nach,
wenn ihr euch entschieden habt,
geht in die Pause und nach der Pause lösen wir das
dann auf.
Okay,
also jetzt fünf Minuten Pause
Untertitel der Amara.org Untertitel der Amara.org Untertitel der Amara.org Untertitel
der Amara.org
Untertitel der Amara.org
Okay, juhu. Die fünf Minuten sind um.
Bitte langsam wieder auf die Plätze begeben. Und ja,
jetzt gucken wir mal hier drauf. Also 135 Stimmen sind
erst abgegeben worden. Die Beteiligung ist noch recht zögerlich.
Vielleicht kommen wir noch auf 150 hier, aber ich glaube,
es wird jetzt nicht mehr viel mehr werden.
Ich halte es mal an. Okay,
wir gucken erst mal auf die Verteilung
Da sagen immerhin 55 Prozent der dynamische Typ ist hier nicht ersichtlich.
Sagen aber auch immerhin noch so 27 Prozent,
also ungefähr ein Viertel sagen, Konto muss eine Klasse sein,
da ja tatsächlich hier beim Aufrufen von M ein Objekt als Parameter begeben wird.
Und immerhin auch noch 14, also ein Siebtel von euch sagt irgendwie,
Konto kann kein Interface sein, da ja die Methode dran aufgerufen wird.
Ja, die richtige Antwort ist tatsächlich hier. Also das heißt,
die Mehrheit von euch hat die richtige Antwort gewählt.
Was ist denn hier die Situation? Das ist ja im Grunde
genommen genau das, was ihr eben schon gesehen habt.
Bei dem Überweiser war es ja auch so,
der Überweiser Hat im Quelltext auch Konto K benutzt und hat dann auch an diesem Cavers aufgerufen.
Also dieser Aufruf, dass das möglich ist, sagt nicht,
dass es was anderes sein muss als ein Interface.
Tatsächlich ist es so, dass man jetzt nicht weiß,
ob das, also bezogen auf das Beispiel,
was ich ja gerade gesehen habe,
das kann ja ein Konto simpel übergeben werden oder ein Konto anders.
Der dynamische Typ ist hier nicht ersichtlich. Der ist auch
hier egal. Für den Klienten ist es vollkommen unwichtig,
was für einen dynamischen Typ das Ding hat.
Hauptsache, es verhält sich wie ein Konto.
Gut, das zu diesem kleinen Storesi-Test
Dann kommen wir jetzt zu einem ganz anderen Thema, könnte man meinen.
Aber tatsächlich hängt das alles jetzt sehr eng zusammen,
was ich heute erzähle. Wir gucken uns jetzt mal das
Thema an, wie man denn systematisch Software testen kann.
Und bevor ich jetzt mit Theorie anfange, zeige ich wieder
ein bisschen Praxis. Hier haben wir den Überweiser. Den
habt ihr ja mal irgendwann auch tatsächlich implementiert in den Übungen.
Und wenn ihr den tatsächlich auch vernünftig implementiert habt,
das gehört zur Softwareentwicklung dazu, dass man nicht nur was implementiert,
sondern auch,
dass man es testet
So, wie würdet ihr denn jetzt dieses Ding hier testen?
Also wenn ich jetzt hier mal das ganze Zeug wegschmeiße.
Wie würdet ihr denn jetzt den Überweiser testen wollen?
Wie würdet ihr testen, ob der Überweiser funktioniert?
Was würdet ihr machen? Macht mal so ein paar Vorschläge,
wie man feststellen kann, ob der Überweiser das Richtige macht.
Bitte? Das Testbahnschreiben, wo man alle möglichen auch die Möglichkeiten,
die Nutzung, die wir weiter aufschreiten, uns der Pascal durchgehen,
was es da macht, wo könnte über den Fehler stehen.
Okay, das ist... Okay, das ist ein guter
Vorschlag. Er sagt, man macht sich einen Testplan,
das heißt, man bewegt sich Step by Step,
wie man den Überweiser benutzen würde und wo was schiefgehen kann.
Das ist schon mal ein ganz guter Ansatz.
Also ich habe hier ja die Schnittstelle von dem Überweiser,
kann man es mal angucken. Hier ist die Schnittstelle,
der hat nur eine Methode, die heißt Überweisen.
Kontoquellkonto, Konto, Zielkonto in Betrag. Wie könnte ich denn jetzt
Wie kann ich denn jetzt feststellen, ob der richtig funktioniert?
Wie würde ich das jetzt konkret machen? Also die Idee mit
dem Testplan ist gut, aber wie können wir das konkretisieren?
Ja, da ist ein Vorschlag? Nein,
ich würde überweisen, und quasi jede Möglichkeit,
von der ich da drei Variablen übergeben und Fehllichkeiten wieder bewege,
die ein paar Jahre übergehen können. Ihr müsst heute,
dann möchte ich den Kopf da in das Richtige,
was ich für das Körper brauche, und zwar,
hol jetzt, wir konnten auch nicht gegeben haben,
dass er von seinem Gehma auf den Boden getragen hat,
der die ganzen Betrag haben,
das erste 7,
der Verkehrskontrollist geliebt hat
Okay, genau, er hat gesagt, was man dann tut,
ist, man ruft hier diese Methode überweisen auf,
mit verschiedenen Belegungen für die Parameter.
Also Quellkonto ist vielleicht nahe, Zielkonto ist vielleicht null Betrag,
ist positiv, ist negativ, also man kann da verschiedene Varianten sich überlegen,
wie man das Ding aufruft.
Okay, aber wie stelle ich jetzt fest,
ob das Ding wirklich das Richtige macht? Nehmen wir mal an,
ich überweise jetzt, mache eine ganz vernünftige Benutzung,
also überweise von einem gültigen Quellkonto auf ein gültiges Zielkonto,
30 Euro.
Wie stelle ich denn jetzt fest, ob das funktioniert hat?
Wie kann ich das
denn überprüfen
Ja. Exemplare ist heute, ich habe das ausprobiert.
Okay, indem ich Exemplare erzeuge und es ausprobiere, genau. Das heißt,
ich erzeuge mir Exemplare, die ich dann ja übergeben muss,
ich muss ja Kontoexemplare übergeben, die erzeuge ich mir selber.
Ich weiß dann ja, in welchem Zustand diese Kontoexemplare sind,
dann übergebe ich die Referenzen auf diese Konten,
lass den Überweiser was überweisen und hinterher kann ich ja dann prüfen,
ob das geklappt hat.
Ich kann ja diese Konten,
die ich ja selber erzeugt habe,
abfragen,
ob die Saldi dann stimmen
Ah, gut. Das ist cool. Ich zeige euch jetzt,
wie man in Blue Jay sowas systematisch machen kann.
Das ist jetzt etwas, was hier neu dazukommt.
Ich kann jetzt im Interface von Blue Jay mir Testwerkzeuge anzeigen lassen.
Jetzt hat man hier tatsächlich neue Schaltflächen und ich kann jetzt
tatsächlich auch bei der Klasse hier über Weiser,
für die ich jetzt einen Test schreiben will, kann ich jetzt sagen,
hier, jetzt zeig mir da mal eine Testklasse.
Das ist also ein neuer Menüpunkt, der jetzt hier aufgetaucht ist.
Testklasse erzeugen. Zack, habe ich eine Testklasse.
Diese Testklasse ist erstmal nur eine leere Klasse
Die ich jetzt hier mir mal angucken kann, die sieht
relativ harmlos aus, da steht ein bisschen Zeug drin,
was wir noch nicht verstehen, also irgendwelche Imports,
sage ich gleich noch was dazu.
Und dann hat man hier einen Testfall. Eine Methode,
die habe ich jetzt hier, Teste, Testfall 1 genannt.
Und naja, darin kann ich jetzt meinen Test schreiben.
Also genau wie wir es gerade beschrieben haben,
ich erzeuge mir einfach ein paar Exemplare, zwei Konten und dann,
also so, das will ich jetzt hier nicht alles schreiben.
Ich habe das zufällig schon vorbereitet. Hier habe ich mir
mal so einen Testfall gebaut. Einen Testfall jetzt hier für
Jen Unit, das ist die Software, die dahinter steckt.
Da ist ja gleich noch ein bisschen mehr dazu
Ich gehe also jetzt hier in den Quelltext rein und paste das
hier einfach rein. Dieser Testfall wird hier jetzt eingefügt.
So, Teste überweisen. Wie sieht der Testfall jetzt aus
? Okay, ich erzeuge mir erstmal ein Konto simpel.
Warum nicht? Dann zahle ich 100 Euro ein.
Kann man machen. Dann erzeuge ich ein zweites Konto und da
sind dann null Euro drauf oder null, wie auch immer.
Das weiß ich. So, jetzt mache ich mir ein
Überzeug, Überweiser. Und dann rufe ich jetzt Überweisen auf.
Das ist der entscheidende Punkt. Das will ich ja testen
. Das ist das Ding, was ich testen will.
So, also ich überweise von Konto 1 auf Konto 2 30.
Und hinterher kann ich jetzt ja testen, besten,
wie der Zustand der Konten sein sollte.
Erstens weiß ich, bei Konto 1 sollte jetzt der Saldo
70 sein und bei Konto 2 sollte der Saldo 30 sein.
Das heißt, ich vergleiche das, was Gibsaldo mir liefert,
das ist das, was da rauskommt, das ist das,
was das Ding wirklich tut, mit dem Wert,
den ich mir überlegt habe.
Ich sage hier, da müsste eine 70 rauskommen.
Also die Erwartung wird abgeglichen mit dem, was wirklich geliefert wird.
Schon haben wir hier einen wunderbaren Testfall. Den habe ich
natürlich jetzt vorbereitet, aber es war das,
was eigentlich der Kommilitone gerade vorgeschlagen hat.
Wir erzeugen uns Konten und machen da mal was und gucken
dann, ob das auch genau so ist
So, jetzt können wir mal sehen, ob das funktioniert.
Schließen, übersetzen. Und das Schöne ist,
ich kann jetzt tatsächlich diesen Test einfach ausführen.
Ich kann jetzt hier sagen, Teste überweisen,
also eine Testmethode auswählen oder ich kann einfach sagen, alles testen.
Alles testen würde, wenn da jetzt mehrere Testmethoden drin stünden,
würden die alle ausgeführt werden. Also hier in dem Fall
alle eine. Dann erscheint hier so ein Fenster,
das ist eine Ausgabe, die von J. Hudith erzeugt wird
und die in Blue Jay nur angezeigt wird.
Und der sagt hier, der Testfall überweiser Test überweisen,
grüner Pfeil, also grüner Haken hat geklappt.
Also es ist nicht fehlgeschlagen
Okay, das heißt, das Gute ist,
jetzt habe ich hier diesen Testfall ausprogrammiert.
Und ich kann dir jetzt immer wieder ausführen. Ich kann dir
jetzt hier sagen, Test starten, zack, auf einen Knopfdruck.
Und wenn ich da jetzt noch mehrere solche Testmethoden hätte,
ich kann ja verschiedene Testfälle mir überlegen. Da war eben der Vorschlag,
ich könnte ja auch testen, was passiert,
wenn einer der Parameter nall ist oder der Betrag negativ ist.
Alles mögliche Testfälle, die könnte ich jetzt alle hier so runterprogrammieren und dann auch ausführen.
Das Schöne ist, ich kann diesen Test ja hier,
diesen Test kann ich ja schreiben,
bezogen auf das Interface,
hier auf dieses Interface
Und kann das also immer wieder verwenden,
ohne jetzt zu wissen, wie da die konkreten Implementationen sind.
Okay. Diesen Testfall hier, gucken wir uns gleich nochmal genauer an.
Was ihr möglicherweise eben, als ich den Quelltext gezeigt habe,
das können wir mal zumachen hier, das sind nur diese Readme-Datei,
kann weg.
Als ich euch den Quelltext von dem über Weiser gezeigt habe vorhin,
da habt ihr möglicherweise nicht gesehen,
dass da ein Fehler in der Implementation ist
Da ist ein Fehler drin. Ich sage jetzt nicht,
wo er ist, aber ihr könnt möglicherweise gleich dann,
indem wir systematisch dieses Testen weitermachen, werden wir den Fehler finden.
Jetzt erzähle ich jetzt mal ein bisschen was zum Testen allgemein.
Also warum überhaupt testen? Naja,
es gibt verschiedene Gründe, warum man testen sollte,
ein prominentes Beispiel ist, hier dieser Absturz der Ariane,
da habe ich den Link mal wieder ein bisschen korrigiert.
Der Link, der im Skript steht, ist nicht ganz aktuell,
aber da wird man zumindest weitergeleitet. Die Ariane 5,
das war die vor fast 20 Jahren explodiert ist nach dem Start.
Das war eine relativ teure Explosion. Also irgendwie eine Milliarde D-Mark,
also das war schon relativ teuer. Passiert ja öfter mal,
dass so Raketen starten und dann kaputt gehen.
Da war es eben halt ein prominentes Beispiel. Und was das Entscheidende war,
in dem Untersuchungsbericht, der auch im Netz zu finden ist,
stand dann drin, wenn man die Steuerungsautomatik besser getestet hätte,
hätte man den Fehler finden können.
Also es war ein Softwarefehler, wenn man den früher gefunden hätte,
bevor das Ding in die Luft geht,
also bevor das Ding in die Luft geht,
dann hätte man vermeiden können,
dass es in die Luft geht
Okay, entschuldigt das Wortspiel. Was ist noch vertesten?
Na gut, Zwarfeln testen ist das Ausführen von Code,
um Fehler zu finden. Haben wir gerade gesehen,
ich möchte eigentlich bestimmte Eingaben reingeben und dann Ausgaben erwarten und
dann feststellen, ob da irgendwelche Fehler dabei sind.
Das sind irgendwelche Definitionen nicht so wichtig. Was ist Testen für uns
jetzt erstmal, eine Qualität, eine Maßnahme zur Qualitätssicherung?
Wir wollen bessere Qualität haben, indem wir uns absichern,
dass die Software wirklich die Funktionalität liefert.
Testen kann zum Aufzeigen von Fehlern dienen
Aber sie kann nicht die Korrektheit von Software nachweisen.
Das ist so ein klassisches Zitat, das hier auch auf
der Folie draufsteht, von dem guten Herrn Deixtra.
Program Testing kennt at best show the presence of errors, but never they appsites.
Der gute Herr Deixtra hat damals gesagt, es ist viel besser,
wenn wir beweisen, dass unsere Software korrekt ist.
Der war ein sehr engagierter Mensch, was Softwarebeweise angeht. Ich würde mal sagen,
das Thema ist heutzutage nicht mehr ganz so prominent,
aber damals war das noch ganz wichtig und er hat damit
quasi die ganze Informatik erst mal so ein bisschen auf einen bestimmten Fokus gesetzt und dann mehr oder weniger das Testen als Disziplin bis kreditiert.
Er hat gesagt, das bringt sowieso nichts
Ja, aber deswegen sollte man es auch gar nicht machen,
mehr oder weniger. Da gibt es andere Leute,
die dann auch sehr prominente Leute, also der Herr Knuth,
ja mal irgendwie mit Lattech gearbeitet hat, der weiß,
dass Tech, wo Lattech drauf basiert,
von Herrn Knuth entwickelt wurde,
Beware of Barks in the Above Coda have only proved it correct not try it.
Das war in der Zeit, in den 60er Jahren,
als ganz viele Programmbeweise veröffentlicht wurden und dann dummerweise in diesen Programmbeweisen Fehler drin waren.
Super, wenn ich beweise, dass etwas korrekt ist,
aber der Beweis ist fehlerhaft. Das nützt auch nicht so viel.
Also Testen ist etwas, was zum soliden Arbeiten dazugehört,
das geplante und strukturierte Ausführen von Programmcode und Probleme zu entdecken sollte.
Selbstverständlicher Bestandteil sein. Also, dass man einfach das,
was man macht, auch testet. Und das Beste ist,
was man machen kann, ist, dass die Tests automatisiert
und wiederholbar sind, was wir gerade gemacht haben.
Als erstes Beispiel war so ein Testfall auszuprogrammieren, also hinzuschreiben,
nicht einfach nur interaktiv das zu machen, um mal zu testen,
sondern das zu programmieren und dann jederzeit schnell wiederholen zu können.
Gut, diese Frage, ob Software korrekt ist,
ist sowieso sehr, sehr schwer zu beantworten.
Erstmal ist der Korrektheitsbegriff in der Informatik
ziemlich klar definiert
Eine Software ist korrekt, wenn sie ihre Spezifikation erfüllt.
So, das heißt, Voraussetzung ist erstmal, dass man
eine Spezifikation hat. Ganz oft ist es so,
dass man keine gute, klare Spezifikation hat,
sondern die Anforderungen sind eher nebulös in den Köpfen von Kunden.
Das ist in der Softwareentwicklung ganz häufig so. Das heißt,
selbst wenn man eine gute Spezifikation hat, die sehr exakt beschreibt,
was gemacht werden soll, ist die noch nicht formal.
Also wenn sie natürlich sprachlich ist,
wie gerade bei unserem Überweiserbeispiel,
das ist eine
natürlich sprachliche Spezifikation
Selbst wenn man eine formale Spezifikation hat,
ist es da auch immer noch sehr schwierig,
mathematisch zu beweisen, dass ein Programm diese Spezifikation erfüllt.
Das ist eines der harten Probleme der Informatik tatsächlich.
Also, und selbst wenn man das formal nachweisen könnte,
ist ja immer noch die Frage, ist denn die Spezifikation korrekt?
Also ist sie valide? Ist sie nützlich, ist sie sinnvoll?
Diese Formalbegriff ist erstmal für sich genommen nicht viel wert. Okay,
also gerade auch bei interaktiver Software, wo Menschen beteiligt sind,
das kann man eben halt nicht gut formalisieren,
den Umgang wie Menschen mit Computern arbeiten
Entsprechend ist das auch mit formalen Korrektheitsbeweisen einfach nicht weit her für interaktive Software.
So, also für uns einfach, ja,
habt den Mut zu sagen, ja, wir testen,
wir lassen uns nicht ins Boxhorn jagen von Herrn Daxtra.
Wir machen das trotzdem, weil wir das Vertrauen in
die Software erhöhen wollen. Also wenn wir systematisch testen,
haben wir mehr Vertrauen darin, dass die Software gut ist.
Und die Nützlichkeit an der Software zeigt sich sowieso,
gerade bei interaktiver Software erst im Gebrauch.
Und dann, so, jetzt wollen wir also,
wir sind jetzt total engagiert, wir sind total begeistert.
Wir sagen, ja, wir wollen testen.
Das hat aber auch ziemlich viele Typen.
Also ihr werdet merken,
testen ist nicht einfach
Vernünftig testen wollt, dann muss man sehr, sehr systematisch rangehen. Also erstmal ist das einfach schwierig,
teilweise auch einfach technisch schwierig, so eine Oberfläche wie Blue Jay hier,
wenn ich jetzt testen will,
ob ich jetzt hier zum Beispiel bearbeiten, neue Klasse,
dann kommt hier so ein Dialog und ich gebe hier einen Klassennamen ein und wenn ich jetzt das möglich mache,
dann möchte ich ja garantieren,
dass hinterher auch in meinem Diagramm diese Klasse auftaucht mit diesem Namen.
Das ist sehr schwer zu testen,
also ich kann das natürlich als Mensch,
kann ich das testen, ich kann Menschen engagieren,
die den ganzen Tag nichts anderes machen,
als diese Software durchzuklicken und das alles auszuprobieren.
Man hätte das aber auch gerne automatisiert,
ist sehr schwierig, grafische Oberflächen automatisiert zu testen.
Es gibt da diverse Ansätze, ist aber ein kompliziertes Thema.
Testen braucht Zeit. Das heißt,
wenn ihr in einem Softwareprojekt arbeitet, dann ist das Erste,
was wichtig ist, immer die Funktionalität, die gefordert wird.
Also der Kunde sagt, das und das muss gebaut werden,
das müssen wir bauen.
Wir sind unter Zeitdruck, Zeit ist immer knapp in Projekten.
Also bauen wir zuerst mal die Funktionalität testen. Ja,
schaffen wir nicht, können wir nicht, kriegen wir nicht hin.
Es ist keine Zeit. Fällt den meisten Projekten dann irgendwann auf die Füße,
wenn man systematisch Tests von Anfang an mit einbezieht,
je früher man testet,
desto besser
Und wenn man dann ausführbare Tests hat,
dann kann man die ja sehr leicht auf Knopfdruck ausführen
und kann eben halt das auch schnell wiederholen.
Aber es kostet halt Zeit, die zu schreiben.
Tests müssen gut vorbereitet sein. Wir brauchen einen Testplan,
wurde schon gesagt. Also man muss einen Plan haben.
Und Tests müssen wiederholt werden. Also gerade diese ausprogrammierten Tests sind natürlich ideal dafür.
Man kann aber natürlich auch einfach einen Test planen,
aufschreiben für einen Menschen im Sinne von,
bitte lege hier was an, bitte mache dann Folgendes.
Diesen Testplan kann man aufschreiben, kann das Menschen immer wieder
ausführen lassen, wie kann man dafür bezahlen.
Wiederholbar sollte das auf jeden Fall sein
Und dann gibt es aber auch neben den technischen Problemen psychologische Probleme.
Also wenn ihr Software, die ihr selbst geschrieben habt,
testet, dann neigt ihr dazu, das zu testen,
wo ihr wisst, dass es funktioniert.
Und testen es ganz stark beeinflusst durch die Programmiererfahrung.
Ein guter Programmierer, ein erfahrener Programmierer, testet sehr viel besser.
Das ist empirisch nachgewiesen. Klar, wenn ihr Programmieranfänger seid
und gerade das Programmieren lernt, kann man nicht erwarten,
dass ihr schon supergut darin seid, Testklassen zu schreiben.
Ein häufiges Problem beim Testen ist,
dass man nur positiv testet. Was heißt das,
dieses nur positiv testen
Bei der, in der Theorie über Testfälle unterscheidet man zwischen Positivtests und Negativtests.
Positives Testen heißt, ich halte mich an die Spezifikation,
wenn ich einen Testfall formuliere. Das heißt,
unser Testfall hier, den wir hier geschrieben haben,
der hält sich an die Spezifikation.
Wenn ihr euch daran erinnert, die Spezifikation von dem Überweiser sagte,
beide müssen ungleich nall sein bei der Referenzen.
Der Betrag muss größer als null sein,
aber er muss auch kleiner sein als der Saldo
des Kontos,
von dem abgebucht wird
Alles das haben wir hier eingehalten. Also wir haben hier
zwei Referenzen erzeugt auf zwei Objekte und machen hier eine Überweisung mit einem Betrag,
der kleiner ist als der Saldo und größer ist als null.
Wunderbar. Das nennt man einen Positivtest.
Wir halten uns an die Spezifikation und prüfen,
ob die Software das auch wirklich dann an der Stelle macht.
Ich verhalte mich korrekt als Klient und erwarte das dann auch das korrekte Ergebnis geliefert wird.
Das ist ein Positivtest. Negativtest wäre jetzt,
was ja vorhin schon vorgeschlagen wurde, ich teste,
was passiert, wenn ich Eine Referenz als Nallparameter übergebe.
So der Fall so das Konto von dem abgebucht wird,
ist nicht Nall, das Konto auf das eingezahlt wird,
ist Nall. Dann wäre es ziemlich blöd,
wenn von einem Konto was abgebucht wird und erst dann festgestellt wird,
oh, es ist eine Nahreferenz,
jetzt kann ich das Geld nirgends mehr hinüberweisen,
es ist weg.
Von dem Konto runter und beim anderen nicht drauf.
Das wäre ziemlich blöd. Wenn jemand sowas macht,
hält er sich nicht an die Spezifikation.
Also ein Klient, der den Überweiser so benutzt,
verhält sich falsch
Aber man kann natürlich bewusst etwas falsch machen als Klient,
um zu testen, ob der Dienstleister denn auch robust ist.
Robust im Sinne von, wenn ich den jetzt falsch benutze,
dann möchte ich ja nicht, dass dann das Geld einfach verpufft.
Also auch für den Fall möchte ich gerne,
dass das Ding irgendwas Vernünftiges tut.
Und das ist genau die Unterscheidung zwischen Positivtests und Negativtests.
Bei Positivtests, da werden nur gültige Eingabewerte zugelassen.
Und wenn ich unerwartete, ungültige Eingabewerte verwende, dann ist das negatives Testen.
Mit positiven Tests erhöhe ich das Vertrauen in die Korrektheit.
Das Ding macht ja wirklich das,
was es machen soll
Und mit Negativtests stelle ich halt fest, ist das Ding
auch robust. Also ist es auch in dem Fall,
wenn ich es falsch benutze, immer noch so,
dass das zumindest mit einer vernünftigen Fehlermeldung rauskommt oder einfach irgendwas macht.
Das wäre schon gut. Gut,
statische und dynamische Tests sind einfach zwei Begriffe,
ganz schnell abgehakt, statische Tests sind alle die Tests,
die man auf dem Quelltext macht.
Man kann, man liest den Quelltext oder man analysiert einfach,
ob da alles richtig ist, was da deklariert wird.
Da gibt es also verschiedene Werkzeuge. Menschen können das machen
. Und dynamische Tests sind alle Tests,
wo wirklich der Code ausgeführt wird
Wirklich, was wir gerade gesehen haben, so ein Jer-Unit-Test,
ist genau so ein Test, der ausgeführt wird und
dann wird geprüft, ob zur Laufzeit das Richtige rauskommt.
Ein Begriff, der relativ wichtig ist, ist der der vollständigen Tests.
Vollständige Test heißt, ich teste alle gültigen Eingabewerte.
So, die werden auch erschöpfende Tests genannt.
Wenn man sich jetzt mal dieses Beispiel hier anguckt,
int multipliziere, zwei Parameter int X und int
Y. Wenn ich da jetzt alle gültigen Eingabewerte testen wollte,
dann würde ich testen, 0 und 0,
0 und 1, 0 und 2, 0 und 3.
So, könnt ihr euch überlegen,
das sind bei Zwei Milliarden möglichen Werten für Int,
mal zwei Milliarden möglichen Werten, ziemlich viele Fälle, die man da so durchprogrammieren müsste.
Das wäre ziemlich ermüdend oder erschöpfend. Macht selten Sinn. Es gibt Fälle,
wo das Sinn ist, sinnvoll ist,
also hier so ein Beispiel mit so einem sehr kleinen Objekt,
wo man sehr klar mit Bullshen Zuzuständen,
da gibt es einfach nicht sehr viele verschiedene Zustände,
da kann man das machen, könnt ihr euch in Ruhe im Skript nochmal angucken.
Aber im Allgemeinen ist es so, dass man nicht vollständig testen kann,
dass es nichts Sinnvolles vollständig zu testen.
Was man stattdessen tut, man bildet Äquivalenzklassen
Und guckt sich die Grenzwerte an. Okay,
das ist was, was jetzt hier in der Folie,
auf dieser Folie relativ blöd, sagen wir,
an einem blöden Beispiel erklärt ist.
Also hier Äquivalenzklasse ist irgendwie das hier, die zahlen einem bestimmten Bereich.
Also wenn ich zum Beispiel diese Multiplizieren-Operation habe,
dann sind halt die Grenzwerte hier so Intager Max Value und in Jamine Value.
Und so Äquivalenzklassen werden einfach die Wette dazwischen.
Wenig hilfreich. Besseres Beispiel jetzt anhand dieses Überweisers,
den wir ja nun schon kennen, ist,
ich überlege mir, welche Grenzwerte gibt es denn?
Also was sind denn die größten oder die kleinsten Werte,
die hier zulässig sind? Wenn wir jetzt nur an
einen Positiv-Test denken. Was ist der größte Wert,
den ich hier jetzt bei der 30 angeben könnte?
Ich kann ja diesen Testfall hier mal kopieren. Ihr könnt schon
mal überlegen, welches jetzt hier ein möglicher, weiterer Testfall wäre.
Also hier sage ich jetzt einfach mal Testüberweisen, zwei.
Und jetzt möchte ich von euch wissen, was ist denn
jetzt hier so ein Grenzwert, der noch zulässig ist?
Null bis Salon
Ja, er sagt von null bis Saldo K1, genau.
Also K1 hat einen Saldo von 100, also von null bis 100.
Ich glaube, der Wert muss größer als Null sein,
also es ist irgendwie eins bis bis Saldo.
Okay, das heißt, ein guter Test geht genau auf
diese Grenzwerte. Man hat also jetzt den Fall,
okay, also 100 darf ich noch überweisen.
Dann nehme ich doch mal genau diesen Grenzfall. 100. So,
dann würde ich jetzt für meinen Test hier erwarten,
dass hinterher der Saldo hier welchen Wert hat.
Was musst du denn haben? Null, genau.
Und hier müssen 100
drauf sein
So, das ist jetzt ein Test.
Kann ich übersetzen? Ich habe einen weiteren Testfall geschrieben,
einen Positiv-Test, weil der hält sich ja an die Spezifikation.
Mal gucken, ob das auch funktioniert. Ich rufe mal auf
, alles testen. Ups, das kracht. Jetzt sehen
wir schon den netten Effekt hier mit so einem Tool.
Also sobald ein Testfall fehlschlägt, gibt es hier einen roten Balken.
Nur wenn alle Tests durchlaufen, ist der Balken grün.
Sobald mindestens ein Testfall fehlschlägt, ist das Ding rot.
Gut, jetzt kann ich hier draufklicken und dann sehe ich
hier tatsächlich, überziehen nicht möglich,
ist die Fehlermeldung
Oha, überziehen nicht möglich. Ich darf doch hier irgendwie,
müsste da eigentlich funktionieren. Überziehen nicht möglich. Das ist hier
die Fehlermeldung. Jetzt kann ich tatsächlich mir den Quelltext anzeigen
lassen, an der da diese Acception geworfen worden ist.
Und hier steht jetzt, if Quellkonto Gibsaldo, kleiner Gleichbetrag,
dann Through New Exception, whatever, über ziemlich möglich.
So, da oben stand, der Geldbetrag, der überwiesen werden soll,
muss größer, null sein und kleiner gleich dem Saldo.
Betrag, kleiner gleich dem Saldo, Betrag,
kleinergleich dem Saldo,
wer sieht den Fehler
Ja, das Gleich muss weg, genau. Kleiner, gleich.
Das ist so ein Programmierfehler, den macht man leicht mal.
Das kann leicht passieren. Den haben wir jetzt gefunden,
weil wir systematisch die Grenzwerte uns an untersucht haben.
Und der andere Testfall, mit der 70,
das ist einer, der in die Äquivalenzklasse fällt,
nämlich alles, was zwischen 0 oder 1,
wir müssen jetzt noch einen Testfall mit einer 1 schreiben und mit dem Maximalwert.
Und alle Werte dazwischen, die sind eigentlich logisch für unseren Test äquivalent.
Deswegen bilden die eine Äquivalenzklasse. Da muss ich nicht
alle Fälle durchgehen, sondern sage halt ein oder zwei
Fälle nehme ich mal so mit ein Ding raus
Okay. Dann gibt es so Begriffe wie Modultests, Integrationstests.
Wir konzentrieren uns hier in SE1 auf Modultests. Ja,
so einzelne Klassen zu testen mit Hilfe von Blackbox,
Whitebox und Schreibtischtests. Integrationstests sind dann Tests,
wo man dann solche verschiedenen Klassen im Zusammenspiel testet.
Fortgeschrittenes Zeug, gucken wir uns erstmal nicht an.
Diese Blackbox und Whitebox-Tests sind klassisch dynamische Tests.
Also das Testobjekt wird wirklich ausgeführt. Schreibtischtests sind statische Tests
. Komme ich jetzt nochmal drauf. Also Blackbox-Tests sind
genau die, wo wir uns als Klient verhalten.
Wir gucken auf eine Einheit drauf, auf einen Dienstleister und gucken,
Was sagt die Schnittstelle, was das Ding leisten muss?
Das teste ich dann. Was wir hier gerade geschrieben haben,
waren tatsächlich solche Blackbox-Tests. Und dann hat man üblicherweise halt
für jede Operation der Schnittstelle hat man dann auch eine Testklasse,
eine Testmethode, die das irgendwie testet.
Blackbox-Test kann man sowohl positiv als auch negativ machen.
Also wir haben es gerade gesehen,
ich kannte mir jetzt auch als Klient überlegen, naja,
was ist denn, wenn ich jetzt Nadel übergebe als Parameter?
Für eine der Referenzen oder für beide?
Die Whitebox-Tests sind die Tests, wo man den Quelltext kennt.
Ich gucke in den Quelltext rein. Ich gucke wirklich hier
in den Quelltext von meinem Überweiser rein.
CEO, hier gibt es ein IF
Ja, dann möchte ich den Fall hier auch abdecken.
Ich möchte also irgendwie jetzt, dass alle möglichen Kontrollpfade,
also alle möglichen Wege durch diese Methode, getestet werden.
Das heißt also, da gucke ich in den Quelltext
rein und schreibe auch meine Tests danach.
Es ist eine andere Sichtweise auf das Testen.
Okay, dann gibt es Schreibtischtests. Das ist eine der
effektivsten Methoden überhaupt, überhaupt um Fehler zu finden.
Mehrere Leute setzen sich zusammen und gucken auf den Quelltext drauf.
Einfach sinnierend auf den Quelltext gucken. Der eine erzählt,
was er sich überlegt hat, der andere sagt, hm,
hast du den in dem Fall auch überlegt und so?
Also einfach andere Leute mit dazuziehen
Das ist eine der effektivsten Methoden, um Fehler zu finden.
Code-Reviews. Gut. Jetzt das Technische. Das habe ich
das meiste, was ich jetzt auf den Folien gleich sage,
habe ich schon live gezeigt.
Also ich kann das relativ schnell jetzt einfach mal zeigen.
Also Grundregel ist erstmal, zu jeder Klasse sollte es eine Testklasse geben.
Also wir wollen ausführbare Tests haben. Wir haben jetzt gerade hier gesehen,
also hier zu dieser Klasse gibt es eine Testklasse.
Ich könnte mir jetzt auch zu Kontosimpel und Konto
anders auch Testklassen schreiben, weil die sind testbar.
Das sind Klassen, da kann ich wirklich
die Funktionalität abtesten
Dadurch, dass die Tests dann wiederholbar werden werden, die zu Regressionstests.
Das ist ein Begriff, der in der Testcommunity einfach etabliert ist.
Regressionstests sind die, dass man das einfach wiederholt testen kann. Die Werkzeugunterstützung ist wichtig,
weil wenn ihr versucht, jetzt so Testcodes selbst zu schreiben,
dann habt ihr ja relativ schnell so Testmethoden,
ihr schreibt solche Testmethoden erstmal selber und sagt immer,
ja, hat geklappt, hat geklappt, hat geklappt,
dann kriegt ihr seitenweise Ausgaben von eurer Testsoftware,
die immer nur sagt, ja, hat geklappt,
hat geklappt, das ist einfach ermüdend.
Und deswegen haben die,
als die J. Unit gebaut haben, schon gesagt,
okay, eigentlich will man nur den grünen Balken sehen,
dass alles geklappt hat und nur im Fall,
dass es nicht geklappt hat, braucht man eine Warnung irgendwie,
da ist was fehlgeschlagen und dann kriegt man den roten Balken und musste sich dann was überlegen.
Also die Frage ist immer, wie kriege ich so,
wie kriege ich das hin,
dass das mich nicht ermüdet, dieses weg?
Ich wollte mich unterstützen, ja, das macht J. Unit,
das sehen wir uns gleich genauer an und ist üblicherweise oder idealerweise eingebunden in die Entwicklungsumgebung.
Das seht ihr hier, dass J. Unit eingebunden ist,
tatsächlich in Blue Jay.
Ja, der Unit ein paar Worte dazu ist das
bekannteste Modul,
Testwerkzeug für Java
Ist von Can't Back und Erich Gamma entwickelt worden,
zwei auch ganz wichtige Namen in der Informatik.
Can't Back hat Extreme Programming geprägt, hat sehr viel mit Smalltalk gemacht früher.
Erich Gamma hat ein Buch geschrieben über Design Patterns,
hat Eclipse mitentworfen. Also das sind beides ziemlich dicke Nummern.
Und der Unit stellt einen Rahmen zur Verfügung,
indem man dann Testklassen schreiben kann.
Erleichtert damit das Ausführen von Testklassen und vereinfacht auch die Darstellung.
Ist in verschiedenen Ewigesumgebungen eingebunden, auch in Bluedray,
auch in Eclipse eingebunden und ist
völlig frei verfügbar
Der Umgang mit J.J. und das ist eigentlich ganz einfach.
Zweite Dinge muss ich tun. Ich muss also,
wenn ich einen Modultest schreiben will, ich möchte da so,
ich habe eine zu testende Klasse und möchte die testen.
Ich muss erstens eine Testklasse erstellen und zweitens muss ich dann
diese Testklasse auch ausführen, also ausführen lassen.
J.J. Juni dazu bringen, dass es das ausführt.
Also ich schreibe eine Testklasse, da sind mehrere Testfälle drin,
haben wir gerade gesehen. Und diese Testfälle sind aus sich
eines Klienten, der zur testenden Klasse formuliert.
Klar. Struktur eines Testfalls ist so,
dass man hier so eine Annotation ranschreiben muss,
dass es für das J.Unit,
was wir hier in SR1 verwenden,
ist das so
Das ist so ein bisschen Magic, die ihr
noch nicht verstehen könnt, weil wir Annotationen nicht behandeln.
Aber dann schreibt man eben halt hier diese Annotation ran,
hat dann hier seine Methode. Und in Brumpf dieser Methode
macht man etwas. Üblicherweise erzeugt man sich so eine Umgebung,
also erzeugt sich mehrere Exemplare, dann macht man den entscheidenden Aufruf
und dann testet man hinterher, ob das alles funktioniert.
Also das ist immer so eine Zweiteilung,
irgendwie sowas aufbauen und dann hinterher prüfen, ob alles geklappt hat.
Also ein Abgleich. Stimmen die Werte nicht überein, wird ein Nicht-Bestehen signalisiert. Okay,
das ausführende Testfälle ist dann ganz leicht, habt ihr gerade gesehen,
kann ich in Jay Unit einfach mit einem Knopfdruck machen was für euch wichtig ist,
für jede, für jede Testmethode wird ein neues Exemplar der Testklasse erzeugt.
Das heißt, die Reihenfolge der Testfälle ist tatsächlich egal.
Ihr dürft nicht davon ausgehen, dass irgendwie diese Reihenfolge in einer Testfälle ausgeführt,
in einer Testfälle in einer Reihenfolge ausgeführt werden.
Okay, alle Testfehler frei durch, gibt es einen Grünbalken,
mindestens einer ist der Wald rot, soweit.
Dann gibt es zwei verschiedene Arten, wie etwas fehlschlagen kann ein solcher Test.
Also man kann entweder das Nicht-Bestehen haben oder es kann ein Fehler im Test fallen.
Ich kann das mal zeigen, wenn ich jetzt hier zum Beispiel Naja,
nicht in diesem, sondern in der Testklasse.
Hier hatten wir jetzt ja gerade den Fall gehabt,
dass das, ach so, jetzt, oh oh,
oder ganz wichtig, ich habe jetzt gerade gezeigt,
dass der, wir haben den Fehler korrigiert im Überweiser.
Das heißt, jetzt müsste der Test eigentlich laufen.
Ja, jetzt laufen beide Testfälle durch.
Wir haben den Fehler ja korrigiert. Jetzt könnte ich in
diesem Testfall hier ja irgendwo einen Fehler machen.
Ich könnte jetzt hier zum Beispiel mal null machen und alle aufrufen.
Dann ist der Testfall falsch geschrieben. Man kann natürlich
auch beim Testen Fehler machen. Das lässt sich übersetzen.
Es ist also formal korrekt, aber führt jetzt dazu,
dass der Test fehlschlägt
Und jetzt ist das ein Fehler. Vorher war das ein nicht bestanden,
weil in der Testklasse stand ja hier, diese komische Assurd-Methode,
diese Assurd-Methoden, die gehören zu J. Unit dazu.
Die kriegen wir quasi mitgeliefert, wenn wir J. Unit benutzen.
Da wird also geprüft, also stell dir mal fest,
ob das wirklich hier das Gleiche ist.
Es gibt Saldo mit null. Wenn eine solche Assursion,
wenn die fehlschlägt, dann ist das ein nicht bestehendes Fehler des Testfalles.
Wenn aber irgendwas anderes schiefgeht und ein Null-Pointer-Acception oder irgendwas anderes
, dann wird das ein Fehler.
Ich kann das hier vielleicht nochmal eben
ganz kurz zeigen
Ich mache hier das mal rückgängig, was ich gerade gemacht habe.
Und sage jetzt hier, falsche Erwartungen.
Also da würde jetzt ja dieses Assured Equals wieder fehlschlagen,
weil das eine falsche Erwartung ist.
Dann sieht man jetzt, wenn ich das ausführe,
sagt er, es ist ein nicht bestandener Testfall.
Und vorher war der Fehler eben hier, da war es
ein Fehler. Also diese Unterscheidung gibt es in Failure,
im Jay-Unit-Terminologie nicht bestanden. Und ein Error wirklich Fehler bei
der Testausführung. Gut, Struktur einer Jay-Unit-Testlasse
haben wir gesehen
Abversion 4, die ihr auch benutzt,
ist sozusagen die neuere, die modernere Variante,
besteht so eine Testleiste einfach aus einer Reihe von Testmethoden,
die jeweils mit der Annotation einen Test versehen sind.
Bad Jerdy und eine 3,8 war das ein bisschen anders für die Leute,
die noch mit älterer Software zu tun haben,
da ist das nochmal mit ein bisschen erklärt.
Diese vorgegebenen Prüfmethoden, die immer mit diesem Asserd beginnen,
da gibt es also Asserd Equals, Asserd same,
um Referenzen zu vergleichen, Asserd True,
Asserd Falls und so weiter, da gibt es einiges.
In Jer-Unit 4.0 werden diese Prüfmethoden üblicherweise über einen
statischen Import innerhalb der Testklasse zugänglich gemacht. Also was,
wenn wir uns mal diese Testklasse hier angucken,
Da steht hier oben Import, Static, Org,
J. Unit as Search Stern.
Das ist wieder so ein bisschen Magic. Diese statischen Importe
werden wir hier in SE1 gar nicht groß thematisieren.
Das müsst ihr einfach hinschreiben und ihr müsst auch diese Importzeile hinschreiben,
damit ihr diese Annotation hier überhaupt benutzen könnt.
Das ist einfach so ein bisschen, sagen wir,
ja, formaler Aufwand, den man da einfach machen muss.
Diese Zahlen sollten da immer stehen, wenn ihr mit J. Unit Testfälle schreibt.
Dann könnt ihr aber auch diese ganzen Assert-Sachen benutzen.
Das war das, was ich zum Testen erzählen wollte.
Testen ist eine Maßnahme zur Qualitätssicherung, ist anspruchsvoll,
wenn man es gut machen will. Testen gehört zum Handwerkszeug.
Solltet ihr also immer alle Techniken dazu auch parat haben?
In einem Objekt notierten System zu jeder testbaren, klassenden Testklasse.
Nachdenken führt schon häufig zu besserer Software,
wenn man sozusagen über die Testbarkeit auch nachdenkt.
Testwerkzeuge können uns einen Teil der Arbeit abnehmen. Und Jay
Unit ist das bekannteste Werkzeug für Unit Test in Java.
Ihr guckt ganz erwartungsvoll. Ich habe leider kein Foto für
euch heute. Das heißt, kein Storesi-Frage zum Testen,
wenn keine Fragen mehr sind, sind wir jetzt fertig.
Sag dir