Einen schönen guten Morgen, herzlich Willkommen zur fünften Übung in
der SVT 1 und da Sie alle in der Vorlesung waren, wissen Sie, die
Vorlesung ist mittlerweile angekommen bei Parallelität und deswegen
haben wir mal ein bisschen Parallelitätsquatsch mitgebracht.
Datenwettläufe haben Sie schon besprochen.
Ich glaube sogar eine Puzzler.
Das ist ein Fall aus dem vergangenen Jahr von Starbucks.
Wettläufe hatten Sie vielleicht mit der Banküberweisung das Beispiel.
Das ist ein sehr klassisches Beispiel für einen Datenwettlauf und zwar
stellen Sie sich vor, Sie haben zwei Konten, Bankkonten und Sie machen
eine Überweisung von einem Konto A auf das Konto B und Sie können die
Überweisung natürlich parallel ausführen und dann haben Sie
klassischen Data Race, wenn Sie vom selben Konto auf zwei andere
überweisen, nämlich wenn Sie den Kontostand lesen und dann den
Überweisungsbetrag subtrahieren und dann entsprechend zurückschreiben
und da können Sie natürlich in den Wettlauf reinkommen, wenn Sie das
falsch implementieren.
Glücklicherweise oder unglücklicherweise dürfen wir keine Banken
hacken oder Krempel machen, aber bei Starbucks kann man das schon mal
ausprobieren.
Bei Starbucks gibt es zumindest in den USA solche Gutscheinkarten,
kann man Guthaben draufladen und die Karten sind auch in
Anführungszeichen übertragbar und das tolle ist, da gibt es ein
Webfrontend dazu und da kann man sich einloggen und sagen, hey wie ist
denn mein Kontostand oder beim Kumpel Sebastian möchte ich was auf
seine Starbuckskarte überweisen.
Sie sehen schon überweisen und Kundenkarten und so, das ist im Prinzip
genau das gleiche und da hat sich ein findiger Mensch mal mit
beschäftigt, wie dieses Webfrontend so aussieht und mit welchem
Webbackend dieses Frontend entspricht und hat sich überlegt, wenn ich
mir jetzt zwei Gutscheinkarten kaufe mit je 5 Dollar und ein bisschen
Zeit investieren, dann kann ich vielleicht so ein Datarace
provozieren.
Was könnte man da machen?
Naja, ich mache ganz wilde Transaktionen von Karte 1 auf Karte 2 und
guck mal was passiert.
Die Überweisung selbst, die läuft zweistufig in der Web-Session.
Das erste ist so sinngemäß Schritt 1, ich sage welcher Betrag wird von
welcher Karte auf welche Karte überwiesen.
Das ist im Prinzip das Anlegen der Überweisung und dann kriege ich
eine Bestätigungsseite, wollen sie das wirklich?
Und dann sagt man yep, ich will und natürlich werden da die ganzen
Session -IDs noch so mitgeschleift, dass natürlich der zweite Schritt
hier weiß, um welche Überweisung im ersten Schritt eigentlich geht.
Was machen wir?
Wir machen ein bisschen Pseudocode.
Curl ist ein Programm, mit dem sie auf der Kommandozeile Web-Anfragen
wegschicken können.
Sie können natürlich Payload mitgeben, wie sie lustig sind und was er
getan hat, ist im Wesentlichen, naja ich überweise mal fünf Dollar von
Karte 1 nach 2 in der Session 1 und das mache ich auch noch in der
zweiten Session genauso.
Und wenn ich das sequenziell mache, dann ist natürlich nach dem ersten
Aufruf hier nichts mehr drauf auf der Karte 1 und die zweite
Überweisung, die geht nicht mehr, weil negativ Guthaben ist nicht
erlaubt.
Da das allerdings erst gebucht wird, wenn man hier Confirm aufruft und
das eine relativ kleine Anfrage ist, kann man ja mal versuchen, das
parallel auszuführen.
Wenn man das oft genug probiert und zwischendrin zurücksetzt, dann
kann man vielleicht was finden.
Und was er gefunden hat, ist tatsächlich, naja, eine Karte mit null
Dollar Guthaben, das war die Karte 1 und zwei Gutschriften auf der
anderen Karte, was nicht genau das ist, was Starbucks eigentlich gerne
haben wollte.
Und er ist dann in den Laden und hat sich für 13 Dollar irgendwas
gekauft.
Also er hat tatsächlich mehr Geld ausgegeben, als er investiert hat.
Und da hat er sich gedacht, bevor ich jetzt in den Knast komme oder
Schlimmeres, lade ich mein Guthaben wieder über einen größeren Betrag
und schicke denen mal eine E-Mail.
Dann ist erst mal gar nichts passiert und nach zehn Tagen haben sie
sich gemeldet, aber nicht mit Dankeschön, sondern mit sie Verbrecher.
Das geht gar nicht.
Insofern war es recht glücklich, dass er vorher die zehn Dollar
aufgeladen hat, dass also mehr Guthaben da ist, als er tatsächlich
abgehoben hatte am Schalter.
Das zeigt zweierlei, wenn sie nicht bei einer Bank arbeiten, sondern
vielleicht bei irgendeinem Retailhändler und sie haben sowas wie
Gutscheinkarten und sie müssen das implementieren, dann denken sie mal
über mehr Fertigkeit nach, weil sie stellen natürlich nur einen
Webserver dahin, aber sie haben sehr viele Kunden und wenn da jemand
dabei ist, der Spaß haben will, dann haben sie genau das gleiche
Problem wie eine Bank.
Das zweite ist, wenn sie auf der anderen Seite stehen und solche
Lücken suchen und die dann ausnutzen und sich dann einen großen Latte
-Mocaccino mit Extrasahne und Karamellsirup rauslassen für 19 Euro,
dann sorgen sie dafür, dass sie sich hinterher nicht vorwerfen lassen
müssen, sie hätten was geklaut.
Also gucken sie, dass sie sich da nicht nicht straffahr machen und
sagen sie den Leuten dann auch, dass sie diese Lücke entdeckt haben.
Am besten posten sie das ganze im Internet, wer ein großer Held und
über die anderen lacht man.
Schauen sie mal.
Na gut, fangen wir an mit der Übung.
Was man natürlich außerdem noch lernt, Kaffee bei Starbucks ist zu
teuer.
Aber nirgendwo sonst kriegt man ein Dessert, das als Kaffee durchgeht.
Okay, fangen wir an mit Aufgabe 1.
Auf dem Übungsblatt sollten sie sich mal die Java API genauer
anschauen und in der ersten Aufgabe ging es um die Event Listener für
Interaktion mit der Maus hauptsächlich.
Da gab es einfach nur ein Maus Listener, ein Maus Motion Listener, ein
Maus UI Listener und einen Maus Input Listener.
Das sind alles Schnittstellen, die verschiedene Methoden
bereitstellen.
Wir wollten wissen, warum unterscheidet Java eigentlich zwischen
solchen vielen Schnittstellen?
Ist auch eine Maus, ein Gerät, warum brauche ich da jetzt fünf
Schnittstellen, die ich implementieren kann?
Und die Antwort ist natürlich ganz klar, diese Events, die sind
dadurch semantisch gruppiert und wenn sie jetzt nur wissen wollen, wie
bewegt sich die Maus und nicht, was wird geklickt, dann gucken sie
sich halt nur diese Methoden an.
Und sie müssen dann auch nur diese implementieren.
Umgekehrt, wenn sie nur wissen wollen, ob jemand auf die Maus klickt
und nicht wohin, was vielleicht ein bisschen nutzlos ist, aber
vielleicht finden sie einen Anwendungsfall, dann müssen sie sich nicht
mit den Methoden auseinandersetzen, die irgendwas mit diesen
Bewegungsevents machen.
Und dadurch kann man natürlich auch ein paar Ressourcen sparen.
Das Ganze steht so oder so ähnlich in den Java-Docs drin.
Wenn sie die also aufgemacht haben, wissen sie das alles schon?
Also die dritte oder vierte Schnittstelle, die wir da hatten, der Maus
-Input -Listener, das ist eine leere Schnittstelle.
Der hat also keinerlei Funktion, die zugesichert wird.
Die Frage war, warum gibt es die?
Und die Antwort ist, naja, dieser Maus-Input-Listener, der ist zwar
leer, aber der erweitert zwei andere, nämlich den Maus-Listener und
den Maus-Motion-Listener.
Und das sorgt dafür, dass natürlich jemand, dass jemand, der den Maus
-Input -Listener implementiert, all das tun muss, was die anderen zwei
Schnittstellen fordern oder vorschreiben.
Und das macht einfach die Definition, wenn sie eine eigene Klasse
implementieren, ein bisschen kürzer, weil sie nicht zwei
Schnittstellen hinschreiben müssen, sondern nur eine.
Denke ich auch einigermaßen einleuchtend.
Diese Idee, die verliert an Reiz, wenn sie hingehen und 27
Schnittstellen zu einer zusammenfassen und die 27 haben nichts
miteinander zu tun.
Dann wird die Klassendefinition zwar besser lesbar, aber schlechter
verstehbar.
Also da müssten sie ein bisschen abwägen, wenn sie selbst so eine
Schnittstelle definieren.
Gut, dann gab es noch einen Maus-Adapter.
Das war eine Implementierung oder ein Maus-Motion- Adapter.
Das war auch eine Implementierung und haben sie da mal reingeschaut,
wie der Quelltext aussieht von diesem von diesen Adaptern.
Wer hat denn da reingeschaut?
Einmal bitte melden.
Doch, zwei.
Der Rest hat einfach geraten oder gegoogelt.
Wenn sie da reinschauen, stellen sie fest, diese Adapter sind leer.
Da sind zwar die ganzen Methoden definiert, aber die haben keinerlei
Implementierung.
Das heißt, wenn sie einen Maus-Adapter nur in der Hand haben, dann tut
er erstmal gar nix.
Warum ist es trotzdem sinnvoll?
Naja, wenn sie von einer Schnittstelle nur eine Methode implementieren
wollen und die anderen fünf interessieren sie nicht, dann können sie
den Adapter hernehmen und dann überschreiben sie diese eine Methode,
die sie interessiert und der Adapter liefert ihnen diese I don't care
Implementierung von den anderen.
Das heißt, da räumen sie auch wieder ihren eigenen Code auf, dadurch
dass sie diese interessiert mich nicht, leeren Methoden an die
Oberklasse abtreten.
Das ist sogar ein Entwurfsmuster.
Ich glaube, das heißt null Methode oder null Objekt, je nachdem, ob
das ganze Objekt nichts tut oder nur die eine Methode.
Könnte also auch eine Frage sein, die man vielleicht in abgewandelter
Form nochmal sich anschaut.
Und die letzte Frage aus Aufgabe 1 war, naja, jetzt haben wir uns mit
dieser Maus-Klasse oder mit diesen Maus-Klassen und Maus
-Schnittstellen beschäftigt und haben gesehen, da gibt es den Adapter
und der Zweck von dem Adapter, der ist klar, der vereinfacht das.
Jetzt gibt es eine Schnittstelle, die heißt Action Listener.
Da gibt es aber keinen Action Adapter.
Warum ist das so?
Hat irgendjemand eine Idee, warum das so ist?
Außer ihm.
Er hat sich direkt instinktiv gemeldet, ohne dass ich fragen musste.
Ihr fünf, wir dürfen es trotzdem beantworten.
Genau, also ihr Kommilitone sagt, völlig richtig, die Action Listener
-Schnittstelle, die hat nur eine Methode.
Der Adapter würde also eine leere Methode bereitstellen und wenn ich
den Action Adapter, den hypothetischen, wenn ich den beerben würde und
ich würde diese Methode nicht überschreiben, dann könnte ich mir das
Erben sparen.
Wenn ich sie überschreibe, dann bringt mir der Adapter nichts, weil
der hat ja nur diese eine leere Implementierung.
Insofern völlig richtig, da spare ich nichts.
Das wäre nur, sagen wir mal, der Konsistenzwillen, um der
Konsistenzwillen die API aufgebläht.
Also völlig unsinnig, sowas zu haben.
Haben Sie Fragen zu dieser Aufgabe?
Hat irgendjemand Probleme gehabt, die Java-API-Dokumentation zu finden
oder zu lesen?
Ich würde mich da jetzt auch nicht trauen zu melden.
Naja, hauptsächlich deswegen, weil die Links auf dem Übungsblatt schon
drauf waren.
Ja, aber du kannst ja mal fragen, wer es tatsächlich getan hat.
Wer hat es denn getan?
Wer hat denn die Java-Doc aufgemacht?
Okay und jetzt alle Hände runter, die das im Browser angeguckt haben.
Wer hat es denn in Eclipse angeschaut?
Gar niemand oder in IntelliJ oder in irgendeiner Entwicklungsumgebung?
Also dann vielleicht doch noch als hilfreiche Info, wenn Sie in
Eclipse, auch in IntelliJ, man mit Steuerung auf, also Steuerung
gedrückt halten und dann mit der linken Maustaste auf eine Methode
gehen oder auf eine Klasse, dann springt der Texteditor an den
Quellcode.
Das ist mal das eine und da haben Sie natürlich auch die Java-Doc
-Kommentare dabei.
Das zweite ist, wenn Sie mit der Maus einfach mal zufällig auf eine
Klasse oder eine Methode drauf kommen und die Maus dann nicht mehr
bewegen, dann kriegen Sie so ein kleines Popup-Fenster und da ist auch
die Java-Doc drin.
Und das ist äußerst praktisch, weil dann brauchen Sie nicht ins
Internet gehen und Sie bekommen das natürlich auch, wenn Sie eine
andere Bibliothek haben, eine andere externe Bibliothek, die keine
Java -Dokumentation im Internet hat.
Sie haben das offline auf Ihrem Rechner, wenn Sie im Zug sitzen und
coden, können Sie trotzdem nachschlagen.
Also wenn Sie das noch nie gesehen haben, gucken Sie mal rein.
Könnte vielleicht irgendwann mal hilfreich sein.
Jetzt habe ich gehört, es gibt gar keine Fragen zur Aufgabe 1.
Ist das richtig?
Das ist sehr erfreulich.
Vielleicht gibt es welche zur Aufgabe 2.
Das würde mich hauptsächlich deswegen freuen, weil ich sie nicht
vorstelle.
Ja, wir haben uns diesmal darauf geeinigt, dass ich auch mal was
vorstellen darf.
Nicht letztes Mal.
Das passiert natürlich hauptsächlich in Ihrem Sinne, damit Matthias
nicht wieder anfängt zu tanzen.
Das wollen wir sparen.
Gut, also Aufgabe 2, der Zustandsautomat, den Sie implementieren
sollten.
Über der Aufgabe stand, glaube ich, Implementierung eines
Entwurfsmusters.
Ja, das war der Titel der Aufgabe.
Das war genau der Titel der Aufgabe.
Es war aber nirgendwo tatsächlich explizit gefordert, dass Sie das
tun.
Es war nur hilfreich.
Wir haben Ihnen den Zustandsautomat nochmal mitgebracht.
Sieht ganz einfach aus.
Hat nur vier Zustände mit dem Endzustand und wir sehen schon, es gibt
Exit -Methoden, es sollte Entry-Methoden geben.
Es gibt hier auch eine Transition, die etwas tut.
Bei der Eingabe von D im Zustand A.
Das heißt alles, was man so aus dem Zustandsmuster auch kennt, was man
da implementieren sollte.
Und das sollten Sie, wie gesagt, umsetzen.
Wir haben Ihnen jetzt hier mal nicht die komplette Lösung mitgebracht,
weil das relativ uninteressant ist.
Mit Copy and Paste aus den Vorlesungsunterlagen oder aus dem Headfirst
Design Patterns hat man es eigentlich schon zu 90 Prozent gelöst.
Dementsprechend zeigen wir jetzt nicht normales, sondern nur so ein
paar kleine Details.
Wir haben allerdings einen JUnit-Test für Sie nach der Übung, mit dem
Sie testen können, ob denn Ihre Lösung korrekt ist.
Das ist auch das, was wir Ihren Tutoren geben.
Das heißt, Sie können da so ein bisschen Konsistenzprüfung betreiben.
Unser Test teste tatsächlich den kompletten Automaten mit allen
möglichen Eingaben.
Das geht zum Glück, weil der relativ klein ist und weil Matthias die
Tests geschickt implementiert hat.
Deshalb kann man das umfassend machen.
Ich fand es schon ganz hübsch.
Das geht auch für große Automaten.
Ja, aber in dem Fall geht es halt dann besonders schnell.
Na ja, auf jeden Fall können Sie das dann ausführlich festen, wenn Sie
möchten.
Kleine Frage.
Ich behaupte, die ganze Zeit ist so einfach.
Wer hat denn die Aufgabe nicht gelöst oder nicht lösen können?
Machen wir es nacheinander.
Wer hat die Aufgabe nicht lösen können?
Fragt doch mal nach dem positiven Fall.
Wer hat denn die Aufgabe gelöst?
Aber dann meldet sich wieder keiner.
Dann meldet sich keiner.
Ja, deshalb.
Also wer hat die Aufgabe nicht gelöst?
Ui.
Okay.
Traut sich einer mir zu sagen, warum?
Ja, bitte.
Hat jemand einen anderen guten Grund?
Okay, also alle, die sich gemeldet haben, haben den Übungsschein.
Ja, kann man das so sagen.
Gut.
Und weil das ja auf der Aufzeichnung nicht zu sehen ist.
Also alle haben den Übungsschein.
Alle haben den Übungsschein.
Jetzt haben sich gerade alle gemeldet.
Das heißt, wenn Sie jetzt nur bei YouTube zuschauen, sollten Sie ein
schlechtes Gewissen haben.
Gut, wunderbar.
Nicht lösen können.
Da jemand dabei?
Das ist zu einfach, oder?
Ja, sage ich doch.
Gut, dann würde ich sagen, mit Aufgabe 3 weiter.
Ja, der Rest hat es gelöst und richtig.
Ja, okay.
Sehr gut.
Eine was?
Eine Du-Methode.
Ihr Kommilitone sagt, es gab eine Du-Methode, die man implementieren
sollte.
Also, der Kommilitone sagt, auf dem Übungsblatt stand drauf, wir
sollten da eine Methode implementieren und da stand Du-Methode.
Das ist ein Schlüsselwort in Java und deswegen kann man das ja gar
nicht machen.
Das ist richtig.
Wie Sie die Methode intern nennen, ist uns auch ehrlich gesagt
herzlich egal, weil wir gucken uns den Automaten von außen an.
Wie reagiert das Ding?
Wie ist es implementiert?
Insofern, ich hoffe, das hat Sie nicht davon abgehalten, die Aufgabe
zu lösen.
Wir haben das ganz geschickt umgangen und die, glaube ich, Do-It
genannt.
Sie merken schon, die Kreativität ist bei uns eine ganz wichtige
Komponente.
Gut, ich habe Ihnen hier einmal ein Beispiel für die Benutzung des
fertigen Zustandsautomaten mitgebracht.
Man legt sich einen neuen Zustandsautomaten an und lässt dann die
Eingaben prozessieren, so wie man die denn gerne haben möchte.
Das ist jetzt gerade eine Variante, die tatsächlich funktionieren
würde mit dem Automaten.
Das heißt, da sollte dann auch kein Fehler auftreten.
Jetzt zu unserer Implementierung und ich sage es mal so, zu der, die
wir uns auch von Ihnen erhofft hätten.
Nämlich, dass man einen abstrakten Zustand implementiert.
Warum sollte man das tun?
Naja, es ist ganz einfach, es ist ganz nett, wenn man so eine
Standardimplementierung hat, die einfach zunächst mal sagt, egal was
ich im Zustand eingebe, ich werfe immer diese Illegal State Exception.
Das ist halt die Standardvariante.
Wenn ich dann unter Zustände tatsächlich implementiere, dann
überschreibe ich halt die jeweilige Methode, so dass das so
funktioniert, wie der Zustand funktionieren sollte.
Aber dann muss ich nicht mehr alle Eingaben implementieren, weil die
restlichen, die eben nicht funktionieren sollen, haben dann schon
durch diesen abstrakten Zustand diese Illegal State Exception.
Außerdem brauchen wir natürlich noch vom Zustandsmuster gefordert oder
auch so von Automaten überhaupt, wenn sie das Zustandsmuster nicht
explizit implementiert haben, trotzdem diese Entry, die Transition und
die Exit-Methoden.
Auch die kann man erst mal leer hier hinlegen.
Wenn man die dann für den Zustand, den jeweiligen nicht braucht, dann
braucht man dort auch nichts überschreiben und ist einfach schneller
fertig.
Zum Beispiel, ich mache ganz kurz den noch fertig, den State Stop
Zustand, der passt nämlich gerade ganz gut in mein Konzept.
Da braucht man nämlich tatsächlich nichts tun.
Wenn wir einmal in dem State Stop sind, dann können wir nichts mehr
eingeben.
Das heißt, wenn da noch eine Eingabe kommt, würde ein Illegal State
Exception kommen.
Und jetzt hat ein Kommilitone eine Frage.
Diese Notation bewirkt, dass ich, dazu sollte man vielleicht zeigen,
wie unsere Implementierung funktioniert, das bewirkt, dass ich einen
Zustand zurückgebe, in den ich dann komme, wenn ich diese Eingabe A
benutze.
Und diese Notation bewirkt, dass ich nur Zustände, die von Abstract,
State, Erben zurückgeben kann.
Ja, und der Punkt ist, wir wollen hier nicht einen Zustand in der Hand
haben, eine Instanz, sondern wir wollen den Typ in der Hand haben,
eine Klasse.
Das sehen wir gleich auf der nächsten Folie, glaube ich.
Das sieht man sogar hier, weil da steht ja Class, das ist der Typ der
Rückgabe und das ist ein Raw-Type und wir wollen das ja einschränken
auf Zustände.
Deswegen schreibt man das dann so hin.
Genau, vielleicht mal direkt am Beispiel, für den Zustand A sieht man
es dann ganz schön, wie unsere Rückgabe funktioniert.
Sie erinnern sich vielleicht im Zustand A, wenn dort die Eingabe A
kommt, dann landen wir wieder im Zustand A und genau das macht jetzt
diese Eingabe A Methode.
Genau, was wir außerdem noch brauchen im Zustand A, wenn Sie sich
erinnern, ist dieses Transition.
Unsere Implementierung sieht jetzt vor, dass man nochmal auf die
Eingabe überprüfen muss.
Kann man sicherlich auch geschickter lösen, glaube ich, aber ist okay.
Kommt drauf an.
Also diese Transition Methode wird automatisch aufgerufen von der
Oberklasse beim Übergang und da Sie natürlich nicht wissen, in der
aufrufenden Methode, welche Übergänge es alle gibt, bleibt da nur noch
diese parametrisierte Variante.
Also in unserer Variante, so wie wir die State Machine implementiert
haben, geht es tatsächlich nicht anders, aber man könnte die ganze
State Machine schon anders bauen, dann würde es tatsächlich ein wenig
hübscher gehen.
Aber so könnte das aussehen.
Was haben wir außerdem noch?
Die State Machine.
Dort brauchen wir in unserer Implementierung einen Namen und den
ersten Zustand, in dem wir uns befinden.
Wenn wir dann eine Eingabe haben, dann ändert sich diese Current
Variable auf den aktuellen Zustand und unser Zustandsautomat ist zudem
noch gecached.
Das heißt, wir kennen unsere Zustände, die halten wir uns in einem Set
und um das tun zu können, müssen wir zunächst erst mal alle Zustände,
die wir kennen und implementiert haben, für den Zustandsautomaten dort
registrieren.
Das ist also das, was hier noch passiert.
Das heißt, dann in unserer Implementierung nur die Zustände, die dort
registriert sind, kann man auch tatsächlich erreichen.
Genau, das ist noch mal das, was ich schon gesagt habe.
Jetzt ein wenig zu unseren Tests.
Was hier hauptsächlich interessant ist, ist dieser Test mit dem
Mocking.
Sie erinnern sich vielleicht, wir wollen Übergänge haben mit einer
Transition Ausgabe und wir haben auch definiert, dass bei Entry und
Exit gewisse Ausgaben getätigt werden und um das überprüfen zu können,
benutzen wir diesen Mock und dazu setzen wir unseren System Out
zunächst einmal, speichern wir unseren System Out zwischen, setzen
dann in den Print Stream auf unseren Mock, so dass wir das testen
können und hinterher muss man das natürlich wieder aufräumen.
Das kann in so einer Teardown-Methode passieren, damit einfach mit
unserem System Out ansonsten nichts kaputt geht, wegen unserem Testen.
Und wenn man das getan hat, dann kann man wunderbar testen mit diesem,
mit dem Mockitor und kann überprüfen, ob denn tatsächlich die Ausgabe
auf der Konsole so passiert, wie sie denn passieren soll.
Außerdem sehen wir hier, wie wir den Übergang von C nach D, ist das
richtig?
Ne, Eingabe C und dann D testen.
Genau, also hier hätten wir nacheinander erst die Eingabe C in den
Zustandsautomat, dann war in dem Zustand, wo welcher auch immer das
war, ich glaube B, war dann definiert, dass im Entry die Ausgabe
passieren soll.
Das wird an dieser Stelle getestet und danach testen wir noch, ob wir
denn im richtigen Zustand sind und zuletzt gibt es noch die Eingabe D
im Zustand B und die ist dort aber nicht definiert, das heißt an der
Stelle erwarten wir dann eine Illegal State Exception, deshalb
erwartet das auch der Test.
So einfach.
Was haben wir hier noch?
Unsere Implementierung der IsInState-Methode, die macht eben genau
das, dass sie überprüft, haben wir denn den richtigen State erreicht?
Einfach indem sie die Klassennamen vergleicht.
Genau und damit bin ich schon wieder durch.
Matthias möchte auch mal wieder tanzen, dementsprechend macht ihr
jetzt Aufgabe 3.
Was man hier vielleicht noch sagen kann, vollständig testen von
Zustandsautomaten heißt, ich muss gucken, komme ich in alle Zustände
und verhalten die sich richtig?
Mechanisch heißt das, ich muss für jeden Zustand und alle Eingaben
prüfen, ob sich das Ding so verhält, wie es soll.
Das heißt nicht nur für die Übergänge, die definiert sind, sondern
insbesondere auch für die, die nicht definiert sind, weil sonst weiß
ich ja nicht, kommt da tatsächlich eine Illegal State Exception oder
wechselt der Automat irgendwo anders hin, was nicht definiert wäre.
Zumindest in der Aufgabenstellung oder in der Modellierung und
deswegen habe ich im Prinzip so viele Testfälle wie Zustände mal
mögliche Eingaben und dann habe ich tatsächlich alles abgedeckt.
Und das ist relativ simpel hinzuschreiben.
Sie können einfach sagen, mit welcher Eingabensequenz komme ich denn
in den Zustand X?
Das mache ich mir eine Methode, dann sage ich, gehe nach X, dann bin
ich dort und dann kann ich ausgehend von X alle möglichen Eingaben
abprüfen.
Relativ kompakt hinzuschreiben.
Okay, hat irgendjemand Fragen zu dieser Aufgabe?
Ihr Kommilitone fragt, wie funktioniert es hier mit dieser Mockito
-Zeile?
Mockito.verify.println.
Das heißt ja, ich kann auf was auch immer aus verify rauskommt und
println aufrufen und das ist genau so, weil dieses verify gibt den Typ
zurück, also den vom gemockten Element.
Ob das statisch ist?
Das ist statisch implementiert, ja.
Ich weiß ehrlich gesagt nicht, wie Mockito implementiert ist.
Sie können einfach mal die Implementierung aufmachen, dann wissen Sie
die Antwort.
Ich kann sie jetzt leider tatsächlich nicht geben.
Ich bin mir nur sicher, dass das so funktioniert.
Das ist der Aufruf, den man zum Überprüfen von Methodenaufrufen macht,
wenn man Mockito benutzt.
Also das ist eins zu eins kopiert aus der Doku von denen und der Test
verläuft und er schlägt fehl oder er ist grün, je nachdem wie es
implementiert ist.
Wie das verify gebaut ist, weiß ich nicht.
Das müssen Sie nachschauen.
Okay, dann gehe ich jetzt davon aus, dass das tatsächlich die letzte
Frage war.
Die Aufgabe 3 hat sich auch wieder mit der Java API beschäftigt und
mit den Entwurfsmustern da drin.
Diesmal sollten Sie mal nachschauen, was sind das denn für Dinge und
wie sehen die aus?
Der erste Teil war, gucken Sie sich den ActionListener an und die
Unterklasse AbstractAction oder die implementierende Klasse
AbstractAction und deren Unterklassen CloseAction, IconifyAction und
RestoreAction.
Und was ist das dann für ein Muster?
Und explizit war gesagt, es geht hier nicht um den Beobachter, weil
der ActionListener, der wird ganz oft als Beobachter benutzt.
Alle Objekte, die einen ActionListener entgegennehmen können, sind
Subjekte und in dem Fall ist dann der ActionListener oder die Instanz
ein Beobachter.
Aber darum geht es hier nicht.
Es geht um die Unterklassen, insbesondere um die untersten drei auf
der Abbildung.
Und wer möchte denn sagen, was das hier für ein Entwurfsmuster ist
oder sein könnte?
Okay, wir gehen nochmal ein Stück zurück.
Wir stellen die gleiche Frage noch mal.
Wer hat denn die Aufgabe nicht gemacht, weil er den Übungsschein schon
hat?
Okay, wer hat die Aufgabe gemacht und hat den Übungsschein schon?
Streber, Sie.
Was ist die Antwort?
Ach Mist, steht schon da und trotzdem hat sich keiner gemeldet.
Das ist das Befehlsmuster.
Und was man hier hat, sind drei konkrete Befehle, die CloseAction und
die anderen beiden.
Die sind analog implementiert, nur eben halt zum minimieren und wieder
maximieren oder wiederherstellen.
Und die AbstractAction, das ist der abstrakte Befehl.
Also relativ simpel.
Vielleicht hilft Ihnen der Tipp Ihres Kommilitonen für die nächste
Frage.
Hier ging es um die beiden Methoden Run und Start von der Thread
-Klasse.
Und welches Muster ist das?
Es steht schon wieder da.
Trotzdem, was ist es denn?
Also das ist eine Schablone.
Haben Sie mal, wer hat denn in den Quelltext reingeschaut?
Einer, zwei.
Wer hat denn das Java-Doc gelesen?
Okay und wer hat es einfach gewusst oder geraten?
Okay, diejenigen, die sich den Quelltext angeschaut haben, denen
irgendwas komisches aufgefallen?
Also den Quelltext von Start insbesondere.
Das ist eine Schablone, das ist völlig richtig und das Besondere oder
das Komische ist, in Start wird Run gar nicht aufgerufen.
Trotzdem hat man hinterher einen zweiten Faden und da drin läuft die
Run -Methode und das liegt daran, dass die JVM die Run-Methode aufruft
und zwar auf dem erzeugten neuen Faden, aber das ist eine Native
-Methode.
Das heißt, da haben sie die Implementierung nicht im Java drin,
sondern das macht die VM und die Implementierung, die native, die
sieht natürlich anders aus.
Je nachdem, ob sie unter Windows unterwegs sind oder unter Linux oder
auf irgendeiner anderen Plattform.
Das heißt, wenn sie jetzt strikt nach Lehrbuch hingehen und sagen,
wenn das eine Schablone ist und Run ist der Einschub, dann muss ja in
Start irgendwo Run aufgerufen werden.
Dann sind sie gekniffen, weil dann finden sie das nicht und dann sagen
sie, nee, Quatsch, muss was anderes sein.
Aber das ist tatsächlich genau so.
Das ist nur eine, sage ich mal, Implementierungsschicht dazwischen,
die man nicht sehen kann.
Sind sie damit einverstanden?
Nein.
Also ihr Kommilitone fragt, ich habe da, oder sagt, ich habe da
reingeschaut und habe dann festgestellt, das sieht doch ein bisschen
aus wie ein Stellvertreter.
Ich sage jetzt mal nein.
Sie müssten mir sagen, was wird da vertreten und wie.
Wenn sie dafür eine gute Antwort haben, kommen sie nach der
Veranstaltung noch mal nach vorne.
Dann bin ich gespannt, aber so lange dürfen sie nachdenken.
Aber vielleicht nur mit einer Hirnhälfte, die andere in der Übung
lassen.
Okay, die dritte Frage war, es gibt ein HashSet, das habe ich falsch
geschrieben auf dem Übungsblatt, stelle ich gerade fest und dieses
HashSet hat eine Methode Clown und jetzt ist die Frage, was ist das
denn für ein Muster und insbesondere, wenn man alle anderen Klassen
anguckt, die java.lang.Clownable implementieren.
Und hier ist die Antwort, das ist ein Prototyp, aber sie müssen ein
bisschen aufpassen, nämlich Clownable als Schnittstelle ist mal wieder
leer.
Das ist nur ein Marker.
Das zwingt sie nicht dazu, die Clown-Methode zu implementieren.
Clown ist tatsächlich schon in Object drin, allerdings funktioniert es
halt nicht, weil die Unterklassen wissen ja nicht, oder Object kann
nicht wissen, wie man eine Unterklasse clownt.
Dementsprechend muss man, wenn man das Prototyp-Muster benutzen
möchte, in der Unterklasse, die den Prototyp darstellt, die Clown
-Methode selbst implementieren und die Schnittstelle Clownable, die
ist eigentlich nur der Hinweis an alle Benutzer dieser Unterklasse,
ich habe mich um Clownable, um die Clown-Methode gekümmert.
Wenn diese Schnittstelle nicht implementiert ist, dann können sie
ziemlich sicher trotzdem Clown aufrufen.
Was dann passiert, wahrscheinlich explodiert und umgekehrt, wenn eine
Klasse die Clownable Schnittstelle implementiert, dann heißt es noch
lange nicht, dass Clown funktioniert.
Das ist also tatsächlich nur ein Hinweis, aber beim Hashtable oder
beim Hashtag, wie es auf dem Blatt war, noch ein Tippfehler, da
funktioniert es.
Gut.
Hat jemand noch eine Frage zu diesem Teil?
Auch nicht.
Sehr schön.
Dann haben wir noch eine Aufgabe.
Ja, ich glaube, eine haben wir noch.
Mist.
Aber das war wieder deine, ne?
Ja, Mist.
Gut, dann muss ich weg.
Dann musst du weg.
Ja, dann mach was halt Sinnvolles in der Zwischenzeit.
Ja, ich teile Ihnen in der Zwischenzeit die Evaluationsbögen aus.
Versuchen Sie kein To-wa-bo-ho zu machen, während mein Kollege hier
das noch vorstellt.
Wir geben Ihnen nach dieser Aufgabe genug Zeit, das auszufüllen.
Ich teile es nur schon mal aus.
Das hat ja super funktioniert.
Das To-wa-bo-ho hat schon angefangen.
Gut, auf dem Übungsblatt haben sie bisher jetzt schon mal nach
Entwurfsmustern in der Java-API gesucht.
Sogar mehr oder minder zweimal.
Es gab schon, ja, bitte?
Es gibt schon eine Frage, bevor ich überhaupt angefangen habe.
Ich mache trotzdem einfach mal weiter.
Wir haben schon ein Entwurfsmuster implementieren lassen.
Jedenfalls von denen, die den Übungsschein noch nicht haben.
Und die letzte Aufgabe war jetzt mal eins zu finden oder, ja, eine
schlechte Implementierung anzuschauen und dann zu schauen, was man
denn besser machen könnte.
Hier gibt es ständig Wortmeldungen, die irgendwie was mit deinem
Verteilstil zu tun haben.
Ich weiß noch nicht genau, inwiefern.
Der ist chaotisch.
Ja, also wenn Sie noch keinen Evaluationsgebogen bekommen haben,
bleiben Sie geduldig.
Das ist wie früher im Kino mit dem Eisverkäufer.
Irgendwann kommt er auch zu Ihnen.
Dann wollen Sie schon kein Eis mehr, aber so ist das halt.
Also bei uns wurde der ja immer ohne ein Eis zu verkaufen wieder
ausgeschickt, aber... Ich habe bis jetzt auch noch kein Geld bekommen.
Du teilst die falschen Dinge aus.
Wahrscheinlich.
Gut, ich fange wieder mal an mit meiner Lieblingsfrage.
Wer hat sich denn überhaupt mit dieser Aufgabe beschäftigt?
Für die, die es nicht sehen können, ihr melden sich gerade alle.
Wieder ans schlechte Gewissen.
Wer konnte die Aufgabe denn, wie er meint, richtig lösen?
Das ist eine etwas kleinere Menge, aber auch immer noch nicht
schlecht.
Gut, also wir sehen hier ein bisschen Spaghetti-Code.
Offensichtlich nicht nur, weil er so heißt, sondern weil es auch nicht
besonders schön implementiert ist.
Insbesondere, wenn man die Kommentare rundherum liest.
Nämlich, dass diese Code-Stelle mit diesem Switch-Block noch an
anderen Stellen im Programm benutzt werden soll.
Was hat das normalerweise zur Folge, wenn ich darin irgendwas ändere
und vielleicht eine neue Art von Rechnung, was in unserem Fall hier
anlegen können möchte.
Dann muss ich das überall tun, wo ich diesen Switch-Block hin kopiert
habe aus dieser Implementierung.
Das ist einfach ein sehr großer Erwartungsaufwand.
Das will man nicht unbedingt haben.
Das geht sicherlich geschickter.
Und jetzt ist die Frage, was könnte man da tun?
Es haben sich vorhin so viele Leute gemeldet, dass sich bestimmt jetzt
ein Freiwilliger findet oder eine Freiwillige, die uns erklären kann
oder der uns erklären kann, was man denn hier geschickterweise
implementieren könnte.
Gut, also ihr Kommilitone sagt, wir könnten eine Fabrik implementieren
beziehungsweise eine Fabrik-Methode würde in diesem Fall beides
funktionieren.
Wir haben, soweit ich weiß, dann auch eine Fabrik gewählt, um das zu
implementieren und ihr Kommilitone hat vollkommen recht.
Dann hat man das nur noch an einer Stelle gebündelt im Programm und
kann diese Fabrik immer dann benutzen, wenn man eine Rechnung haben
möchte.
Auf dem Übungsblatt war, glaube ich, nur die Bonusaufgabe, das auch zu
implementieren, aber diese Begründung, die ihr Kommilitone da gerade
eben gegeben hat und ich ganz kurz zusammengefasst habe, die wollten
wir gerne haben.
So sieht das dann in der Implementierung aus.
Jemand ist tatsächlich implementiert.
Wir sehen hier unsere Rechnungsfabrik, die im Grunde genau diesen
Switch -Block trägt.
Ein bisschen hübscher sogar noch, weil man direkt Return-Statements
benutzen kann und sich deshalb die Breaks sogar noch sparen kann und
sie werden gleich sehen, wenn man das so benutzt, kann man überall im
Code dann einfach diese Fabrik benutzen und spart sich die Switch
-Blöcke.
Aber bevor wir uns das anschauen, erst mal noch zum zweiten Teil.
Nach diesen ganzen Switch-Aufrufen zur Erstellung der Rechnung sollten
ja auf dieser Rechnung noch Dinge passieren, nämlich dass ein Geschenk
hinzugefügt wird, dass Nachlass bewilligt wird und so weiter und so
fort.
Allerdings sollten gewisse Dinge davon für alle Rechnungstypen gleich
sein und für manche Rechnungstypen sollten sich die Implementierung
von dem Discount und dem Billing-Amount glaube ich, ich weiß es nicht
mehr genau, oder von dem Geschenk was glaube ich, unterscheiden und
auch das kann man natürlich geschickter lösen, als es für jede Art von
Rechnung einmal hinzuschreiben.
Und jetzt wieder die Frage an Sie.
Was könnte man denn an der Stelle tun?
Steht es vielleicht wieder oben?
Nein?
Diesmal haben wir das geschickter gelöst.
Matthias, was könnte man denn tun an der Stelle?
Ich habe echt nicht aufgepasst.
Okay.
Darfst du das Publikum befragen?
Aber ich habe den Übungsschein schon.
Du hast den Übungsschein schon?
Ja, der ist tatsächlich Voraussetzung.
Gut, dann fragen wir eben doch das Publikum, wenn Matthias nicht
möchte.
Ich fasse das mal ganz kurz zusammen.
Er hat die Musterlösung schon gesehen.
Ja, er hat die Musterlösung irgendwo schon gesehen.
Also, kurz zusammenfassend, Ihr Kommilitone hat gerade perfekt das
Entwurfsmuster erklärt, was man hier verwenden sollte.
Allerdings hat er uns nicht verraten, wie es heißt.
Weiß er anscheinend nicht.
Dafür gibt es, glaube ich, auch einen halben Punkt.
Nur für die Nennung, oder?
Ja.
Ja, ist schade für Sie.
Vielleicht kann jemand helfen.
Der Vorschlag war Strategie.
Das reicht leider nicht.
Nicht ganz.
Darf noch jemand anderes?
Ja, das ist die Schablon-Methode.
Wunderbar.
Hat jemand doch gewusst.
Ja, bitte jetzt wieder der Kollege vom Anfang.
Mehr eine Kombination, sagt Ihr Kommilitone, von was?
Ihr Kollege schlägt vor, das wäre eine Kombination von Schablone und
Strategie.
Mir ist die Strategie nicht so ganz ersichtlich an der Stelle, oder?
Mir auch nicht.
Also, für die Schablon-Methode brauchen Sie all das, was Sie erklärt
haben, nämlich die abstrakte Oberklasse, eine Methode, die die
Einschübe zusammenfasst.
Und hier haben wir zwei Einschübe und zwei festgesetzte Methoden.
Und natürlich braucht man dann sinnigerweise auch Unterklassen.
Es kann sein, dass man an einer anderen Stelle mit diesen Unterklassen
dann eine Strategie hat oder bauen kann.
Aber nur für die vier Methoden reicht die Schablon-Methode als
Erklärung.
Jetzt hat Ihr Nachbar noch eine Anmerkung.
Doppelt so laut ungefähr.
Der Kommilitone sagt, kann man die vier Methoden nicht einfach in die
Fabrik -Methode packen?
Das haben wahrscheinlich alle gehört, außer unsere armen YouTuber.
Jein.
Natürlich kann man in die Fabrik-Methode noch mehr reinstecken, wenn
man zum Beispiel sagt, das Geschenk und der Rabatt, der wird immer
sofort beim Erzeugen von so einer Rechnung berücksichtigt.
Aber zum Beispiel das Versenden der Rechnung, das würde ich jetzt
nicht unbedingt bei der Konstruktion schon machen.
Stellen Sie sich vor, Sie haben ein System, wo Sie alte Rechnungen
anzeigen können.
Und jedes Mal, wenn Sie die anzeigen, wird sie verschickt.
Das ist wahrscheinlich nicht das, was Sie haben wollen.
Also ja, man kann das nach oben ziehen, aber ich glaube, insbesondere
bei der letzten Methode ist das nicht sinnvoll.
Es hört nicht auf.
Jetzt wieder der Nachbar daneben.
Gut, bitte.
Also Ihr Kommilitone sagt, es stand in den Kommentaren explizit, dass
das immer direkt nach dem Erzeugen passiert.
Ja, da haben wir einen nicht ganz eindeutigen Kommentar.
Insofern, ja, blöd für uns.
Mit dieser Begründung lasse ich das durchgehen, mit dem nach oben
ziehen.
Das war nicht das, was gemeint war.
Aber Sie haben mich erwischt.
Blöd gelaufen jetzt.
Noch eine Frage aus der Ecke.
Ihr Kommilitone schlägt vor, das Ganze mit einem Besucher zu lösen.
Kann man bestimmt.
Vielleicht kommen Sie nach der Vorlesung mal zu uns und erklären uns,
wie.
Und dann schauen wir mal, ob das wirklich besser und effizienter ist
oder schöner oder irgendwas.
Man hätte es auch tun können, ist ihr Argument.
Ja, man kann viele Dinge tun.
Ob es dadurch wirklich schöner und besser wird, ist halt die andere
Frage.
Wir haben es jedenfalls so gelöst, mit der Schablon-Methode, auch wenn
es hier offensichtlich viele andere Vorschläge gibt.
Hier sehen Sie nochmal das, was wir gerade eben länglich diskutiert
haben, mit den beiden abstrakten Methoden, die sich tatsächlich dann
unterscheiden für die konkreten Implementierung.
Jetzt noch ganz zum Schluss, was ist eigentlich das Schöne daran, wenn
wir das jetzt tatsächlich benutzen wollen, haben wir nur noch ein
Zweizeiler, statt dem großen Switch-Block vorher und den ganzen
Methoden, die später dann auch aufgerufen wurden, um diese Erstellung
der Rechnung zu finalisieren.
Können wir jetzt, egal wo wir das im Code benutzen wollen, einfach
diese zwei Zeilen benutzen und sind fertig.
Ja, und wenn man das Argumentativ nach oben zieht in die Fabrik
-Methode, dann hat man nur noch einen Aufruf.
Insofern ist es noch kürzer.
Ob das jetzt semantisch überall sinnvoll ist, ist die nächste Frage.
Also gerade mit dem Argument, man möchte vielleicht nicht immer genau
das tun, auch wenn es in den Kommentaren so stand, kann man schon
sagen, dass es mit der Variante, die Schablonen-Methode noch
zusätzlich zu benutzen, doch ein Tick hübscher ist.
Hier noch mal das Ganze als Klassendiagramm.
Ich würde ja sagen, das ist nur für Sie zur Übersicht.
Ich glaube, da muss ich jetzt nicht nochmal darauf eingehen, außer
vielleicht auf die Sichtbarkeiten.
Auch nicht.
Matthias schüttelt den Kopf.
Dann lasse ich das.
Also ich finde, man könnte darüber diskutieren, wie die Sichtbarkeiten
sind.
Also offensichtlich brauchen wir eine Methode, die wir als Schablonen
-Methode benutzen.
Das ist bei uns die Prepare-and-Send-Methode.
Die muss offensichtlich public sein, damit man sie benutzen kann.
Und die darunter liegenden Einschubmethoden für Ihre Schablone, die
sollten Sie so weit einschränken, wie Sie die einschränken können.
In dem konkreten Beispiel, wo wir tatsächlich mit den Methoden sonst
nichts anderes tun, sollten die wahrscheinlich protected sein.
Dann kann ich sie noch überschreiben, was ich ja tun muss.
Aber ich kann sie nicht noch von irgendwo anders her benutzen.
Das ist eigentlich das, was man an der Stelle haben möchte.
Wenn Sie diese einzelnen Einschübe noch irgendwo anders benutzen
wollen, wenn Sie ja das Geschenk hinzufügen wollen, ohne den Rest zu
benutzen, dann könnte die auch public sein.
Zwei Kommilitonen gehen schon.
Ich hoffe, Sie haben den Eval-Bogen trotzdem ausgefüllt.
Daumen geht hoch.
Ich glaube, das jetzt einfach mal und mache weiter.
Ich glaube, der Punkt ist klar geworden mit den Sichtbarkeiten immer
so weit wie möglich einschränken.
Insbesondere hier an der Stelle, wo wir die Einschübe benutzen wollen.
Ich würde ja sowieso grundsätzlich immer protected benutzen, aber das
haben wir länglich diskutiert und sind uns nicht ganz einig geworden.
Deshalb stehen hier die Fragenzeichen ganz um L nicht konform.
Gut, bitte da kommt eine Frage.
Ihr Kommilitone merkt an, man könnte ja die AddGift und die
CalculateDiscount -Methode einfach leer implementieren in der
Oberklasse.
Dann könnte man sie einfach dort, wo man sie braucht, überschreiben.
Das kommt so ein bisschen darauf an, was Sie denn haben möchten.
Also zum einen haben Sie dann das Problem, dass Sie die Unterklasse
nicht mehr dazu zwingen können, die Methode zu überschreiben.
Das haben Sie im abstrakten Fall.
Die andere Sache ist, so wie wir uns das gedacht haben, sollte die
immer angepasst werden.
In unserem Beispiel war das nicht so vorgegeben, dass man das tun
musste, weil nur in den speziellen Fällen von dem Premiumnutzer was
hinzugefügt werden sollte.
Das heißt, Sie haben in dem speziellen Fall recht.
Im allgemeinen Fall würde ich es eher so implementieren, sodass ich
alle Unterklassen dazu zwinge, die speziellen Bestandteile, also die
speziellen Einschübe der Schablonmethode tatsächlich zu
implementieren.
Aber Sie sehen schon, das kommt so ein bisschen auf den Anwendungsfall
an und das ist auch genau die Sache mit den Entwurfsmustern.
Die sind nie so fertig, dass man sie einfach benutzen kann, sondern
passen Sie es an auf Ihren Fall und in dem Fall haben Sie recht,
könnte man das so bauen.
Ich fasse das noch mal zusammen und wenn ich damit fertig bin, sagen
Sie mir, ob ich Sie richtig verstanden habe.
Sie sagen, wenn es Methoden gibt, die nicht ausschließlich in der
Rechnungsklasse oder einer dieser Unterklassen implementiert und auch
aufgerufen werden, dann könnte man hingehen und das Strategie nennen,
weil ich muss ja von außen darauf zugreifen und dann könnte ich sowas
machen wie die Create Bill ist eine Methode der abstrakten Strategie
und in den Unterklassen ist es dann die konkrete Implementierung.
Ist das das, was Sie...
ja genau, okay.
Ja, ich glaube, das kann man schon machen.
Ich glaube, was Sie dann hinkriegen, ist so eine Mischung aus Fabrik
und der Strategie.
Sie sind noch nicht ganz angekommen bei der abstrakten Fabrik, weil
wahrscheinlich haben Sie keine zwei separat getrennten
Vererbungsbäume, die Sie mit den abstrakten Fabriken dann erzeugen.
Da müsste man ein bisschen hinschauen, wie das dann tatsächlich
aussieht.
Wenn Sie fragen, weil Sie das so gemacht haben und so begründet haben
in Ihrer Lösung, dann kommt es sehr stark auf die Begründung an, weil
das war ja, wenn ich mich recht entsinne, im Aufgabenblatt auch
gefordert.
Also welches Muster kann man nehmen und warum und was muss ich tun?
Wenn Sie das sinnvoll begründet haben und davon gehe ich jetzt einfach
mal aus, dann haben Sie da wahrscheinlich wenig zu befürchten.
Wir haben uns gedacht, wir gehen den einfachsten Weg und das ist der,
der uns eingefallen ist.
Ich habe noch ein Kapitel dabei, das heißt Programmierrichtlinien.
Das heißt, ich mache jetzt nicht weiter mit der Parallelität, die
Professor Tichy angefangen hat.
Die wird da auch mit Ihnen zu Ende machen, aber wir haben hier noch
ein paar, sage ich mal, relativ weiche Folien, die wir sehr gut an
diese weiche Übung anschließen können.
Programmierrichtlinien haben Sie schon kennengelernt.
Warum macht man das?
Naja, konsistenter Stil erleichtert die Lesbarkeit.
Wenn Sie ein konsistent entwickeltes Programm haben, auch auf Code
-Seite, dann ist es einfacher, sich da rein zu arbeiten.
Sie haben das in der Übung selbst erleben müssen, wenn man sowas
kriegt wie JMJ-RST.
Ist nicht so ganz konsistent, nicht so ganz super, dann ist es halt
schwierig.
Und insbesondere, wenn Sie sich auf Fehlersuche begeben, dann ist es
natürlich schön, wenn das Programm schön geschrieben ist.
Darüber hinaus, ein sauberer Stil sollte sowas sein wie Ehrensache.
Stellen Sie sich vor, Sie sind Fliesenleger.
Die Fliesen sauber zu verlegen, das ist Ehrensache, auch wenn die im
Keller sind.
Wenn Sie Software entwickeln, Software sauber entwickeln, ist
Ehrensache, auch wenn das die hinterletzte Backend-Funktion ist, in
die bestimmt nie irgendjemand reinguckt.
Weil das stimmt nicht, da wird jemand reingucken, ganz sicher.
Wenn Sie Pech haben, sind Sie selbst in zwei Jahren und dann fragen
sich, wer hat das denn gemacht?
Und wenn Eclipse automatisch die Java-Doc-Kommentare dran geschrieben
hat, dann wissen Sie, oh Mist, das war ich selbst.
Blöd.
Also gucken Sie, dass Sie das sinnvoll machen, aber denken Sie auch
dran, wenn da eine Regel ist und die ist schwarz-weiß formuliert, dann
ist sie sicherlich nicht schwarz-weiß anzuwenden.
Stellen Sie sich vor, Sie haben eine Regel, Quelltext darf nur 80
Zeilen breit geschrieben werden, dann muss ein Zeilenumbruch kommen
und Sie haben jetzt einen Strichpunkt als letztes Zeichen und das hat
die Nummer 81 und alle Ihre Kollegen haben 22 Zoll widescreen
Displays.
Dann ist es vielleicht nicht ganz so schlimm, wenn Sie das da
hinschreiben.
Wenn Ihre Kollegen alle remote auf einem Terminal arbeiten, auf so
einem Texting mit 80 Zeichen Breite, dann ist das wichtig.
Also Sie müssen mit Ihrem Team einigermaßen einen Konsens haben,
welche Regeln sind wirklich essentiell, woran muss man sich ohne Wenn
und Aber halten und welche kann man mit Augenmaß anwenden.
Es gibt sogar Stellen im Quelltext, wo Sie vielleicht mal die
automatische Code-Formatierung ausschalten, weil Sie ein Integer-Array
immer nach der vierten Stelle umbrechen, weil das eine Matrix wird und
dann kann man es halt hübsch lesen.
Vielleicht ist es sinnvoll, sowas zu tun, kommt aber auf den
Einzelfall an.
Welche Programmierrichtlinien Sie benutzen, ist mir ehrlich gesagt
herzlich egal.
Das ist wahrscheinlich auch Ihnen herzlich egal, es sei denn, Sie sind
ein flammender Verfechter vom Nu-Stil von Emacs.
Dann bitte überzeugen Sie alle, Ihre Kollegen, mit denen Sie
zusammenarbeiten, das auch so zu machen.
Das ist auch der wichtigste Punkt.
Es nützt nichts, wenn Sie den Stil A benutzen und Sie den Stil B und
Sie machen das konsistent, aber Sie arbeiten an einem gemeinsamen
Projekt.
Sie müssen immer auf Team-Ebene denken.
Gucken Sie, dass Sie alle den gleichen nehmen und ob Sie jetzt nach
der Bedingung umbrechen und dann die geschweifte Klammer hinschreiben
oder nicht, ist wirklich völlig egal.
Sie müssen es nur immer gleich machen.
Die Stile sind nicht klausurrelevant.
Sie müssen nicht wissen, ob beim BSD-Style ein Zeilenumbruch kommt
nach der schließenden runden Klammer oder nicht.
Das ist einfach nur eine Folie, die zeigt, es gibt Unterschiede und ob
man das so haben will oder nicht, ist letzten Endes Geschmack.
Aber Sie müssen sich einigen und dass Sie sich einigen müssen mit
Ihrem Team oder dass Sie, wenn Sie Teamleiter sind, einen Stil
vorgeben, das ist durchaus klausurrelevant.
Wenn da eine wahre Falschfrage ist und da steht, es ist egal,
Hauptsache jeder Entwickler folgt seinem persönlichen Stil und Sie
kreuzen da wahr an, dann kriegen Sie den Punkt halt nicht.
Und noch ein bisschen schlimmer, bei wahre Falschfragen in Klausuren,
da gibt es Abzug.
Also wenn Sie was Falsches ankreuzen, dann verlieren Sie einen halben
Punkt.
Das ist vielleicht ein recht übungsnaher Hinweis.
Bei wahre Falschfragen, wenn Sie keine Ahnung haben, einfach mal
Klappe halten, nichts ankreuzen.
Ich meine das ernst.
Es gibt Leute, die machen bei einer Aufgabe mit zehn wahre
Falschfragen, machen sie fünf richtig und fünf haben sie
offensichtlich geraten.
Was kommt raus?
Höchstwahrscheinlich null Punkte.
Das ist ein bisschen ärgerlich.
Also im Mittel, wenn die Kreuze gleich verteilt sind und gut gestreut,
kommen sie vielleicht noch auf zweieinhalb.
Aber das ist halt ungeschickt.
Sie bekommen einen halben Punkt für jede richtige und minus einen
halben für jede falsche.
Und bei fünf richtigen und fünf falschen?
Der Erwartungswert ist gerade null.
Glauben Sie, der Erwartungswert, wenn Sie wirklich raten müssen, der
ist kleiner null.
Und ich garantiere Ihnen nicht, dass das gleich verteilt ist.
Und ich garantiere Ihnen auch kein Ankreuzmuster.
Also war falsch und war falsch ist immer abwechselnd.
Oder ist es immer war falsch, falsch war.
Es gibt Leute, die haben solche Theorien und die sind alle falsch.
Sie können nicht davon ausgehen, dass bei einer war falsch Aufgabe mit
zehn Fragen fünf wahr sind und fünf falsch.
Sie können noch nicht mal davon ausgehen, dass ungefähr gleich viel
war falsch drin ist.
Es könnte sein, wir machen eine Aufgabe, da sind zehn Kreuze auf der
Wahrseite.
Nicht so ganz wahrscheinlich, weil der Erwartungswert für jemanden,
der null Ahnung hat, wird dadurch deutlich höher.
Aber sie können da sich auf nichts verlassen.
Außer für jede falsche Antwort gibt es einen halben Punkt Abzug
innerhalb dieser Aufgabe.
Also wenn sie fünf Punkte haben, möglicherweise bei einer war falsch
Frage und sie kreuzen alles falsch an, dann kriegen sie null.
Nicht minus fünf, sondern null Punkte.
Ja wunderbar und schon hat es ihr Kommilitone gelohnt.
Das schneiden wir aus der YouTube Aufzeichnung aus, oder?
Ich glaube, das ist gar nicht nötig, weil das erscheint ja nicht unten
bei der Auswahl mit an Zeitpunkt 658 springen, um zu hören, was
Matthias über war falsch Frage zu sagen hat.
Das ist wahr.
Das gibt es ja zum Glück nicht.
Aber was ich viel interessanter finde, ist, dass es ihr Kommilitone
geschafft hat, eine Frage zu stellen, die du mit ist überhaupt nicht
klausurrelevant beantwortet hast und dann wird es hochgradig
klausurrelevant.
Das finde ich schon sehr interessant.
Ja, bemerkenswert ist auch der Geräuschpegelverlauf.
Da ist es ungefähr so.
Ja und jetzt geht es wieder hoch.
Okay, machen wir also weiter mit der nächsten Folie.
Codeformatierer haben sie in jeder IDE.
Ich hoffe, sie haben den von Eclipse oder von IntelliJ benutzt in der
Übung.
Wir haben ihnen auch gesagt, man kann das Ding anpassen und wenn sie
sowas haben wie Checkstyle, dann können sie sich den Formatierer
generieren lassen aus den Checkstyle-Regeln.
Sorgen sie dafür, dass ihr Team dieselben Einstellungen hat.
Sonst wären sie zum Hirsch.
Ich glaube, in einer Übung hatten wir sogar mal den Vorschlag, dass
man so...
Genau, man könnte das ja machen und Jamf JST aufräumen.
Ja, kann man machen.
Der Punkt ist, wenn sie die Regeln anpassen an ihre Bedürfnisse und
sie passen die Regeln an ihre Bedürfnisse an und das erste, was sie
instinktiv machen, wenn sie sich was aus dem Git holen ist,
formatieren, dann haben sie keine Chance mehr herauszufinden, was in
dieser Änderung eigentlich die Änderung war und was war die
Formatierungsänderung.
Also sorgen sie dafür, dass es im Team konsistent ist, sonst wird es
hochgradig blödsinnig.
Das haben sie hoffentlich schon gesehen.
Es gibt ein paar Coding-Conventions, die sind in Eclipse schon dabei.
Zum Beispiel die Java-Coding-Conventions und ich glaube, es gibt die
offiziellen Sun oder Oracle, je nachdem, was da gerade en vogue ist.
Und sie können das natürlich anpassen auf ihre Bedürfnisse und sie
können Eclipse auch sagen, bitte, wenn ich die Datei speichere, dann
formatiere ich alles oder formatiere ich alles, was ich geändert habe,
vorm Speichern.
Dann haben sie das immer schön konsistent und dann wird das Ganze
hübsch.
Was haben wir noch?
Einen Einstellungsdialog.
Vielleicht kann es noch sein, dass man es exportieren kann.
Man kann es exportieren.
Das ist in der Tat genau der Punkt, wo man den Kollegen auf die Nerven
gehen kann.
Das ist total klasse.
Also wer diesen Screenshot gemacht hat, der gehört also geschüttelt.
Also es gibt Neu, es gibt Importieren und es gibt auch noch einen
Export -Knopf und ich glaube, der ist hier.
Jedenfalls, sie können das Einstellen, Exportieren in ihr Projekt-Git
legen und sagen, jeder der was macht, der holt sich das vorher raus.
Sie können Eclipse sogar so einstellen, dass Änderungen an dieser
Datei, wenn die aus dem Git kommen, automatisch aktualisiert werden im
Eclipse, dass sie das also immer up-to-date haben.
Das klingt jetzt vielleicht ein bisschen trivial, aber wenn sie das
erste Mal mit jemanden zusammengearbeitet haben in einem Git, der
einen anderen Formatierstil hat und den auch durchsetzt mit seinem
Formatierer, dann drehen sie quasi durch, weil der macht dann eine
Zeilenänderung, vielleicht nur eine halbe und trotzdem ist die
komplette Datei geendet und das wollen sie nicht haben.
Deshalb sagen sie, mein Stil ist der bessere und dann wird er bestimmt
sagen, ja, du hast recht.
Wenn nicht, dann überzeugen sie ihn halt.
Also wenn sie die Chance haben, dann legen sie es fest.
Das ist vielleicht auch ein Hinweis fürs PSE.
Da werden sie zu fünft, zu viert oder zu sechst an einem Projekt
arbeiten und sie tun sich wirklich einen sehr großen Gefallen, wenn
sie dafür sorgen, dass alle den gleichen Formatierstil benutzen.
Insbesondere, wenn sie sagen, ich bin der Beste in dieser Gruppe und
die Pfeife, die macht nur hier meine Getter und Setter und kommentiert
die und das war knapp vorbei.
Wir haben nicht zusammen PSE gemacht, das muss man dazu sagen.
Wenn sie das durchsetzen, dann ist das völlig okay.
Es ist fragwürdig für den Team Aspekt im PSE, aber inhaltlich für die
Programmierung ist das in Ordnung.
Aber wenn ihnen die Pfeife dann beim Getter und Setter schreiben, die
gesamte Datei ändert, sie drehen durch, glauben sie es mir, spätestens
dann.
Was man alles einstellen kann, sehen sie in diesem Dialog und sie
sehen, der hat sehr viele Reiter, die werden wir natürlich nicht
angucken und um die Frage vorweg zu nehmen, nein, auch das ist nicht
klausurrelevant, schauen sie einfach mal drauf, wenn sie das noch nie
gesehen haben.
Bitte gucken sie sich an, was man da alles machen kann.
Das haben sie hoffentlich auch gefunden.
Man kann aus Checkstyle ein Formatierungsprofil generieren.
Ich glaube, wir haben das in der allerersten Veranstaltung sogar
gesagt oder gezeigt.
Vielleicht war es auch die zweite.
Insofern ist das auch nur mäßig spannend und dann ist dieser
Foliensatz auch schon vorbei.
Aber sie haben hoffentlich was fürs Leben gelernt.
Wenn nicht, dann fürs PSE und wenn auch dafür nicht, dann für die
Klausur mit den Wahr-Falschfragen.
Es soll keiner leer ausgehen hier und wenn sie gar nichts gelernt
haben, dann durften sie immerhin einen E-Wahlbogen ausfüllen.
Und wer keinen E-Wahlbogen bekommen hat, bitte noch mal melden.
Der nächste Punkt, das nächste ebenfalls weiche Kapitel ist
selbstkontrolliertes Programmieren.
Das hat auch ein bisschen was mit Selbstdisziplin zu tun.
Wenn man das allerdings macht, erwachsen daraus Fähigkeiten oder
Kompetenzen, die durchaus wichtig sind.
Wichtig ist, die erfahrenen Programmierer, die lernen aus ihren
Fehlern.
Das sollten auch die unerfahrenen Programmierer tun, wie ich finde.
Sie sollten sich an Regeln halten und sie sollten diese Regeln auch
aufstellen und aktualisieren.
Und dadurch entsteht eben ein Regelkreis.
Sie machen einen Fehler, sie gucken den an, sie analysieren die
Ursache und leiten daraus eine Regel ab.
Was muss ich tun oder was darf ich nicht mehr tun, damit solche Fehler
in Zukunft nicht mehr auftreten, also mir nicht mehr passieren.
Das könnte zum Beispiel sein, ich mache eine bedingte Anweisung.
If true, dann tu irgendwas.
Und weil dieses tu irgendwas nur eine Zeile ist, spare ich mir die
geschweiften Klammern.
Ist ja viel übersichtlicher.
Bis sie dann feststellen, jemand fügt in diesen bedingten
Kontrollfluss eine weitere Anweisung an und das macht er vor ihrer
Anweisung.
Er macht aber keine Klammern dazu.
Was passiert?
Die Einrückung ist dem Java Compiler herzlich egal.
Es ist jetzt eine bedingte Anweisung, nämlich die alte und die neue.
Und die alte, die wird jetzt immer ausgeführt.
Bei if true nicht so furchtbar schlimm, aber wenn da wirklich eine
Bedingung steht, dann sind sie gekniffen.
Und solche Fehler zu finden hinterher ist äußerst schwierig.
Die Regel würde also lauten, wann immer eine Bedingung kommt oder eine
Schleife und danach kommt ein Block, dann macht geschweifte Klammern
hin.
Auch wenn der Block nur eine Zeile hat und deswegen die Klammern
optional sind.
Das wäre so eine Regel, die sie sich selbst ableiten könnten.
Und dann könnten sie zu ihrem Pfeifenentwickler gehen und sagen, du
machst es jetzt auch so.
Vielleicht als kleiner Hinweis an der Stelle.
Das kann man tatsächlich auch einstellen, dass das durchgesetzt wird.
Das kann man auch einstellen.
Eclipse zum Beispiel.
Man kann es in Eclipse einstellen, dass es beim Speichern durchgesetzt
wird.
Sie können das bei Versionskontrollsystemen einstellen.
Sie können nämlich verhindern, dass jemand was ins Depot einbucht,
wenn er sich nicht an die Regeln hält.
Das ist... also er lacht schon, er würde es sofort machen.
Ja, alle meinen Stil.
Das kann man machen.
Sie laufen da halt in das Problem rein, dass ihnen die Entwickler
wegrennen, weil wenn die nicht mehr committen können, dann können sie
nicht mehr arbeiten.
Und wenn der freitags nicht committen kann und dann fährt er heim und
dann fällt ihm das Notebook runter und dann ist die ganze Wochenarbeit
weg, dann ist teuer.
Also solche expliziten harten Schranken einzuführen, bin ich kein Fan
von.
Aber so ein Formatierungsprofil anlegen und das durchsetzen, auch mit
Disziplinarmaßnahmen, darf nicht mit auf die Weihnachtsfeier oder
sowas.
Das finde ich super.
Oder viel schlimmer, muss die Weihnachtsfeier organisieren.
Okay, das ist dieser Regelkreis, von dem ich sprach.
Idealerweise kommen sie am Ende von diesem Regelkreis zu korrekten
Ergebnissen.
Das ist aber, denke ich, einleuchtend.
Das sollte man auch ohne Regelkreis hinkriegen.
Typische Fehler, die man so macht, das sind oft ByOne-Fehler, das sind
Null -Pointer-Exceptions, die kriegen sie bei Java einigermaßen gut
weg.
Wenn sie mit irgendwas arbeiten, wo sie Zeigermanipulationen machen,
dann wird es ein bisschen blöder.
Aber das sind alles Dinge, die man sich anguckt.
Und das Problem ist, dass Grenzfälle und Ausnahmen und Annahmen, die
man so als Entwickler trifft, das sind meistens nur die Fälle, die sie
für repräsentativ halten.
Sie persönlich, aus ihrer eigenen Erfahrung.
Und das sind meistens nicht die Fehler oder nicht die Probleme, die
tatsächlich häufig auftreten.
Das sind nur die, die sie persönlich am meisten gekniffen haben.
Und deswegen sollten sie sowas wie ein Protokoll führen.
Was gibt es noch?
Es gibt falsche Hypothesen.
Es könnte sein, dass sie vor 40 Jahren angefangen haben zu
programmieren und da war es so, mach eine Schleife mit Additionen.
Das ist viel billiger als eine Multiplikation hinschreiben.
Das ist vielleicht auf irgendwelchen obskuren oder sehr altertümlichen
Architekturen richtig.
Heute ist es eher Quatsch.
Und wenn das so ist, dann sollte sich eigentlich der Übersetzer darum
kümmern, sowas auszurollen.
Das ist also eine Regel, die man eigentlich sich nicht mehr machen
sollte.
Das gleiche gilt für potenzieren, ist wesentlich aufwendiger als
multiplizieren.
Gleiche Idee, ich rolle das ganze aus.
Und ein ganz großer Punkt, Cache-Effekte.
Eigentlich mit Bindestricht zwischen Cache und Effekt.
Die sind unwichtig.
Das ist eine Behauptung, wenn die Ihnen jemand sagt, nehmen sie ihn,
schütteln sie ihn.
Das ist Quatsch.
Der Cache ist so unglaublich schnell.
Wenn sie Cache-Effekte erzielen können, positive, dann nehmen sie die
mit.
Sie kriegen damit ein Speedup hin.
Der ist viel, viel wichtiger und viel größer als den, den sie kriegen
können mit Parallelität.
Insbesondere, wenn sie Dinge anhand der Cache-Größe ausrichten können.
Also Arbeitspakete machen, die in den Cache passen, dann profitieren
sie ganz extrem von diesem Beschleunigungsvorteil.
Also die Aussage, wie groß der Cache ist, ist mir egal.
Das ist eher blöd.
Aber auch hier gilt wieder, wenn das in zehn Jahren passiert, dass
Ihnen das jemand sagt, dann könnte die Regel falsch sein.
Dann schütteln sie, aber schütteln sie nur so lange, dass er hinterher
noch sprechen kann, denn vielleicht hat er gute Argumente.
Vielleicht sagt er, heutzutage macht das der Übersetzer.
Ja, dann macht schon jemand das Schütteln vor, danke.
Also, was ich Ihnen damit sagen möchte, ist eigentlich nur, wenn Sie
Regeln anlegen, überprüfen Sie die regelmäßig.
Seien Sie nicht gefangen in Ihren eigenen Regeln, die Sie irgendwann
mal aufgestellt haben, sondern bleiben Sie am Zahn der Zeit und
überprüfen Sie, ob das überhaupt noch seine Richtigkeit hat, was ich
da als Regel aufgestellt habe.
Insbesondere, wenn ein Jüngerer kommt, der gerade erst fertig ist mit
seiner Ausbildung oder in der Ausbildung, der kommt und sagt, die
Regel ist Quatsch, das gilt heute nicht mehr.
Dann hören Sie ihm mal zu.
Wenn Sie jetzt der Jüngere sind, dann sagen Sie vielleicht, bitte
Chef, hören Sie mir mal zu, das ist Quatsch, was wir hier optimieren.
Vielleicht noch als Tipp, den Chef nicht schütteln.
Ja, oder halt auf der Weihnachtsfeier, da könnte das okay sein, aber
wahrscheinlich eher nicht.
Stimmt.
Gut, was haben wir noch?
Drücken der Maschinenarithmetik, das haben Sie auch schon gesehen oder
das müssen Sie jetzt auf dem nächsten Übungsblatt zeigen.
Sie können reelle Zahlen nur mit begrenzter Genauigkeit darstellen.
Es gibt Brüche, die kann man einfach nicht darstellen und sowas hier
ist halt irgendwie blöd.
Das gleiche gilt natürlich, wenn Sie mit Geldbeträgen arbeiten und Sie
sagen, hey, es gibt Euro und Cent, von Cents gibt es 99 Stück plus die
Null, da reicht ein Double ohne Probleme aus.
Das ist falsch, das reicht nicht.
Nehmen Sie, wenn Sie mit Geld arbeiten, Ganzzahlen und nehmen Sie die
letzten beiden Stellen für die Centbeträge und zeigen Sie in der GUI
halt einen Punkt an der richtigen Stelle an, sonst sind Sie gekniffen.
Sie kriegen dann Rundungsfehler oder Dinge, die auf irgendwie der 10.,
12.
Dezimalstelle erst relevant werden und dann kommt jemand und sagt,
mein Konto ist im Minus, ich kriege kein Geld mehr und dann stellen
Sie fest, das ist irgendwie ein Millionstel Cent.
Vielleicht blöd.
Also gucken Sie, dass Sie da die richtigen Typen nehmen.
Was haben wir noch?
Ihre führenden Namen.
Gucken Sie, dass Sie Variablen so benennen, dass jemand, der das
liest, auch weiß, was drinsteht.
Das ist, denke ich, was, was ich Ihnen nicht wirklich erklären muss.
Wichtig ist, wenn Sie sich mit Bedingungen auseinandersetzen und Sie
haben nicht If A, Else B, sondern Sie haben irgendwie
Bedingungsschachtelung und Sie haben noch eine Schleife drumherum und
sowas.
Furchtbar unübersichtlich.
Es ist höchstwahrscheinlich, dass Sie irgendeinen Randfall nicht
bedacht haben und zwar nicht beim Hinschreiben des Codes.
Er macht wahrscheinlich genau das, was Sie wollen, aber beim
Überlegen, was sind denn überhaupt die ganzen Fälle, die es gibt und
was sind die Kombinationen, die möglich sind.
Also machen Sie sich da Notizen auf Papier oder sowas.
Das ist einfach schwierig, logische Bedingungen, Komplexe aufzustellen
und dann aufzulösen.
Das machen wir nicht einfach so im Kopf.
Nehmen Sie sich da einfach ein Stück Notizpapier und dann ist gut.
Das ist was, was Sie beim Debuggen wahrscheinlich häufig mal treffen.
Sie gucken sich eine Variable an und denken, da steht 17 drin, weil
das ist immer so, aber in dem einen Fall, wo es halt knallt, da steht
da 17,2 drin und Sie wissen nicht genau, woher, weil die Variable wird
an 400 Stellen im Programm benutzt und Sie wissen nicht genau, wo sie
nur gelesen wird, wo sie geschrieben wird und wie auch immer.
Vielleicht hat auch irgendjemand mal vergessen, das Ding zu
initialisieren.
Dann haben Sie vielleicht einen falschen Startwert.
Sowas nachzuvollziehen ist einigermaßen hässlich.
Also gucken Sie, dass Ihre Programme da so einfach wie möglich
gestrickt sind, so übersichtlich wie möglich gestrickt sind.
Wichtige Nebensachen.
Wir haben jetzt nur ganz, ganz wichtige Hauptsachen gehört.
Wenn Sie hergehen und sagen, der Teil meines Programms, das ist, das
ist das Kernfeature.
Da muss wirklich alles gehen.
Wenn da was hakt, dann schreit der Kunde und das sind irgendwie zehn
Prozent von der Anwendung und Sie sagen das auch all Ihren Entwicklern
und auch Sie sagen es all Ihren Testern, dann konzentrieren Sie sich
auf die zehn Prozent.
Was passiert?
Die 90 Prozent, die die einfach sind, die enthalten dann Fehler und da
guckt auch keiner nach, weil das sind ja die unwichtigen 90 Prozent.
Der Kunde, der macht das Programm auf und dann knallt's, weil der
klickt sofort auf das, was er jeden Tag braucht.
Nicht was der Spezialfall ist, sondern das, was halt benutzt wird.
Sie können nicht Teile des Programms degradieren und sagen, das ist
nicht so wichtig, das wird nur selten benutzt oder das ist eh einfach.
Solche Annahmen sind in der Regel falsch.
So, was haben wir noch?
Trügerische Redundanz.
Wenn Sie copy and paste benutzen beim entwickeln, dann erzeugen Sie
diese Redundanz.
Das ist natürlich irgendwie unschön, aus vielen verschiedenen Gründen.
Das eine ist, Sie duplizieren Code.
Das heißt, Sie müssen, wenn da ein Fehler drin ist oder wenn sich da
was ändert, das an allen Stellen ändern, wo es hin kopiert worden ist.
Wenn Sie es wiederfinden, ist gut.
Wenn Sie es nicht wiederfinden, ist schlecht.
Vielleicht werden auch Teile davon weiter kopiert vom nächsten
Entwickler, der copy and paste macht.
Dann haben Sie keine Chance, das mehr nachzuvollziehen.
Also nutzen Sie da wirklich die Möglichkeiten, die Ihnen moderne
Programmiersprachen bieten.
Machen Sie für alles, was groß ist und groß heißt in dem Fall drei
Zeilen vielleicht, vier Zeilen, was mehrfach gebraucht wird.
Hier steht zwei Zeilen, machen Sie da eine eigene Methode für.
Es gibt einen Punkt, den man da gerne übersieht, den man aber sehr,
sehr elegant lösen kann.
Das sind tatsächlich Bedingungen.
Sie können hinschreiben, if a größer 18 und b gleich true und c gleich
5, dann mach irgendwas.
Sie können aber auch hinschreiben, if ist volljährig und hat seit fünf
Jahren einen Führerschein.
Und die Methode gibt einen boolschen Wert zurück und die prüft halt
die drei Variablen.
Sie schaffen dadurch Lesbarkeit.
An der Bedingung steht dann, wenn der Mensch volljährig ist und seit
fünf Jahren einen Führerschein hat und jeder weiß, was das bedeutet,
wie das berechnet wird, ist ja völlig egal.
Wenn da nur Zahlen stehen und Konstanten, die 18, was bedeutet die?
Ist es überhaupt Volljährigkeit?
Wenn Volljährigkeit auf 16 gesenkt wird, müssen sie nach allen 18ern
suchen in ihrem Programm.
Viel Spaß!
Das sind auch Dinge, die kann man zu Methoden machen und dann wird es
lesbar und dann wird es auch wartbar.
Insbesondere, wenn man diese Bedingungen an mehreren Stellen braucht.
Denkfallen vermeiden, das heißt gucken sie, dass sie nicht immer
wieder in dieselben Probleme rennen, wie Sebastian das gesagt hat.
Wenn sie eine Regel aufgestellt haben, überprüfen sie die auch mal, ob
die noch gilt und das können sie natürlich am besten machen, wenn sie
sowohl die Regeln als auch die Fehler, die sie machen, aufschreiben.
Kannst du vielleicht noch mal kurz eine Folie zurückgehen?
Den letzten Punkt, da würde ich gerne noch mal was zu sagen.
Also aus meiner Erfahrung mit Bachelor und sagen wir mit
Abschlussarbeitern, kann ich sagen, das ist mit das Allerschlimmste,
wenn jemand etwas ändert, weil er festgestellt hat, oh ja, das könnte
man ja eigentlich irgendwie besser lösen und dann den Algorithmus
komplett ändert und die alten Kommentare stehen lässt.
Wenn man dann nämlich über den Code geht und sich auf die Kommentare
verlässt, dann wird es lustig.
Dann findet man den Fehler, der vielleicht bei der Neuimplementierung
aufgetreten ist, nie.
Also wenn sie etwas ändern, ziehen sie immer die Kommentare nach.
Dann ist es sogar besser, die Kommentare wieder zu löschen, weil dann
weiß man, okay, hier hat sich noch keiner die Mühe gemacht, seinen
Quellcode zu kommentieren.
Wenn da Kommentare sind, dann liest man die und dann verlässt man sich
darauf, dass die richtig sind.
Also ich finde das relativ schwerwiegend.
Ich bin da schon häufig auf die Nase gefallen.
Ja, gucken sie, dass sie ihren Abschlussarbeitsbetreuern, ihren
zukünftigen Kollegen und den Leuten, die ihre Bibliotheken nutzen,
nicht allzu viele Stolperfallen stellen.
Das hat auch was mit Ehrensache zu tun.
Irgendwann macht mal jemand ein Programm von ihnen auf und da steht
ihr Name drin.
Mittlerweile sind sie CIO von irgendeiner tollen Firma und der Code
sieht aus, also wenn das jemand auf Facebook hochlädt, dann haben sie
ein Problem.
Sowas sollte nicht passieren.
Gucken sie, dass sie ihren Keller sauber halten.
Gut, Fehler-Logbuch.
Ich glaube den Rest von der Folie hatte ich im Wesentlichen schon so
abgerissen.
Was sollte man denn in so ein Fehler-Logbuch reinschreiben?
Wer hat denn eine Idee?
Ja.
Okay, die Antwort war vermutlich alles.
Ja, das stimmt.
Können sie dieses alles ein bisschen präzisieren?
Okay, also ihr Kommilitone sagt, alles heißt jeder Fehler, der so
auftritt und dann... Ja, stimmt.
Was ich eigentlich eher meinte war, wenn das eine Tabelle ist und
jeder Fehler ist eine Zeile, was sind denn die Spalten?
Also was müssen sie über einen Fehler wissen, damit sie mit dem
irgendwas anfangen können?
Was wären denn gute Ideen?
Okay, also was den Fehler hervorbringt, das ist sowas wie, wie kann
ich den reproduzieren?
Ja, vielleicht auch, was ist die Ursache?
Und zum Beispiel, ich habe mal wieder versucht, irgendwas in Java zu
machen, was nur in C geht oder umgekehrt.
Also ihr Kommilitone sagt, in welcher Version dieser Fehler
aufgetreten ist, weil ich ja wissen muss, sind irgendwie jüngere
Programmstände anfälliger als spätere zum Beispiel, was natürlich
stimmt.
Was auch eine gute Notiz wäre, ist, wo ist der Fehler überhaupt
aufgetreten?
Also an welcher Codestelle, in welchem Modul?
Hat das was mit Datenbank zu tun oder mache ich den Fehler immer nur,
wenn ich, was weiß ich, in Dateien schreibe?
Das sind alles Dinge, die sie notieren sollten.
Und was vielleicht ein anderer, eine andere Dimension ist, wie
schwerwiegend war denn der Fehler?
Ist das ein Rechenergebnis, das falsch ist und das wird nur irgendwo
im Debug ausgegeben?
Wahrscheinlich nicht ganz so schwerwiegend.
Stürzen Flugzeug ab?
Wahrscheinlich ziemlich schwerwiegend.
Andere Dimension ist, wie lange haben sie denn gebraucht, um den
Fehler zu finden?
Wenn sie sagen, na ja, ich habe halt die zehntel Sekunde gebraucht,
bis ich festgestellt habe, oh, ich habe mal wieder den Strichpunkt am
Zeilenende vergessen.
Wahrscheinlich weniger schwerwiegend.
Wenn sie sagen, da hat ein QA-Team drei Monate lang repariert,
wahrscheinlich wirklich wichtig.
Insbesondere die Regel, die sie aus diesem Fehler ableiten, ist dann
sehr wichtig.
Auch wenn es vielleicht nur ein sehr kleines Detail ist.
Schauen Sie mal ins Ilias unter den, ich glaube, das heißt so blumig
weiterführenden Materialien.
Da finden Sie ein Fehlerlogbuch.
Gucken Sie mal rein, was da so alles drin ist.
Und auch hier gilt, das ist halt ein Fehlerlogbuch.
Und das heißt ja selbstkontrolliertes Programmieren.
Sie kontrollieren also sich selbst.
Wenn sie andere Spalten für sinnvoll erachten, dann machen sie die
halt dazu.
Wenn sie es nicht interessiert, in welcher Programmversion das
passiert ist, weil es geht ihnen um ihre persönlichen
Programmierfähigkeiten, dann können sie die Programmversion auch
weglassen.
Gucken sie, dass das für ihren Prozess passt.
Und wenn sie einen Prozess definieren für andere in ihrem Team, dann
gucken sie, dass das jeder gleich macht.
Weil nichts ist schlimmer, als mal nicht konsistente Dinge hinterher
konsolidieren zu müssen.
Stellen Sie sich vor, einer schreibt immer nur auf, in welcher Code
-Stelle das passiert ist, aber keine Fehlerbeschreibung.
Der nächste schreibt nur die Fehlerbeschreibung hin und dann sollen
sie hinterher Regeln daraus ableiten.
Das ist quasi unmöglich.
Das ist unbrauchbar und dann ist Zeitverschwendung.
Also sorgen sie dafür, dass das sinnvoll gemacht wird.
Mit diesen Worten entlasse ich Sie ins Wochenende, wünsche Ihnen
schönes Wetter, wenn das irgendwie geht und bis zum nächsten Mal.