Das ist die Grundlage dafür, dass wir heute weiterdenken können,
dass wir heute also mit diesen Prinzipien umgehen können.
Ich hoffe, ihr erinnert euch, dass ich euch versucht habe beizubringen,
was alles für Beziehungen in objektorientierten Systemen herrschen.
Das waren nicht viele. Wir haben also Klassen benutzt,
Beziehungen, Vererbungsbeziehungen. Darüber hat Axel, glaube ich,
auch letzte Woche euch einiges erzählt. Oder war es Heinz
? Wer hat euch letzte Woche was erzählt über Vererbung?
Heinz? Okay, Heinz. Heinz, okay. Gut,
aber dann habt ihr was darüber gehört, auf jeden Fall.
Es war Heinz, oder? Nee, Guido?
Nee. Axel, Axel
Okay. Na, dann habt ihr es ja auf jeden Fall verstanden,
wenn Axel euch das erzählt hat, ne? Also,
benutzt Beziehung und Vererbungsbeziehung kennt ihr, ihr habt von mir gelernt,
dass es Pakete gibt, in denen Klassen enthalten sind. So,
und der wichtige Punkt, der möglicherweise für einige banal ist,
aber für die, die es noch nie selber ausprobiert haben,
immer wieder wichtig ist zu erinnern,
wenn man in solchen Objektorientierten Systemen auf abstraktere Ebenen geht,
dann faltet man das Innere zusammen,
man lässt also die Klassen einfach weg und sagt dann,
dass die Subsysteme Beziehungen zueinander haben oder eben die Packages,
ja,
in Java redet ihr ja auf erster Ebene auf jeden Fall
erstmal von Packages
Packages haben, natürlich, wenn man programmiert, keine Beziehungen,
aber wenn man auf solchen höheren Ebenen die Systeme betrachtet,
dann haben die Packages Beziehungen zueinander.
Und das kann man immer weiter zusammenfalten. Also ich hoffe,
wenn ich in der Klausur die Frage stelle,
wo die Beziehungen zwischen Packages herkommen in einem Diagramm,
das ja dann als Antwort dahinter schreibt, die Packages haben,
keine Beziehung, die Beziehungen kommen von den Klassen,
die in den Packages sind, weil die Klassen sich gegenseitig benutzen.
Irgend so was in der Art. Das würde mich durchaus
freuen, wenn ihr das tut. Gut. So,
und jetzt zeige ich euch Um welche Prinzipien wir uns heute kümmern.
Wir werden also einmal Prinzipien kennenlernen,
die sich bei Robert Martin unter dem Begriff Solid verbergen.
Das sind fünf verschiedene Prinzipien, die lernt ihr gleich kennen.
Und er hat das zusammengefasst zu dem Wort Solid,
um zu sagen, das, was ich baue, soll solide sein.
Ja, das soll also stabil und solide sein.
Und wir werden uns dazu auch eine Metrik angucken,
mit der man zumindest einen Aspekt davon überprüfen kann.
Und dann werde ich euch noch ein paar Prinzipien auf der Architekturebene zeigen.
Genau. Und was ich euch noch empfehlen will,
wenn ihr mal Zeit habt,
dann geht ihr mal auf diese Webseite
Oh, jetzt habe ich es durchgestrichen. Das ist ja
was. Obcheckt. Mentor.com. So. Ich hoffe,
dass es besser zu lesen als auf der Tafel.
Das ist der Block von Robert Martin und einer ganzen Menge
anderer Leute und die Artikel sind wirklich erhellend.
Also Block, Punkt und dann Object Mentor.com. Da werdet ihr,
da wird euch geholfen über alle wichtigen Themen, was Softwareentwicklung angeht.
Gucke ich ungefähr alle Woche mal rein und es ist immer
irgendwas Spannendes dabei.
Gut,
wir fangen mal an
Was bedeutet Solid? Solid steht für fünf Prinzipien,
die man beim Design auf der Klassenebene anwenden sollte.
Man sollte also das Single Responsibility Principle anwenden, das Open Closure Principle,
das Lizkov Subsitution Princible Interface Cerrigation Principle und Dependency Inversion Principle.
Und die werden wir jetzt kennenlernen,
wer letzte Woche gut aufgepasst hat oder die Folien von letzte
Woche nochmal reingeschaut hat, der soll jetzt eins wiedererkennen.
Das war schon mal erwähnt.
Ja,
einfach mal sagen
Das erste, genau. Das erste war schon mal erwähnt.
Ich zeige euch ja auch gleich noch mal die Folie vom
letzten Mal, auf der das erste erwähnt ist.
Ich erkläre es euch aber erst mal. Das Single Responsibility,
Princible geht darum, dass, welch Wunder,
eine Klasse, nur eine Zuständigkeit haben soll.
Eine Verantwortung oder wie auch immer. Und Robert Martin macht das
aber sozusagen nicht an der abstrakten Idee der Verantwortung fest,
sondern er sagt, Es soll immer nur,
es soll nie mehr als einen Grund geben für eine Klasse, sich zu ändern.
Also er macht sozusagen die Zuständigkeit daran fest,
wie sehr sich eine Klasse ändert aus verschiedenen Gründen.
Es soll nur einen Grund geben, warum sie sich ändert.
Und wenn ihr jetzt mal da dieses Modem Interface anguckt,
ja, wenn man das genau anguckt und das wirklich,
also sich auf die Hinterbeine stellt, dann kann man sagen,
oh, da sind zwei Zuständigkeiten drin.
Einmal die beiden und dann die beiden. Die oberen Zuständigkeiten
sind dafür zuständig, eine Verbindung zu halten, ja,
zu erstellen und wieder zu schließen zu einem anderen Gerät
Und die beiden unteren sind für das Versenden und Empfangen von Nachrichten
zuständig. Und wenn sich also jetzt an der Logik,
wie man Verbindungen herstellt, etwas ändert, dann sind wahrscheinlich
nur die beiden oberen Methoden betroffen und nicht die beiden unteren.
Also wenn wir uns sowas vorstellen, dass wir dieses Modem haben.
Und das wird verwendet, meinetwegen von einem Connection-Server.
Ja, so. Meine Schrift wird nicht besser dadurch,
dass ich hier draufschreibe, aber vielleicht wird es lesbarer.
Und auf der anderen Seite von einem Mail-Programm.
Ja. Und wir stellen uns vor,
das Mail-Programm verwendet nur Cent und Receive und der Connection-Server verwendet aber
Die und hängen ab. Wenn sich jetzt an dem Verbindungs-,
an der Verbindungslogik was ändert, dann muss sich ja dieses Modem ändern.
Ist logisch. Dann muss ich das, da muss ich was umprogrammieren.
Wen muss ich dann danach alles neu übersetzen möglicherweise?
Wenn ich das Modem geändert habe
Wisst ihr, was passiert, wenn ihr eine Klasse ändert mit den anderen?
Die davon abhängen? Alles, genau. Also beide,
ne? Super. Wenn ich das Modem aufteilen würde,
in zwei Hälften, ja,
und die eine Hälfte Diele und Heng abmacht und die andere
Cent und Receive und das sind zwei getrennte Klassen.
Ja, und an dem Verbindungslogik ändert sich was.
Dann muss ich nur noch den da übersetzen und kann
das Mail-Programm lassen,
so wie es ist
Also neben der Tatsache, dass ich mit einer Klasse zu tun habe,
die viele Zuständigkeiten hat,
möglicherweise, hat das sogar noch eine programmiertechnischen Hintergrund,
dass ich eigentlich nur eine Zuständigkeit für eine Klasse haben will.
Und das sagt Robert Martin eben mit dieser Sache,
es soll nur einen Grund für eine Veränderung geben, nicht mehrere.
Und in dieser Modemklasse hier gibt es immerhin zwei,
kann es zwei Gründe für Veränderungen geben.
Das Senden und Empfangen hat sich geändert,
vielleicht will ich einen neuen Nachrichtentyp haben oder was weiß ich.
Und das Aufnehmen von Verbindungen hat sich geändert.
Also diese beiden verschiedenen Gründe fürs Ändern kann es geben.
Und sowas will man eigentlich vermeiden
Das haben wir letztes Mal schon mal gehört.
Die Folie hattet ihr letztes Mal. Da ist es toll,
dass sich noch jemand erinnert hat, weil es war ja nun wirklich klein.
Ich habe letztes Mal zu euch gesprochen,
ich habe diese eine Folie nur als Erinnerung vom letzten Mal da reingemacht,
die ist nicht in euren Unterlagen drin,
aber ihr habt sie ja letztes Mal bekommen.
Also es geht nur um die Erinnerung hier an dieser Stelle.
Das heißt, wir haben letztes Mal schon darüber gesprochen,
wir wollen also nur eine Zuständigkeit pro Klasse,
nur eine Verantwortung und was ihr jetzt noch dazugelernt habt,
ist, dass der Robert Martin das auch noch versucht anders auszudrücken.
Wir wollen auch nur einen Grund haben,
warum sich
eine Klasse ändert
So, also jede Klasse und im Endeffekt auch jedes Package und jedes Subsystem
und überhaupt jeder sollte nur einen Grund zur Veränderung haben.
Gut, da sind wir wieder bei dem, nur meine Sachen,
die ich da drauf gemalt habe, sind weg,
weil das ist nochmal dieselbe Folie dahinter geplatt, gepackt.
Das Beispiel hier, also ich habe da die Folie dann nochmal,
weil ich wollte euch noch sagen, dass dieses Beispiel natürlich sehr weit hergeholt ist.
Also es ist sehr unwahrscheinlich natürlich,
dass ich so ein Modem aufteilen würde, sondern ich würde das genauso lassen,
denn eigentlich sind das natürlich Eigenschaften von einem Modem,
die ich nachher verwenden will und Ich soll ja auch nur dann so etwas aufteilen,
wenn ich wirklich erwarte, dass es Veränderungen gibt.
Ja, also man muss sich natürlich,
man muss natürlich jetzt nicht hergehen und sich beim Bau einer Klasse alle möglichen Veränderungen ausdenken,
die man sich so vorstellen kann, ja,
und dann die Klasse in so viele kleine Teile aufteilen,
dass man immer nur ein oder zwei Methoden hat.
Ja, also dazu möchte ich euch jetzt nicht hinreißen oder so,
sondern ich möchte, dass ihr erstmal Klassen baut,
okay, was ist die Zuständigkeit, was ist die Aufgabe,
dann baut ihr die Klasse, ja,
das ist der erste Wurf und der ist auch gut,
dann macht ihr einen zweiten Wurf, weil der Kunde kommt und sagt,
irgendwas muss anders und dann werdet ihr merken,
was für Veränderungen es gibt.
Dann werdet ihr die ersten Veränderungen ja erleben und werdet sehen,
welche Punkte in dem System sich verändern.
Was können wir denn tun als Softwareentwickler, um uns auf Veränderungen vorzubereiten?
Also um das möglichst früh zu merken, was können wir machen?
Was habt ihr schon gelernt darüber, was wir tun können?
Um Klassen so zu bauen, dass sie relativ resistent gegen Veränderungen sind.
Eine Antwort ist bei mir immer richtig.
Wisst ihr das schon, welche Antwort bei mir
immer richtig ist
Fängt mit Tee an und hört mit Tee auf.
Und hat vier Buchstaben. Test, ja,
die Antwort ist immer richtig. Was könnte denn die komische
Frau da vorne mit Ihrer Frage und diese Antwort meinen?
Wieso hilft mir Test? Und das macht ihr nämlich auch
. Also selbst wenn ihr nicht mögliche Szenarien durchtestet,
sondern wenn ihr überhaupt Tests schreibt, ja,
dann testet ihr ja die Verwendung der Klasse, ja.
So. Das ist eine Sache, die ihr schon könnt,
aber die Antwort ist genau richtig
Habt ihr schon was von iterativer Softwareentwicklung gehört?
Oder so Zyklen und sowas in Zyklen, Software entwickeln?
Nein, okay. Also übernächste Woche kommt Guido Gritzan und wird
euch was darüber erzählen. Aber trotzdem ist es was,
was ihr schon wissen könnt,
wenn ich mich auf Veränderungen vorbereiten will in meiner Software,
dann ist es keine gute Idee,
den Kunden zu fragen, was er möchte,
das aufzuschreiben und mich dann in meinen Kämmerlein zurückzuziehen und das zu bauen,
was er mir gesagt hat.
Und es ihm dann nach einem halben Jahr hinzustellen
Denn der Kunde hat mir sicher am Anfang nicht alles erzählen können.
Der konnte ja auch gar nicht ahnen, was ich alles so bauen kann.
Es ist viel schlauer, kleine Einheiten zu machen.
Kleine Pakete und möglichst früh vom Kunden Feedback zu bekommen.
Und das ist die Idee von agiler Softwareentwicklung,
dass man in kleinen Schritten Stück für Stück was entwickelt und
möglichst früh vom Kunden einen über die Rübe gebraten kriegt,
weil man in die falsche Richtung läuft.
Dann hat man nämlich schon was gebaut und man wird den
ersten Veränderungen, den wird man dann begegnen in dem Moment
und muss damit klarkommen
Ja. Also ich kann euch nur empfehlen,
wenn ihr nach dem Studium anfangt zu arbeiten und in
eine Firma kommt und die sagt zu euch,
wir entwickeln nach dem Wasserfallmodell.
Ja, also erst die Antwort. Das heißt so,
das ist ja das Geniale. Das Ding heißt Wasserfallmodell.
Erst Anforderungen, dann mache ich einen Entwurf und dann baue
ich das und am Schluss kriegt der Kunde das.
Dann dreht ihr euch auch gleich wieder um.
Denn da werdet ihr keinen Spaß haben. Ja,
dann werdet ihr frustrierende Erfahrungen mit Softwareentwicklung haben.
Aber man kann mit diesem Prinzip,
mit dem Wasserfallmodell kann man es erreichen,
dass man ein Projekt in Zeit und Budget
zu Ende bringt.
Das kann man erreichen
In der Regel, wenn man das schafft,
dann wollen die Leute das Software-System nachher doch nicht haben,
weil es tut nicht das, was sie wollten.
Also man muss schon sich ein paar Gedanken darüber machen. Gut,
das war das erste Prinzip, das war noch nicht schwer,
denn ihr kanntet das schon von letzter Woche.
Jetzt kommt das nächste Prinzip, das heißt Open Closed Principle.
Und da geht es darum, dass eine Klasse,
hier steht jetzt im Modul,
aber ihr denkt jetzt erstmal in Klassen bitte,
also eine Klasse,
sollte offen sein gegen Erweiterungen,
aber geschlossen gegen Veränderungen
Also Erweiterungen sind erlaubt, Veränderungen sollten, sind keine gute Idee.
Was soll das bedeuten? Also wir haben hier hier dieses
Modem schematisch nochmal und wir haben hier eine Hierarchie hier,
oben ein Interface und da unten drei Implementierungen für verschiedene Modems.
Und der Client benutzt dieses Interface. So,
wenn jetzt Veränderungen auftreten, also zum Beispiel hier unten kommt
eine neue Art von Modem hinzu.
Was bedeutet das für den Client?
Hat der Client dann
ein Problem
Einige fangen an, den Kopf zu schütteln,
das finde ich gut. Nein, der Client hat natürlich
kein Problem, weil der Client benutzt nur das Interface.
Nachdem ist das egal, ob da hier ein anderes Modem unterhängt.
In der ganzen Implementierung von dem Client wird immer nur der Interface-Typ benutzt.
Ja, Interface, das habt ihr ja auch schon
sehr viel kennengelernt, soweit ich weiß.
Also insofern sollte euch das keine neue Idee sein.
Was kann man dazu sagen? Also Klassen oder Komponenten
oder was auch immer, sollte offen sein für Erweiterungen,
aber geschlossen gegen Veränderungen, das ist das Prinzip.
Das heißt, man sollte eine Komponente so, wie sie ausgeliefert wird, auch benutzen können.
Und trotzdem sollte das Ganze noch erweiterbar sein. Das heißt,
wenn ihr also irgendein Framework euch einkauft in eure Firma oder irgendeine Library,
irgendeine Bibliothek, dann ist es eine gute Idee,
da mal reinzugucken, ob da ein paar Interfaces drin sind,
sodass ihr, wenn ihr doch was ändern müsst,
euch unter so ein Interface hängen könnt vielleicht.
So, das ist besser,
als wenn ihr in die Klassen selber
anfassen müsst
Ja, wir haben mal bei einem Kunden ein Projekt gemacht,
wo wir Hybernight eingesetzt haben. Und das war so ein
bisschen spezielles System. Das musste ganz, ganz,
ganz, ganz viele Daten, die sehr verschachtelt waren,
alle gleichzeitig laden und speichern und sowas.
Das ist mal so eine Anwendung, um die Leasing-Gebühren für Autos abzurechnen.
Und Hybernight kam mit der großen Datenmenge nicht zurecht.
Da war was in der Hybernight-Cloud falsch programmiert.
Auf jeden Fall war das Grotten langsam.
Und mein Kollege hat gesucht und gesucht,
bis er schließlich mit dem Performance-Tuning-Tool im Hybernight rumgevorwerkt hat und
den Fehler gefunden hat
Dann musste er eine Hybernate-Klasse ändern. Herr Hybernate,
für alle, die es nicht wissen, ist ein Stück Programm,
was man Open Source aus dem Internet bekommt,
was einen dabei unterstützt, seine Objekte in eine Datenbank zu bekommen.
Also das macht das Mapping von den Klassen, die er schreibt,
von den Daten, die er da reintut, in die Datenbank.
Das ist also was, was ziemlich weit unten in einem Software-System drin ist und was,
also in der Java-Entwicklung heute eigentlich Standard ist. Also wenn ihr irgendwo ein Datenbank-Bank-Mapping,
so nennt mir man das,
wenn ihr irgendwo ein Datenbank-Mapping braucht, braucht,
dann sucht nach Hybernate und dann werdet ihr es finden und dann werdet ihr auch eigentlich glücklich sein,
aber wenn ihr gerade das Problem habt,
was wir da damals hatten mit diesen sehr verschachtelten Objektbäumen,
dann funktionierte das halber Nate nicht schnell genug und wir haben einen Fehler gefunden,
haben ihn gefixt und hatten dann plötzlich unsere eigene Hybernate-Version.
Das ist natürlich ganz übel. Wir haben das auch gemeldet an die Community,
aber bei denen ist das so, da kann jeder Fehler melden,
die er findet und die, wo sich am meisten Leute für interessieren, die werden gefixt.
Also so, ist ja auch irgendwie ganz logisch,
weil man hat nur begrenzt Zeit,
man kann ja nicht alles machen,
es gibt tierisch viele Leute, die Hybernate benutzen,
also kann man sich immer nur um die wichtigsten Sachen kümmern und unsere Sache hatten,
das war wohl hauptsächlich unser Problem,
andere Leute hatten solche Probleme nicht und deswegen hat es Ewigkeiten gedauert,
bis sich diese Veränderung in den Standard integriert hat
Und wir konnten uns also nicht unter irgendeinem Interface hängen und irgendeine Logik anders machen,
sondern wir mussten wirklich in den Klassen da drin schrauben.
Das war sehr unangenehm. Also immer gut hingucken,
es wäre schön, wenn so ein System,
was man sich da besorgt, in irgendeiner Form für Veränderungen gut ist.
Gut, das war das Open Closed Principle.
Also wenn ihr was baut, versucht Interfaces einzubauen,
ich bin jetzt einfach nur noch mal kurz zurückzugegangen,
um das für euch bildhaft zusammenzufassen.
Das nächste Prinzip ist jetzt ein bisschen schwierig.
Das heißt, Lizkov Substitution Principle, das heißt so,
weil eine Frau mit Namen Lizkov Barbara Lizkov hat das
sozusagen entwickelt
Jetzt müsst ihr aber mit mir Vererbung lernen, sozusagen.
Weil ich habe nämlich immer auf Axels Liste geguckt und
da stand bei dieser Woche Subtyping und Vererbung.
Und da habe ich gedacht, jetzt könnt ihr das.
Aber ihr könnt das, glaube ich, erst übernächste Woche.
Da kommt nämlich nochmal ein Thema zur Vererbung. Nichtsdestotrotz wird euch
das nicht schwerfallen, das jetzt mit mir zu lernen.
Ich habe etwas geändert auf dieser Folie, zu dem,
was ihr in euren Unterlagen habt.
Das ist rot. Ich habe Subclasses in Subtypes umgewandelt,
weil ihr habt in OPM gelernt Subtypen, Typen und Subtypen,
dass es sowas gibt.
Und darum geht es eigentlich in diesem Prinzip auch
Das Prinzip sagt aus,
dass ein Substyp immer für seinen Basistyp verwendet werden muss.
Substitute heißt, dass man für den eingesetzt werden kann.
So, das habt ihr eigentlich auch mit Interfaces und Klassen schon gelernt.
Ja, also wenn ihr in einem Interface irgendwelche Operationen habt,
dann müsst ihr die implementieren in der Klasse, die ihr dazu baut.
Und dann sollten die möglichst auch das tun,
was in dem Interface beschrieben ist.
Und wenn ihr im Interface Verträge angibt,
dann sollte die Klasse diese Verträge auch erfüllen.
Und genauso ist es bei Vererbung zwischen Klassen
natürlich auch
Aber wenn man zwischen Klassen erbt, kann man was Böses tun.
Man kann nämlich, wie ihr hier seht,
in der Oberklasse Eclipse. Das ist also eine Klasse,
die eine, wie heißt denn das auf Deutsch?
Kein Kreis, sondern eine Ellipse. Genau, eine Ellipse
. Eine Ellipse darstellt. Und die hat eine Methode,
die heißt Z-Fokus. Mit der kann man, und ich hoffe,
ihr wisst das, und wenn ihr es nicht wisst,
dann zeige ich euch die nächste Folie.
Eine Ellipse hat zwei Fokuspunkte hier und da.
Das braucht ihr noch nicht lesen. Ich wollte euch
nur die Form zeigen. Das ist eine Ellipse, okay
Und jetzt könnte jemand hergehen und sagen, oh,
ein Kreis ist doch eine spezielle Form von der Ellipse, oder?
Also ich meine, die beiden, die beiden,
wie heißen denn die? Mittelpunkte, die beiden Mittelpunkte sind
keine Mittelpunkte, aber die beiden Punkte sind an einer Stelle.
Was? Rennpunkte. Ja, könnte sein, dass sie
so heißen. Ich weiß es gar nicht. Also jedenfalls
der Fokus. Es gibt nur einen Fokus. So,
jetzt kann man, wenn man zwischen Klassenerb das so machen,
dass man in der einen Klasse eine Implementierung hat,
ist ja schließlich eine Klasse und kein Interface.
Und in der da von Erbendenklasse schreibt man einfach
eine andere Implementierung hin
Das kann man tun, wenn man,
wenn Klassen voneinander haben, einige von euch,
die das schon getan haben, die wissen das auch schon.
So, an sich gilt da genauso wie beim Interface und bei der Klasse,
dass die Methode, die hier unten implementiert ist,
mit den Vorbedingungen stärker, also keine stärkeren Vorbedingungen haben dürfte und keine schwächeren Nachbedingungen.
Ihr wisst, was Vor- und Nachbedingungen sind?
Ja, ja. Wie implementiert man die denn in Java?
Mit As hört, genau. Okay, dann bin ich
ja doch hier richtig, das ist ja sehr schön.
Gut, also, was ihr hier aber seht,
ist, Die Elipse hat dieses Set-Fokus-Methode,
da werden zwei Punkte übergeben und dann setzt sie ihr eines
Attribut My Focus auf A und My Focus B auf B.
Ist ja auch irgendwie logisch.
Ja, das sind die beiden Parameter,
die reinkommen und die genau den Punkt angeben.
Was ist Point? Was könnte Point sein? Was muss
Point sein? Bitte? Ich verstehe dich leider.
Stellt einen XY-Koordinator da, genau. Wie kann denn das
gehen? Oh, es ist eine Klasse, genau.
Da muss noch eine Klasse Point irgendwo sein, sehr schön.
Die haben wir hier hier noch nicht. So,
und der Kreis, wenn man bei dem Set-Fokus aufruft,
Und dann setzt er mein Fokus A gleich A und mein
Fokus B gleich A. Könnte man so bauen.
Könnte irgendjemand so bauen. So. Erstmal kommt dann ein Kreis
bei raus. So. Und Frau Liskov sagt mit ihrem Substitutionsprinzip,
sowas darf man eigentlich nicht tun, weil die Bedingungen,
die für die Oberklasse gelten, müssen auch für die Unterklasse gelten.
Ich könnte jetzt also zum Beispiel eine Methode schreiben,
an der ich eine Elipse übergebe,
dann erzeuge ich mir zwei Punkte und setze E,
also diesem übergebenen Parameter hier, P1 und P2.
Und hinterher prüfe ich, ob P1 der Fokus von A ist und P2,
der Fokus B. So, und was ihr hier seht,
geht dann natürlich schief.
An welcher Stelle geht es schief? Mal ein bisschen Source-Code
lesen, an welcher Stelle geht es schief?
Beim letzten Assert, genau, hier.
Weil beim Kreis der Fokus B auf P1 gesetzt wurde.
Was müsste man denn dann tun, um hier noch klarzukommen?
Was müsste ich an meiner Methode My Method ändern hier oben
Ich könnte den zweiten Assort weglassen. Das könnte ich machen
. Aber das ist irgendwie langweilig. Was könnte ich denn
noch machen? Was P2 nach gleich P1 setzen,
aber dann geht es für die Elipse nicht mehr.
Es soll ja für beide gehen. Ich könnte versuchen,
bevor ich das zweite Assort aufrufe, rauszukriegen,
ob das ein Kreis oder eine Elipse ist.
Ihr kennt Instance Off, glaube ich, oder
Kennt ihr? Ja, genau. Ich könnte also da
sowas hinschreiben. Ich schreibe das jetzt mal da drunter.
E. Instance. Off. Kreis. Ja? Oder vielleicht lieber,
if not e. Instance of Kreis. Ja? Dann kommt
diese Zale da. Die. Ja. Also nur,
wenn ich nicht vom Typkreis ist, dann kann ich das machen. So,
und Wo ich euch also die ganze Zeit hinführen will,
ist zu sehen, das ist kein gutes Design,
ja, mit diesem Kreis und der Elipse.
Der Kreis verhält sich nicht so wie die Ellipse.
Ich kann nicht die gleichen Sachen, die gleichen Vor- und Nachbedingungen mit ihm erledigen.
Und ich muss mit Instance Off arbeiten. Ich habe vorhin
in einem Raum mit einem Kollegen gesessen und habe zu ihm gesagt,
sag mal, wie schreibt man eigentlich Instance Off?
Muss man das auf groß schreiben? Und dann sagt er,
nee, das muss man klein schreiben. Und dann dachte ich,
habe ich so lange nicht mehr programmiert, aber das stimmt nicht.
Ich habe letzte Woche noch Java programmiert.
Und dann sagt er zu mir, ich glaube,
das liegt daran, weil du kein Instance Off verwendest.
Und dann sage ich, ja,
daran liegt das
Instant Off sollte man eigentlich überhaupt nicht verwenden müssen.
Wenn man so ein schlechtes Design macht,
dann muss man es verwenden. Also in den Tools,
mit denen man Software-Systeme auf ihre Qualität hin vermisst,
kann man auch nach Instant Off suchen.
Nach der Verwendung von Instant Off. Und das ist ja
ganz einfach. Einfach ein Pattern-Matching und schon hat man alle Stellen.
Wenn in einem System ganz, ganz viel Instants Off drin ist,
dann sage ich den Leuten von vornherein, das ist nicht gut gelungen.
Da ist irgendwas schief. Ihr habt keine guten Vererbungshierarchien gemacht
. Wenn ihr immer dauernd wissen müsst,
was das nun eigentlich für ein konkreter Typ ist,
wozu habt ihr
die Abstraktion
Also, wenn ihr Klassen programmiert und wenn bei euch jetzt auch der Punkt kommt,
dass ihr von das Klassen von Klassenerben, ja,
dann guckt euch das genau an,
ob ihr das Lizkov-Substitution Principle erfüllen könnt,
also ob ihr eure Klassen so programmieren könnt,
dass die erbenden Klassen auch die ganzen Vor- und Nachbedingungen ihres Supertyps erfüllen können.
Gut. Das ist ein schönes Prinzip und das schließt an
unsere Diskussion von letzter Woche über die Fassade an.
Das Interface Sigregation Principle. Das sagt, dass es besser ist,
wenn man bei einer Klasse mehrere Clients drauf arbeiten hat,
also wenn mehrere Klassen eine Klasse benutzen,
dass es dann besser ist, mehrere Interfaces zu haben als eins.
Wir machen mal, ja, das ist so ein Problem,
ich habe also letzte Woche angefangen zu überlegen,
dass ich hier mit dem Stift draufmalen sollte,
aber die Folien waren alle schon voll,
habe ich da hinter mir eine, ja, genau,
wir lesen das jetzt durch und dann male ich euch gleich ein Bild.
Also die Idee ist, wenn eine Klasse A mehrere Klienten hat,
also mehrere andere Klassen, die was von ihr wollen,
dann sollten die Methoden des Klienten in eine Schnittstelle extrahiert werden
und jeweils klientenspezifisch angeboten werden
Klientenspezifisch bedeutet, dass ähnliche Clients kategorisiert werden und
die kriegen dann jeweils die spezifische Schnittstelle.
Und wenn ein oder zwei von diesen verschiedenen Klienten ähnliche Methoden benutzen,
dann kopiert man die lieber in mehrere Interfaces rein,
als dass man das versucht irgendwie zu vereinheitlichen.
Wir versuchen uns das mal bildlich darzustellen. Ich habe da
mir eine leere Folie reingemalt und ich hoffe,
dass ihr Platz habt, irgendwo euch das hinzumalen.
Erinnert ihr euch noch an das Fassadebeispiel, das wir beim ersten Mal hatten?
Da hatten wir eine Klasse, die hieß,
ich kürze das jetzt mal ab,
Spelling for Sate
Spelling for said hieß die.
Und die wurde benutzt von dem Word-Processor hieß der.
So. Und die Spelling for said hat ganz viel versteckt.
Da unten gab es ganz viele Klassen. Da gab es
ein Dictionary-Pool. Und da gab es Dictionaries.
Die wurden von dem Pool benutzt und dann gab es
einen Comperer,
glaube ich
Den gab es. Und dann gab es,
was gab es, noch ein paar Sa?
Und es mag sein, dass es noch irgendwas gab,
dass ich jetzt aber nicht mehr weiß.
Und die hatten alle irgendwie miteinander zu tun. Irgendwie so,
glaube ich. Ist auch nicht so wichtig. So. Und
neben dem Word Processor gab es noch ein anderes Programm.
Das war der Administrator. Der war dazu da,
um Dictionaries zu verwalten. Jetzt ist der dummerweise auf der
anderen Seite, aber das macht nichts.
Der hat also dann das Dictionary Poole verwendet
Und das Dictionary. So. Wir haben am Rande darüber geredet,
als wir uns mit dem Beispiel beschäftigt haben. Ja,
malt ihn doch auf die andere Seite, ich kann das
leider nicht schieben hier. Das wäre hübsch, ne?
Also wenn ich das jetzt einkreisen könnte und umbohre.
Das kommt dann nächstes Jahr in der Vorlesung. Auf jeden Fall,
wir haben letztes Mal darüber geredet, ob diese Fassade nicht
eine viel zu bereite Schnittstelle ist an der Stelle, ja?
Und mein Argument war dann, naja gut,
sie ist aber jetzt nur für den Word-Processor.
Und das ist auch tatsächlich das Argument,
wenn ich jetzt für den Administrator auch noch ein Interface haben wollte,
eine Schnittstelle für diesen System teil
Würde ich, wenn ich diesem Prinzip hier folge, ein zweites Interface bauen.
Eine zweite Fassade oder wie auch immer ich das nennen will,
ja, meinetwegen atmen, for said, ja,
ich drücke immer auf den Pfeil und dann kommt immer so,
ja, ich würde also für dieses,
für dieses Subsystem hier dieses ganze Ding hier,
was hier unten ist und was,
was das Prüfen von Worten,
das Prüfen,
ob die richtig geschrieben sind und so weiter,
ausmacht,
das würde ich
mit zwei getrennten Interfaces versehen
Um einmal das, was der Word-Prozessor will,
denn der will Funktionalität, dass ich da prüfen kann,
ist dieses Wort schon in dem Dictionary drin.
Ist es richtig geschrieben und so weiter,
das will der an Fuluktionalität und der Administrator will mir eigentlich
einen Blick auf die verschiedenen ermöglichen Dictionaries geben, will mir erlauben,
Worte daraus zu löschen oder neue Worte einfach direkt hinzuzufügen.
Oder auch zu filtern, ich weiß nicht, ob ihr
euch daran erinnert, dass man das da auch konnte.
Also ich würde an dieser Stelle, wenn ich diesem Prinzip folge,
eher zwei Schnittstellen machen für diesen Systemteil als eine.
Ja, und das war ja auch die Richtung,
in der die Diskussion ging,
die wir da letztes Mal hatten
Ja. Und was der Robert Martin in seinem Buch sehr schön sagt,
ist, er will nicht, dass ein Klient dazu gezwungen wird,
auf Methoden sich abzustützen, die er gar nicht braucht.
Also wenn man nur so ein dickes Interface hätte für einen Systemteil,
dann wären da ja ganz, ganz viele Methoden drin.
Wahrscheinlich, wenn da ein Softwareentwickler gewesen wäre,
der nach diesem Motto braucht, der nach dem Motto entwickelt,
das ist eine tolle Idee, das könnte auch nochmal irgendjemand gebrauchen,
dann wäre also diese Schnittstelle richtig breit,
da wäre ganz viel drin.
Und wenn ich nur drei Methoden daraus brauche,
dann habe ich den ganzen anderen Salat auch an der Backe
Und wenn denen dann irgendwann mal jemand anders benutzt und merkt,
da ist ein Fehler drin,
dann wird das korrigiert und dann muss ich auch noch neu übersetzt werden,
wo ich doch nur diese drei Methoden brauchte,
die immer noch gleich sind und die auch gut funktionieren.
Ja, also es ist sinnvoll,
die Schnittstellen ganz spezifisch für die einzelnen Klienten auszurichten.
Wir machen noch drei Folien, da machen wir Pause.
Okay, dann habt ihr die ersten,
den ersten Teil geschafft. Fünf Prinzipien und eine Metrik.
Das ist schon mal nicht schlecht
Die nächste Metrik, äh, das nächste Prinzip ist es,
die Pendency Inversion Principle. Bei diesem Prinzip geht es darum,
dass wir in unseren Software-Systemen auf Abstraktionen,
von Abstraktionen uns abhängig machen wollen und nicht von konkreten Lösungen.
Das ist hier erstmal wieder ein schlechtes Beispiel, was ihr hier zuerst seht.
Das ist ein typischer prozeduraler Entwurf. Das heißt,
ich habe eine Main-Methode hier oben, ja,
und aus dem Main werden diese hier alle aufgerufen und
die rufen dann immer noch wieder irgendwelche Detailmethoden auf
Das heißt, die oberste Main-Methode ist von allen darunter abhängig.
Wenn sich da unten irgendwas ändert,
muss die neu übersetzt werden, alles muss neu getestet werden.
Was viel besser ist, ist das,
was wir mit Objektorientierung können. Das konnten die Leute,
die prozedural entwickelt haben, ja auch noch nicht.
Die hatten kein Interfaces, keine Vererbung und sowas.
Also ihr seid sozusagen in der glücklichen Lage der späten Geburt
und musstet nicht irgendwas vordrannen oder sowas programmieren.
Aber vielleicht müsst ihr das doch. Aber bei uns müsst
ihr das jedenfalls nicht. Also ihr könnt sowas bauen wie
das hier. Ihr könnt also oben eine Hauptklasse haben,
die Interface ist einfach nur verwendet und die werden dann implementiert.
Ja, und diese Klasse da oben hängt nur von
diesen Interfaces ab und nicht von den Implementationsklassen unten.
Da gibt es eine wunderbare Metrik, mit der man sich
das angucken kann und die zeige ich euch jetzt mal.
Die heißt Average Component Dependency. Es geht darum für eine Komponente sagen zu können,
wie abhängig sie ist von dem Rest des Systems.
Ja, und das macht man so,
hier links seht ihr das erste Bild.
Wir haben also oben eine Komponente,
die benutzt zwei und dann Wind da unten benutzt
Und jetzt, um das zu berechnen, muss man für
jede einzelne Komponente feststellen, von wie viel anderen sie abhängt.
Und die Idee dabei ist, diese Komponente hier unten,
die hängt nur von sich selbst ab.
Deswegen schreibt man da eine Eins rein,
bei der auch und bei der auch.
Diese hier, die hängt von sich selbst ab und,
das seht ihr gleich, von der und von der.
Also eine Drei ist ganz einfach. Diese hier hängt auch
von sich selbst und von dem und von dem ab und
die da oben hängt von sich selbst und von der und
von der und von der und von der und von der
Aber man schreibt da nicht die Wege rein, die man
machen kann. Man könnte da oben ja auch denken,
dass man eine Sieben reinschreiben müsste oder so.
Ja, das ist es nicht. Eins, zwei,
also ihr seht mich nicht, wenn ich da drauf male, ne?
Eins, zwei, drei, vier, fünf, sechs.
Also die Anzahl der Komponenten wirklich, von denen sie abhängt. So,
und was man dann macht, ist die Summe bilden,
also sechs plus drei, plus drei,
plus eins plus eins plus eins,
ist 15 durch die Anzahl der Komponenten.
Also dieser Graf hier hat eine durchschnittliche Komponentenabhängigkeit von
Und das Beispiel auf der linken Seite, nee,
auf der rechten Seite, hier, das ist das Beispiel,
wo wir eben die umgekehrten Beziehungen hatten, wo wir also,
ich gehe nochmal zurück, das Bild hatten,
dass diese Komponente hier von den drei abhängt,
aber hier die Beziehung ja in die andere Richtung geht,
denn diese Klasse hier erbt von der, das heißt,
sie ist abhängig von dem Interface.
Erben, da geht der Pfeil in die andere Berichtung.
Dieses Interface hier oben kann ich alleine übersetzen.
Ich brauche nicht die erbende Klasse dazu
Ist euch das klar? Dass das Interface nicht von seinen Implementierungen abhängig ist.
Ja, gut. Also gucke ich mir diesen Grafia an und stelle fest,
der hier ist von zwei abhängig,
nämlich von sich selbst und von dem,
der ist von drei abhängig, es ist jetzt nicht mehr schwer,
von sich selbst und von dem und dem,
der auch und der da oben ist,
auch nur von dreien abhängig.
Und wenn man dann rechnet,
kommt man auf eine durchschnittliche Komponentenponentenabhängigkeit von zwei
Das ist wahr. Man könnte hier natürlich auch was anderes haben als Interfaces,
weil eben habe ich ja schon über Erben von Klassen geredet.
Also man könnte da auch abstrakte Klassen haben, die auch was implementieren.
Da hast du sicherlich recht. Aber es geht ja hier darum,
Abhängigkeiten einzuschränken und nicht die Menge des Sources,
die ich in einer Klasse habe, zu begrenzen.
Also denn auch hier werden die ja nur Hüllen sein,
die da unten wieder aufrufen. Also insofern Aber das zeigt
schon eine Sache, die man immer hat,
man rechnet irgendwas aus. Also man macht,
man macht irgendeine Metrik und schwupp kommt jemand und sagt,
mit meiner Metrik würde das aber viel besser und genau andersrum aussehen.
Das ist also sozusagen ganz normal bei Software und großen Software-Systemen.
Das ist aber ja grundsätzlich ein Problem von irgendwelchen Zahlen und Aussagen,
die man darauf macht. Also mit Statistiken sowieso.
Gut. Ein Punkt ist dabei aber noch wichtig und den
will ich euch nochmal zeigen, und zwar an dem Modembeispiel.
Jetzt habe ich hier das Bild von der Modem der
von dem ersten Prinzip einfach nur dahin kopiert, okay?
Weil ich einfach zu faul war, das nochmal abzumalen.
Aber was ihr dafür braucht, um das zu verstehen,
ist auch einfach nur dieses Interface da oben,
die da von erbenden Klassen und der Client, der das benutzt.
Wir haben ja bei unserem Fassadebeispiel auch zwei Packages gehabt.
Ihr erinnert euch, wir hatten ein Package,
das war der kleinen Teil und ein Package,
das war die Spelling for Sade.
Wie würdet ihr das hier denn aufteilen, wenn ich zu euch sage,
ich will das jetzt in zwei Packages haben,
weil ich will den Klienten irgendwo anders deployen und so.
Wie müsste man das aufteilen?
Wo würde man
das durchschneiden
Vorschläge? Wie würde man das aufteilen?
Ist es zu einfach oder zu schwer, die Frage?
Ja, das sind ein Interface und vier Klassen.
Ja, wir stellen uns aber mal vor,
dass hier hinter dem Client noch ganz viele andere Klassen sind.
Ja, das ist also auch eine größere Menge.
Und ich bin eurer Projektleiter und komme vorbei und sage,
hör mal zu, Das sind zu viele Klassen in einem Package.
Du musst das aufteilen. Ich will, dass du zwei Packages
daraus machst. Wo wird jetzt hier durchgeschnitten? Wo möchtest
du durchschneiden, zwischen welchen zwei oder was auch immer?
Was soll in einem Package? Es gibt keine falschen Lösungen
. Es gibt nur Ideen, bitte. Okay,
also du würdest so machen? Ja, so habe ich
dich richtig verstanden und so
Ja? Okay. Ist das, gibt es eine andere Idee?
Dein Vorschlag? Ich versuche mal, ob ich jetzt eine
andere Farbe kriege. Mal sehen, ob das jetzt geht.
Du möchtest hier, also, oh, falsche Richtung.
Du möchtest hier durchschneiden, verstehe ich das, richtig?
Also so und so.
Was ist besser? Und warum? Wer ist,
wenn man es so aufteilt, wer ist da von wem abhängig?
Wir nehmen mal das Rote. Es gibt einen Klienten und
es gibt diese Mode im Hierarchie.
Wer ist da von wem abhängig
Genau, der Klient, also das Klientenpaket ist von dem Paket abhängig.
Wenn wir den gelben Schnitt machen, was haben wir dann?
Wer ist dann von wem abhängig? Genau, umgekehrt,
die hier unten sind abhängig von dem da oben.
Das Gelbe ist die bessere Idee, ja?
Weil ich kann das da oben auf dem Kleinen deployen,
ich kann hier unten was ändern,
das stört das da oben nicht
Und mein Klient ist nicht abhängig von irgendetwas weniger Abstraktem da unten,
sondern es ist andersrum. So, und mit dieser Erkenntnis
entlasse ich euch jetzt in die Pause und dann gucken wir mal.
Bis gleich. Ja, fünf Minuten, ne?
Weiter geht's. Wir haben die Hälfte geschafft, über die Hälfte.
Wir haben also jetzt die fünf Grundprinzipien für Klassenentwurf kennengelernt,
Solid, man auch auf Architekturebene beziehen kann,
wie er eben in dem Beispiel gesehen habe,
was ich mit euch angeguckt habe.
Und wir haben diese eine Metrik kennengelernt,
um die durchschnittliche Abhängigkeit von Komponenten zu berechnen in einem System.
Jetzt wenden wir uns der Architekturebene zu und die ersten drei Prinzipien,
die jetzt gleich kommen, die sind ziemlich einfach zu verstehen.
Wir fangen mal an. Einmal das Kommen Closure Principle
Das ist eigentlich nur dasselbe unter einem neuen Namen. Denn hier geht es darum,
dass Klassen, die sich zusammen verändern, auch zusammengehören.
Ja, das heißt, alle Klassen in einem Package sollten zusammen verändert werden.
Das ist im Grunde das Single Responsibility Principle von der Klassenebene
auf die Package-Ebene hochgezogen. Ihr habt inzwischen von mir gelernt,
dass in großen Entwicklungsprojekten die Klassen in Packages aufgeteilt werden und die Packages in Subsysteme.
Und je mehr Klassen in einem Package oder in einem Subsystem
in einem Release verändert werden müssen,
desto größer ist auch der Aufwand, den man dann treiben muss,
um das System neu zu bauen, zu testen,
zu deployen und so weiter.
Und um sozusagen diesem Problem etwas herr zu werden,
solltet ihr also hergehen und auch die Klassen,
die zusammen geändert werden, zusammen in ein Subsystem tun.
Man findet oft in Systemen so ein Subsystem oder eine ganze Schicht,
habe ich euch auch schon mal erzählt.
Die Util heißt, wo sozusagen alles Utilities,
wo alle Reste drin sind, wo man nicht weiß,
wo die hingehören. Das ist ganz schwierig,
weil da ist natürlich alles Mögliche drin Was der hier braucht,
was der da braucht, was der da braucht.
Also das Ding, wenn sich das ändert, haben immer alle was davon.
Sowas ist relativ unangenehm. Und ihr müsst dann also immer
das gesamte System neu übersetzen, wenn sich nur das Utility Package verändert.
Da müsst ihr alles wieder testen und so weiter und so fort.
Also sehr unangenehm. Was bedeutet das im Endeffekt? Ihr solltet versuchen,
in euren Systemen keine Utility Packages oder Schichten einzubauen,
sondern die Teile dahin zu tun, wo ihr sie braucht.
Also in denen Package oder in das Subsystem,
wo ihr sie nachher braucht.
Gut. Also
relativ einfache Sache
Dann gibt es ein zweites Prinzip, was im Grunde
in dieselbe Richtung guckt, aber von der anderen Seite.
Denn dieses Prinzip sagt, dass Klassen, die nicht
zusammen verwendet werden, auch nicht zusammen gruppiert werden sollten.
Also das guckt mehr von der Verwendungsseite als von der Änderungsseite.
Wenn ich also ein Package verwenden will und da sind Klassen drin,
die ich gar nicht brauche. Ihr erinnert euch an das,
was wir eben auch über die Interfaces von solchen Teilen sprachen,
lieber mehrere Interfaces für die verschiedenen Clients, die man hat.
Und auch die Klassen in den Packages bitte so sortiert,
dass man, wenn man ein Package braucht, dann möglichst auch alles daraus braucht.
Und nicht nur so einen kleinen Teil
Ich habe mal ein ganz hübsches Eclipse-Plugin gesehen. Das hat,
wenn man an eine Klasse geändert hat und die gespeichert hat,
hat dann unten angezeigt, welche anderen Klassen im System
in der Regel mit diesem zusammen geändert werden.
Das war total cool, weil dann konnte man immer sehen,
okay, aha,
ich muss wahrscheinlich noch hier in diesem Resource-File zum Beispiel was ändern oder normalerweise,
wenn hier jemand was geändert hat,
dann hat er auch in der Server-Komponente was geändert oder in dem Client was geändert.
Also ein Plugin, das einem sozusagen unabhängig von den Beziehungen,
die man zwischen Klassen hatte,
immer wieder zeigte,
okay,
das musst du
alles noch mitändern
Wahrscheinlich. Da kann man dann sehen, okay,
welche Klassen werden wahrscheinlich zusammen verändert oder auch zusammen benutzt.
Die muss sich alle auch noch anfassen.
Ich kann euch leider nicht sagen, wo es ist,
weil ich habe es nur auf einer Konferenz gesehen.
Es war ein Forschungsplugin, aber die Idee fand ich
sehr schön. Gut. Und das dritte Prinzip,
das ist überhaupt mein Lieblingsprinzip, das nennt sich A
Cyclic Dependency Principle. Und da geht es darum,
dass zwischen Packages soll es keine Zyklen geben.
Was ihr da jetzt seht, ist ein, oh,
was ist denn das für ein Diagramm
Wisst ihr, was das für ein Diagramm ist?
Typ. Ich höre einen flüsternden Sebastian, der sagt,
das ist ein Paketdiagramm. Ja, das ist ein Paketdiagramm.
Man sieht Pakete, hier ein Paket. Oh, jetzt
bin ich gelb. Dann ist es auch nicht schlecht.
Und da drin sieht man Klassen. Das ist eine Klasse.
Da sind noch mehr Klassen. Und was sieht man?
Klasse A benutzt Klasse B und Platz zu Y benutzt Klasse
B. Das ist ein Package-Zyklus. Ja,
weil wenn ich nämlich jetzt die Klassen weglasse und nur die Packages hinmale,
Und die Beziehungen sozusagen hoch arrigiere,
ihr erinnert euch an das Bild von vorhin,
dann habe ich das.
Ich glaube, ich gehe lieber wieder auf den roten Stift.
Was? Der rote Stift war besser, ne?
Ja. Fehlstift. So,
also ich habe ein Paket und noch ein Paket,
ne, und ich habe einmal Beziehung und die Richtung und in die.
Ja, also diese Beziehung hier ist die da und
die da ist die,
Jetzt hat jeder verstanden, was ein Package-Zyklus ist, ja?
Das eine Package braucht das eine und das andere braucht das eine.
Wenn man das hier anguckt, ist das ein Klassenzyklus?
A, B, X und Y?
Nee, ne? Aber ich mache einfach mal einen draus.
Schwupps. Jetzt ist es auch ein Klassenzyklus.
Ja? Klassenzyklen sind viel ekliger als Package-Zyklen,
weil um die Cup aufzubrechen, muss man hier drin rum programmieren.
Was kann man denn
hier machen
Um den Package-Zyklus aufzulösen. Eine Lösung ist da unten,
aber es gibt noch eine andere, eine viel einfachere.
Ja, das ist die Lösung hier unten.
Das kann man zwar nicht lesen, aber da steht Interface.
Aber die Idee ist ja auch gut, ja? Also man kann sagen,
okay, ich führe jetzt für B ein Interface ein,
dann erbt B von dem,
dann gibt es nur noch eine Beziehung in der Richtung,
ja?
Und Y arbeitet auf dem Interface. Ist eine gute Lösung
. Hast du noch eine andere Idee?
Wie bitte
Man könnte ein Paket draus machen. Das ist natürlich
auch eine Idee. Na, man könnte, also,
ne, jetzt brauche ich mal ein Radiergummi hier.
Oh, na komm. So, oh Mann. Das ist
so schön, aber manchmal kann ich es noch nicht richtig.
Ne, wird nichts. Also,
wenn der weg ist und der weg ist, ja,
was kann ich dann machen, um den Zyklus aufzuheben?
Ich kann einfach B hier in dieses Package tun.
Ja, und der Zyklus ist weg, ja,
dann habe ich auch das Bild von hier unten.
Also hier kann ich auch B darüber tun, ne. So,
das ist natürlich Das, wenn ich,
wenn ich mir Software-Systeme angucke und aufräume in denen, ja,
also ich gucke mir die an und dann schimpfe ich mir den Leuten über all ihre Zyklen,
ne, und dann räumen wir aber eigentlich erstmal auf.
Und Package-Zyklen sind wirklich ganz nett, weil meistens ist das
nur eine falsche Sortierung. Also die Leute haben eine Klasse,
die viel mit den anderen zu tun hat, an den falschen Ort getan.
Und dann räumt man ein bisschen auf und dann sieht es schon viel besser aus. Ja,
bei Klassenzyklen, also wenn das doch so wäre hier,
dann würde das verschieben von B ja gar nichts nützen,
ja, weil, wenn B da drüben ist,
würde es immer noch A benutzen.
Ja, also Klassenzyklen sind viel unangenehmer. Deswegen schon,
wenn ihr im Kleinen anfangt zu entwickeln,
baut bitte keine Zyklen ein in eure Systeme
So, aber lange Rede, kurzer Sinn,
was wir jetzt gelernt haben sollten, ist,
Zyklen sind keine gute Idee.
Warum eigentlich nicht? Ich sage das hier so einfach.
Gibt es eine Idee? Man soll nicht alles glauben,
was die Leute erzählen. Das ist was, ja.
Wenn eine kaputt ist, dann geht nichts.
Also man kann nicht, man kann diese Hälfte niemandem geben,
ohne die andere Hälfte
Und dann gibt es noch eine Antwort, die ist immer richtig.
Testen, genau, aber was ist das Problem mit Zyklen und Testen?
Stellt euch mal vor, ihr sollt einen Test für A schreiben.
Und ihr habt hier diese ganzen Beziehungen hier so im Kreis rum.
Was müsst ihr dann machen, um einen Test für A
zu schreiben? Braucht ihr dann nur ein A?
Nein, ihr braucht noch ein X,
ein Y und
ein B. Wie unangenehm
Ihr wolltet nur einen Test für Arsch schreiben und ihr braucht diese ganzen anderen Objekte.
Ja? Wenn das hier nicht existiert und es nur diese eine Beziehung gibt,
dann braucht ihr immerhin nur das X, um A testen zu können.
Ja? Also, was will ich damit sagen?
Es ist ganz schwer, zyklische Strukturen zu testen.
Und wenn ihr, oh, ihr lernt ja sogar,
glaube ich, Test first, oder?
Erst den Test schreiben und dann die Implementierung. Also,
wenn ihr das macht, ja,
also wenn ihr das wirklich macht,
sollte dabei ein quasi zyklenfreies System rauskommen,
weil ihr versucht immer einen Test nur für die
eine Klasse zu schreiben und nicht für irgendeinen Haufen
Mir ist völlig bewusst, dass es Tests auf höheren Ebenen gibt,
Integrationstests und so, wo man auch immer ganz viele Klassen braucht.
Das ist auch unbedingt notwendig. Aber jetzt geht es erst mal
um automatisierte Unit-Tests und da schreibt ihr einen Test für eine Klasse.
Und vielleicht noch mal für zwei. Aber ihr wollt jedenfalls
keinen Test für fünf Klassen auf Mal schreiben.
Okay. Und jetzt kommt wieder die Metrik.
Weil die macht nämlich auch bei Zyklen ganz hübsche Werte.
Links ist das Ding, also links ist hier.
Ich weiß nicht, wo es bei euch ist
Also links ist wieder der von vorhin, der,
wo als Average Componed Dependency 2,5 rauskam. Rechts haben wir
einfach mal einen Zyklus eingebaut, der da unten benutzt,
also auch noch den da oben, seht ihr.
Und wenn man das dann ausrechnet, ja,
also dieser hier ist abhängig von dem und von dem
und von dem und weil er von dem abhängig ist,
auch noch von dem und von dem ist er schon und von dem,
also von allen sechs.
Kann man verstehen. Das heißt,
wenn man einen Zyklus in so einem System hat,
dann hat man ein viel schlechteres Ergebnis für die durchschnittliche Abhängigkeit,
Komponenten in dem Baum.
Das ist ja jetzt noch ein einfaches Beispiel.
Das wollen wir uns jetzt mal bei einer Architektur angucken.
Das ist die typische Architektur von einem großen Softwaresystem.
Ich habe Schichten. Eine hier, eins, oh,
das war nicht, das war nicht, was du solltest.
Eine hier, eine da, eine da und eine da.
Vier Schichten. Ich möchte nicht, dass alle eure Systeme,
die ihr baut, in Zukunft vier Schichten haben, okay?
Ich bin auch glücklich, wenn ihr 15 Schichten habt.
Hauptsache ihr habt Schichten
Was bedeutet Schichten? Schichten bedeutet, dass es nur Beziehungen von oben
nach unten gibt, genau wie diese blauen Pfeile hier.
Ich male einfach mal da rein in die Pfeile, ja?
Von oben nach unten. Das da ist verboten,
okay? Verbotener kann ich es nicht malen, ja?
Auch das ist verboten, das ist noch verbotener.
Alles verboten, ja? Von unten nach oben ist verboten
. So. Bitte was? Ja, das kommt jetzt.
Erstmal Schichten. Wenn ihr, also, ihr werdet anfangen,
irgendwo in ein System zu bauen für, Was macht ihr jetzt?
Ach so, ihr macht bald für die, für die,
für das Kinokartensystem. Das kommt bald, das Projekt mit
dem Kinokarten. Ihr werdet also ein System bauen,
mit dem der Kinokartenplaner seine, sein Kino planen kann.
Also wann welcher Film läuft und so. Um welche Uhrzeit
und so. Also ein Kinoprogramm veröffentlichen und solche Sachen.
Da werdet ihr so etwas bauen. Eine, einen
vertikalen Schnitt. Ja. Wer, die,
ich schreibe ganz langsam, damit ist Carla Schnitt.
Oder manche Leute sagen auch leider Gottes Modul dazu,
obwohl dieses Wort für so viel verwendet wird, ja?
Oder manche sagen, das englische Wort. Vertical slice. Ja,
also es gibt, also Schichten, so heißen die immer,
ja, auf Englisch Layers, das ist sozusagen Standard,
ja, Schicht.
Das ganze Ding hier, oder auf Englisch, Layer,
ja, das ganze Ding.
Aber für diese Quer von oben nach unten durch,
für die gibt es noch keinen einheitlichen Namen.
Deswegen muss man mit den Leuten immer reden,
was die meinen
Wir können uns auf vertikalen Schnitt einigen. Also das erste System,
was ihr wahrscheinlich mal alleine baut, wird so ein vertikaler Schnitt sein.
Wenn das System größer wird, dann ist meine Erwartungshaltung,
dass mehrere vertikale Schnitte entstehen. Ja,
weil euer System nicht nur für den Kinokartenplaner verwendbar ist,
sondern auch noch für die Frau an der Kasse.
Und die macht was ganz anderes. Die verkauft nämlich Karten,
die plant nicht ihr Kinoprogramm. Ja, das ist hier
an dem einfachen Beispiel einer Bank gemacht. Also es gibt einen Bereich,
wo die ganzen Programmteile für Kunden drin sind, einen Bereich,
wo alle Programmteile für Konten drin sind und einen,
wo alles für Kredite drin ist
Und auch da will man Beziehungen nur in eine Richtung haben.
Und zwar von links nach rechts. Ja, oder wie rum
man das jetzt aufmalt. Ja, das ist ja egal.
Aber nur in eine Richtung. Ja, also ein Kunde,
die ganze Kundenlogik sollte nichts von Konten wissen, verboten.
Konnten sollten nichts von Krediten wissen, verboten.
Ja? Auch in der Richtung eine Schichtung im Endeffekt.
Ja,
eine vertikale Schichtung
So, so würde ein großes System aufgebaut sein.
Und auch da kann man mit dieser hübschen Metrik,
mit der wir eben gearbeitet haben, schicke Sachen machen.
Wir stellen uns also vor, was für Beziehungen wollen wir erlauben?
Ja, ich habe ja jetzt erst mal nur gesagt,
von oben nach unten und von rechts nach links.
Da ist ja noch vieles möglich.
Wir fangen mal klein an. Wir sagen also,
okay, wenn ich hier in irgendeinem Quadrat bin,
dann darf ich alles verwenden, darunter und daneben.
Ja, und daneben unter. Ja, also nach unten,
also von dem hier aus zum Beispiel runter,
darf ich,
aber runter heißt auch darunter,
ja,
oder dahin
Ich darf auch da und dahin, ja. Und ich darf noch da.
So, und der darf dann auch hier, ne,
ihr versteht das Muster, ja? So. Und wenn man das jetzt mit dieser hübschen Metrik berechnet,
mit der Average Component Dependency, dann stellt man fest,
also wenn sich alle an die Regeln halten,
dann ist der hier unten nur von sich selber abhängig,
der hier von sich selbst und von dem,
bei dem ist das dasselbe, er selber und der,
dieser hier kennt vier, sich selbst,
den,
den und den und so weiter und so weiter und
so weiter
Ich war mit meinem Studium irgendwann mal in einer Logistikvorlesung.
Ich habe Logistik im Nebenfach gehabt.
Und da hat der Prof vorne einen Algorithmus zur Berechnung,
wie vier LKWs von fünf Standorten, so und so viele Waren,
an so und so viele Plätze bringen können.
Hatte er so eine Formel? Und dann hat er
die erste Iseration vorgerechnet. Ihr wisst,
was eine Iteration ist aus Mathe wahrscheinlich, hoffe ich jedenfalls.
Und dann dachte ich, alles klar.
Und dann hat der Prof da vorne an der Tafel
15 Iterationen vorgerechnet
Weil diese ganzen B-Wähler um mich rum alle völlig in Panik geraten sind,
weil sie nicht verstanden haben, was ein Algorithmus ist.
Ich hoffe, dass das bei euch anders ist.
Deswegen mache ich jetzt nicht alle Pfeile auf, dieses Bild.
Okay? So, also, was kommt raus?
Wir haben 60 durch 12, also die durchschnittliche Komponentenabhängigkeit in dieser Architektur ist 5.
Und das ist toll, wenn man das hinkriegt, ja? Und jetzt versteht ihr,
glaube ich, auch, warum das Ding Average Component,
die Pendencies heißt, weil diese Quadrate hier, ja,
diese Dinger, Das sind keine Klassen, ne,
das ist klar, oder?
Da sind ganz, ganz viele Klassen drin oder sogar
ganz viele Pakete drin und nochmal Pakete,
die ganze Gui für die Kundenverwaltung, ist alles da drin.
Ja? Diese Dinge, diese Kästen sind sozusagen Subsysteme oder
Komponenten. Habe ich noch nicht verraten, ne?
So. Deswegen heißt das Ding Average Component, Dependency,
was man kann es auch auf Klassenebene spielen,
aber eben auch auf dieser Ebene.
Also, da kommen fünf raus.
So. Jetzt kommt jemand und sagt, ich mache das
noch besser
Ich erlaube nämlich auf dieser unteren Ebene hier keine Querbeziehung mehr.
Seht ihr das? Die Beziehung hier? Sind nicht da
. Und auch hier unten nicht. Denn ich will
diese Beziehung eigentlich nur auf der Ebene der Logik haben.
Ich will meine Business Objects, also die Klasse Kunde,
die Klasse Konto und die Klasse Kredit, die will ich voneinander entkoppeln.
Die sind hier unten. Hier ist eine Klasse Kunde.
Ich schreibe nur mal einen K rein. Oh,
das ist lustig. Ich kann überall einen K reinschreiben.
Hier ist die Klasse Konto Und hier ist die Klasse Kredit
, okay. Das müsst ihr euch dann dazu denken.
Die sollen nichts voneinander wissen. Das soll alles
auf Ebene der Business-Logik erledigt werden. Dann kriege ich einen
viel besseren Wert hin, naja, viel besser.
Aber ich spare jedenfalls was. Ich habe nur noch 4,25.
Also ich habe ein entkoppelteres System, wenn ich sowas tue.
Ihr fragt euch gerade, warum. Ja,
das ist immer eine, das ist eine häufig gestellte Frage
und ich kann das
auch verstehen
Performance-Probleme treten heutzutage eigentlich hauptsächlich bei der Verbindung zur Datenbank auf oder übers Netz.
Aber nicht in dem Programm selbst. Also Logik,
also das Ausrechnen von etwas ist so schnell,
dass sozusagen die Verbindung hier oben hin zu verschieben,
in meiner Wahrnehmung nicht zulasten der Performance gehen kann.
Verstehst du das Argument? Ja, also Probleme treten über Netzwerkgrenzen
auf. Oh, ich will überhaupt nichts lauter machen.
Über Netzwerkgrenzen auf und wenn die Datenbank irgendwo anders ist.
Ja, oder du eine entfernten Service aufrufst
Ja. Also du könntest natürlich auch in der Datenbank mit
Stored Procedures und sowas arbeiten. Ich glaube, wenn du,
also dass du die Daten aus der Datenbank holen musst und in deinem System haben musst.
Ja, das ist ja auf jeden Fall so.
Ob die nun da drinnen über irgendwelche Referenzen verbunden sind,
über irgendwelche Fremdschlüssel oder nicht, ist eigentlich egal.
Und wenn du sie dann rausgeholt hast,
also das ist sozusagen der Moment,
wo Performance ein Problem sein kann und je feingranularer du da die Daten in deiner Datenbank hast,
umso schneller kriegst du sie raus,
würde ich mal sagen,
als wenn du so lauter Verknüpfungen hast.
Insofern eigentlich besser
Und die Frage, wo man bei einem System,
was in die Datenbank tut und was in das OO-System,
das ist schon eine relevante Frage,
über die können wir dann da hinten in eurem Studium reden, okay?
So. Aber ihr habt verstanden, warum das jetzt besser ist,
also warum dann besserer Wert rauskommt, ja?
So. Jetzt wollen wir mal was anderes gucken.
Hier hat jemand einen Zyklus eingebaut, hier, oben,
der rote Pfeil, den seht ihr, ne,
der, ja?
Da wird es viel schlechter. Was würde passieren,
wenn jemand hier
auch noch einen einbaut
Mal kurz nachrechnen? Nee, lieber nicht. Wie viel
kommt denn hinzu? Wie viele Beziehungen kommen dann hinzu?
Also wir haben ja hier oben, ist sowieso schon alles vorbei.
Was kommt zu? Was muss ich da obendrauf verlieren auf
die 63? Neun, genau. Hier plus drei.
Oh, weg da. Nee. Plus drei und hier
plus sechs.
Ja? Okay
Also wird noch schlechter. Also Zyklen machen auch die
durchschnittliche Abhängigkeit in einem System schlimmer. So,
was ihr jetzt, so jetzt habe ich gedacht,
ich hätte noch Zeit, euch noch mit mir zu quälen.
Das werde ich jetzt aber nicht tun. Das heißt,
in dem Skript sind die nächsten drei Folien sowieso nicht drin,
aber rennen auch nicht weg,
weil ich sage euch gleich noch was zu den nächsten Wochen.
Deswegen springe ich da jetzt mal drüber weg und gehe zur Zusammenfassung,
wer noch mehr lernen will,
der guckt sich die Folien an, die da noch sind.
Kurze Zusammenfassung. Wir haben heute uns noch mehr damit beschäftigt,
wie ich in einem System klare Strukturen hinkriegen kann.
Ja, also wie ich das System durch verschiedene Prinzipien auf
der Klassenebene und auf der Architekturebene besser strukturiert bekomme.
Ihr habt Solid kennengelernt, ihr habt Common Closure Principle kennengelernt,
ihr habt Common Reuse Principle kennengelernt und das Accycling Dependency Principle.
Und wir haben uns Metriken angeguckt, die nicht.
Die sind auch hübsch, aber die kommen dann ein andermal.
So, das solltet ihr mitnehmen von dieser Woche
und Das ist der Plan für die nächsten Wochen.
Nächste Woche werde ich euch was zum Berufsbild des Softwarearchitekten erzählen.
Und wir müssen, das stand vorhin auf der ersten Folie,
um 14 Uhr anfangen. Denn ich muss um 15.30 Uhr
hier raus und zur Hochschule für angewandte Wissenschaften fahren,
die HAW am Berliner Tor.
Und da als Vertreterin der Praxis die Absolventen verabschieden.
Okay, also werde eine Viertelstunde zu spät kommt,
den bestraft das Leben. Das ist dann einfach so.
Ja, in der Woche danach kommt mein Kollege Guido Grützan.
Und erzählt euch zwei Wochen lang was zu Vorgehensmodellen.
Das solltet ihr euch nicht entgehen lassen
Guido ist sehr amüsant. Ich habe mir sagen lassen,
er macht immer so, wenn er nicht will,
dass das auf dem Film kommt,
was er dann über irgendeine Firma erzählt.
Und da bin ich dann wieder da. Ich plane noch
zwei Gastvorträge. Einmal ein Kollege, der was zur Usability
erzählt und einer, der euch was über das SAP-Universum erzählt.
Den habe ich da schon reingeschrieben. Und da ist der andere.
Ich weiß noch nicht, ob die da sein werden.
Das wird sich noch finden in den nächsten Wochen.
Aber ihr werdet das dann sehen. Gut,
dann wünsche ich euch ein schönes Wochenende und freue mich auf
die nächste Woche.