Okay, herzlich willkommen zur zehnten Vorlesung von SE2. In den
letzten Wochen gab es ja hier dann einige muntere Wechsel mit den Dozenten.
Heinz war hier, hat eine Vorlesung gehalten.
Guido war hier, hat zwei Vorlesungen gehalten.
Fred war hier, hat auch eine Vorlesung gehalten.
Ihr habt jetzt einiges gehört über objektorientierte Analyse,
über mehr oder weniger ein bisschen Ansatz vom Werkzeug- und Materialansatz.
Guido hat was erzählt über interaktive Systeme, über die Entwurfsregeln,
die wir hier für euch in SE2 aufgelegt haben.
Und dann hat Fred und Guido haben letzte Woche was erzählt,
über Guiz, über Guiz-Programmierung, also insbesondere die sehr spezifische Guiz-Programmierung in Java.
Heute geht es um was anderes. Heute geht es um
was anderes. Eigentlich um ein Thema,
das komplett unabhängig ist von der Frage, ob es interaktive Systeme sind oder nicht.
Es geht darum, Entwurfsregeln zu kennen, Die helfen, Objekte und Hitte Systeme zu bauen. Also,
das, was wir an Entwurfsregeln haben,
ist sehr spezifisch für interaktive Systeme, in SE2,
gilt auch nur in SE2, hat also jetzt nicht irgendwie die Gültigkeit,
dass wir sagen, so soll man auf jeden Fall interaktive Systeme bauen,
sondern das ist eher so der Ansatz,
wir geben euch ein paar Regeln vor, damit es euch leichter fällt,
die Regeln einzuhalten und damit sozusagen Einstieg zu kriegen, wie man solche Systeme baut.
Was ich heute erzähle über das Thema Klassenentwurf ist deutlich allgemeingültiger.
Das heißt, heute werden wir die Da haben wir
die aufgesammelten Weisheiten der Softwaretechnik der letzten 30,
40 Jahre hier so ein bisschen mal durchgehen.
Wie man denn insbesondere Objektnotierte, aber nicht nur Objektnotierte Software entwerfen sollte.
Und der Einstieg ist natürlich mal wieder über das Thema Qualität.
Schon vor zwei Wochen hat wieder ein bisschen was zur Qualität gesagt.
Qualität kann man unterscheiden in die Qualität der Software nach außen.
Also das, was sie dem Klienten gegenüber anbietet,
dem Benutzer,
der Benutzerin
Da zählt alles rein, was Funktionalität ist, Zuverlässigkeit und so weiter.
Das ist natürlich auch die wichtigere Qualität. Also wenn die Software nicht das abliefert,
was sie abliefern soll, dann ist das nicht gut.
Aber weil wir ja hier in einer Informatikveranstaltung sind,
geht es uns auch darum, auf die innere Qualität zu gucken.
Das heißt, auf die Möglichkeiten, wie man den Softwaresysteme so bauen kann,
dass jemand anders sie vielleicht Jahre später immer noch gut warten kann.
Also Verständlichkeit, Wartbarkeit, Modularität,
das sind so die Aspekte, die ziemlich wichtig sind.
Und wir werden uns jetzt ansehen, wie man Klassenentwürfe machen kann.
Das heißt, wir gucken mal auf objektnotierte Systeme.
Das heißt also, das Design von objektnotierten Systementwurf,
Design, ähnliche Begriffe. Tatsächlich kann man den Begriff Entwurf
im objektnotierten Sinn, aber auch allgemein differenzieren.
Und das machen wir auch hier so ein bisschen.
Nämlich allgemein kann man sagen, hier ist ein System.
Das hat eine bestimmte Struktur. Und da kann man sagen,
oh, das hat aber, das ist ein ganz guter Entwurf,
den die da gewählt haben.
Die haben ein ganz gutes,
ganz gute Designüberlegung angestellt,
haben das dann implementiert
Und dann ist das System da und hat einen guten Entwurf.
Das ist eher was Statisches. Das heißt,
das System ist da, man kann es analysieren mit
verschiedenen Werkzeugen und diese statische Analysestruktur auseinandernehmen.
Deutlich weniger Tool belastet, aber durchaus auch inzwischen.
Die Informatiker machen ja alles mit Tools inzwischen.
Ist auch die Tätigkeit des Entwerfens selbst unterstützt.
Also wenn wir beim Entwerfen sind,
dann treffen wir üblicherweise Entscheidungen
Das heißt, Entwerfen heißt Entscheidungen treffen. Es gibt fast immer zwei Möglichkeiten,
das habe ich schon ganz zu Anfang von SE1 gesagt,
mindestens zwei Möglichkeiten, meistens gibt es mehr,
etwas zu tun, etwas umzusetzen, etwas zu realisieren.
Und man muss sich ständig entscheiden. Das heißt,
Entwerfen heißt Entscheidungen treffen. Und da kann man auch eine
Tonunterstützung bei haben, ist eher selten, wie gesagt.
Eine ganz nette Unterstützung gibt es, das sind die sogenannten CRC-Karten.
Also bevor ich jetzt gleich darauf eingehe,
was wir als wichtige Regeln ansehen für gute Klassenentwürfe,
Will ich ein bisschen was sagen zu CRC-Karten,
weil die ein bisschen sonst zu kurz kommen in dieser Veranstaltung.
Die waren früher mal bei Heinzensvorlesungen mit verortet,
da passt das nicht so gut mit rein.
Und deswegen haben wir gesagt, wir nehmen das hier mal
wieder mit rein, da passt das deutlich besser rein.
CRC-Karten. Ich zeige erstmal, wie so eine Karte aussieht.
Das ist so eine CRC-Karte am Beispiel.
Das heißt, üblicherweise nimmt man dafür eine Karteikarte.
Ihr kennt diese Karteikarten, Papier, Pappe, ein bisschen dicker.
Und wenn man mit mehreren Leuten zusammensitzt und einen objektorientierten Entwurf machen will,
dann kann man sehr schön genau sowas verwenden
Nämlich, wenn man sich überlegt,
welche Klassen sollte es denn geben in unserem System?
Ihr habt jetzt gerade die Situation, dass ihr in der
Mediathek und auch in dem Kinokartensystem eigentlich alles vorgegeben habt.
Das heißt, so von der grünen Wiese auf Systeme bauen,
habt ihr ein bisschen noch selten gemacht.
Aber wenn man sowas macht, dann helfen diese CAC-Karten sehr gut.
Dann kann man tatsächlich sich überlegen, man schreibt erstmal so
einen Namen bei einer Klasse auf so eine Karte oben links.
Und dann schreibt man, kann man so Brainstorming-mäßig aufsammeln.
Ja, was ist denn so die Zuständigkeit von dieser Klasse?
Was nur die fachlichen Eigenschaften, was muss die denn alles können?
Das müssen nicht unbedingt Methoden sein,
sondern es sind tatsächlich Zuständigkeiten
Oder Dienstleistung. Und dann gibt es noch die Frage,
mit welchen anderen Klassen muss diese Klasse dann zusammenarbeiten?
Das sind dann die Collaborators, also Responsibilities hier, Collaborators auf der rechten Seite.
Und dieses Konzept, das man so mit Karteikarten,
die man dann hin und her schieben kann, auf dem Tisch,
dass man dann auch Sachen hier durchstreichen kann, woanders raufschreiben kann,
das ist von Candback und What Cunningham mal entwickelt worden,
Ende der 80er, als die Objektorientierung noch in ihren Kinderschuhen steckte,
ein definitiv lesenswerter Artikel.
Also wenn ihr nach Möglichkeit sucht,
so einen Entwurfsprozess zu unterstützen, mit CRC-Karten,
dann lest dieses Paper, das ist sehr gut lesbar,
da sind, glaube ich, nur vier Seiten oder sechs,
sehr gut, sehr empfehlenswert, sehr lesenswert.
Ja, das ist eigentlich schon mehr oder weniger das,
was ich dazu sagen will,
weil wenn man es wirklich ausführlich vorstellen wollte,
müsste man es am Beispiel machen und dafür fehlt uns hier ein bisschen die Zeit.
Okay, also CRC-Karten, nur mal so als Anregung für
diejenigen unter euch, die da mal mehr lesen wollen.
Eigentlich ist aber die Frage,
wie kommen wir zu einem guten Entwurf und das ist das,
was wir heute als Thema hier haben.
Diese Folie ist so eine Art Übersichtsfolie,
gibt uns ein bisschen Orientierung,
was heute dran ist
Ganz allgemein gilt die Grundregel, wir machen einen Entwurf nach Zuständigkeiten,
da werde ich gleich mehr zu sagen, was das bedeutet.
Und dann gibt es zwei große Bereiche beim Klassenentwurf,
nämlich einmal die Frage der losen Kopplung.
Das ist ein Designziel beim Klassenentwurf, dass man möglichst lose,
die Klassen koppelt, mit diversen konkreten Ausprägungen,
wie man das erreichen kann.
Und die andere Richtung geht dann in hohe Korision.
Das heißt, man möchte, dass die Elemente im Softwareentwurf
alle möglichst eine hohe Korision haben.
Das Leitbild für einen allgemeinen objektorientierten Entwurf ist der sogenannte Entwurf nach Zuständigkeiten.
Oder Responsibility Driven Design. Das ist auch wieder ziemlich alt,
kommt aus einer gleichen Zeit wie das Paper von Can't Back
und Ward Cunningham, also so Ende der 80er.
Und die Grundaussage ist eigentlich total einfach.
Objects I'm not just simple bundles of logic and data,
they are responsible members of an object community.
Das heißt, wir sehen Objekte eben halt nicht nur als
Daten und ein bisschen noch Methoden,
die dann auf den Daten arbeiten
In der Objektentscheidung ist das natürlich ein Kernkonzept,
dass man die zumindest schon mal zusammenfügt, also zusammenbündelt.
Nicht nur, wie klassisch in der imperativen Programmierung sagt,
naja, wir haben Datenstrukturen und dann gibt es noch die Algorithmen,
die auf den Datenstrukturen arbeiten, sind aber total getrennt voneinander.
In der Objektorientierung wurde das dann zusammengeführt und dieses Zusammenführen kann man aber auch noch sehr unterschiedlich machen.
Man kann sozusagen immer noch die Objekte nur als zusammengeklebte Teile ansehen,
aber es ist ja immer noch die Datenstruktur und immer noch
der Dings oder man sieht sie tatsächlich als, ja,
Responsible Members, das heißt, als Objekte,
als Teile einer Community,
ein Objekt mit dem System besteht aus Objekten,
die alle
eine bestimmte Zuständigkeit haben
Und das heißt, jedes Objekt sollte eine klare Zuständigkeit haben.
Und das ist eigentlich auch nichts Neues.
Also in der Objektantierung ist dann nur sozusagen etwas konkretisiert worden,
was auch der Herr Deikstra schon früher gesagt hat,
so in Richtung Separation of Concerns.
Das heißt, teilt ein System in die Teile auf die aufteilbar sind.
Ich zeige das vielleicht mal etwas genauer an einem Beispiel.
Und zwar an einem Beispiel, das ihr schon kennen
könnt,
nämlich hier
Dieses Projekt Graf, möglicherweise erinnert ihr euch noch daran,
das war so in SE1 das letzte Projekt, was ich noch gezeigt habe.
Das ist keine Übungsaufgabe mehr gewesen. Ich glaube,
das Aufgabenblatt haben wir auch gar nicht mehr ins Komsi gestellt.
Es geht hier um ein Thema,
das ihr aus Discreta Mathematik kennt, also ein Graf,
da steht aus einer Menge von Knoten und Beziehungen zwischen diesen Knoten.
Und das ist hier modelliert, objektorientiert. Es gibt also
eine Klasse Graf und es gibt eine Klasse Knoten.
Und diese Anwendung, die hier gebaut ist, visualisiert aber auch Grafen.
Das heißt, man kann hier tatsächlich jetzt diese Main-Methode aufrufen von diesem Hauptfenster
Und dann wird hier ein Graf visualisiert.
Und wer jetzt vielleicht in den letzten Monaten die Gelegenheit hatte,
in der Mondrean-Ausstellung zu sein, da wird die Fachabgebung wiedererkennen.
Ich habe hier einfach jetzt Knoten oder Knoten in einem Grafen,
die sind hier farbig dargestellt.
Und Beziehungen zwischen diesen Knoten, nämlich als gewichteter Graf.
Das heißt, hier sind, die Kanten sind ungerichtet,
aber gewichtet. Das heißt, die haben sozusagen ein Kantengewicht,
also sowas wie ein Kosten, die man braucht,
um von einem Knoten zum anderen zu kommen oder ähnliches.
Das Ganze ist ein bisschen nett animiert hier.
Ich kann also hier die Dinge
alle bewegen
Kann die auch irgendwo hinziehen, relativ ohne Bedeutung,
einfach nur, dass es nett aussieht. Ich kann aber auch
neue Knoten hinzufügen, die dann hier irgendwie eingesetzt werden können.
Ich kann auch Verbindungen herstellen. Ich kann also jetzt hier sagen,
ein Verbindungsgewicht, was weiß ich, 34, vielleicht soll eine neue Verbindung entstehen,
von diesem Knoten zu diesem Knoten, diese Maus ist mal wieder betrunken.
Wahnsinn. Und dann ist hier tatsächlich so eine Verbindung hergestellt. Okay,
das ist nur erstmal die Visualisierung des Graphen und das ist
aber dann hier umgesetzt in diesem objektnotierten System und man sieht hier,
dass die Teile, die wirklich die Fachlichkeit abbilden,
also tatsächlich hier den Grafen selbst modellieren,
die sind hier in diesen beiden Klassen drin,
also Graf und Knoten.
Und eine Aufgabe, die wir früher mal gestellt haben,
die ihr inzwischen nicht mehr bearbeiten müsst,
ist in der Klasse Graf.
Hier ist die Klasse Graf. Also ein Graf ist eine
Menge von Knoten. Hat also hier eine Exemplarvariable Knotenmenge.
Und dann gibt es hier Möglichkeiten,
neue Knoten hinzuzufügen,
Knoten zu entfernen
Knoten direkt zu verbinden. Das sind also alles Operationen,
die jetzt in der Gui gerade an diesen Objekten aufgerufen wurden.
Also als ich das da gemacht habe mit dem Verbinden.
Gut, dann kann man sich hier alles Mögliche geben lassen.
Und dann sollte einige, sollte einiges implementiert werden.
Also eine Aufgabe wäre hier gewesen, man soll feststellen,
ob zwei Knoten miteinander verbunden sind. Also sowas hier,
ob zum Beispiel der Knoten mit diesen Knoten verbunden ist,
kann man hier deutlich sehen, ist aber in der Implementierung
nicht ganz so einfach.
Und dann hier Anzahl der Kommunen. Und auch der Grad
eines Grafen. Das sollte hier auch implementiert werden.
Gut, wer weiß denn noch aus der Diskretmathematik,
was der Grad eines Grafen ist
Boah. Hat das noch jemand parat? Was ist der
Grad eines Grafen? Ja. Anzahl der Kanten, nee
, nicht ganz. Der Grad eines Grafen ist definiert,
als der maximale Grad seiner Knoten. Das heißt,
der Grad eines Knoten ist einfach die Anzahl seiner Kanten,
die er hat. Und der Grad des Grafen ist dann
der maximale Grad an der Knoten. Das heißt,
der maximale Grad hier ist, dieser Knoten hat,
wenn ich das richtig zähle, sechs Kanten.
Das heißt, der Grad dieses Grafen insgesamt ist sechs
So, das soll jetzt hier implementiert werden.
Und in diesem Projekt war es halt so,
dass hier die Knoten relativ einfach sind,
also so einem Knoten sieht es sehr simpel aus,
man kann sich das Verbindungsgewicht geben lassen zu einem anderen Knoten.
Man kann fragen, ob eine Verbindung zu einem anderen Knoten
existiert. Dann gibt es hier diese Iteratorgeschichte,
dass man sozusagen über die Nachbarn eines Knoten iterieren kann.
Und man kann die Verbindung auch wieder trennen und ähnliches.
So, und hier sollte jetzt in der Klasse Graf
implementiert werden,
die Operation Grat
Wie würde man das jetzt machen? Ich werde jetzt hier mal,
vielleicht mal anfangen, Max Grad deklarieren, ist erstmal null
und dann liefere ich am Ende hier Max Grad zurück.
So, hat jemand jetzt eine Idee,
wie ich jetzt für den Grafen den maximalen Grad ermitteln kann?
Wie ich das hier implementieren würde, ja?
Genau, ich gehe einfach durch alle Knoten durch.
Ich kann also tatsächlich hier jetzt einfach eine Vorschleife schreiben,
vor Knoten, für jeden Knoten K in der Knotenmenge,
das ist die Exemplarvariable hier, die diese Klasse Graf hat.
Ja, und da, was mache ich da jetzt?
Ich will jetzt ja irgendwie für jeden Knoten ermitteln,
was für einen Grad er hat. Also mache ich immer
wieder eine lokale Variable, in Grad ist gleich null.
Wieso? Und jetzt, jetzt bin ich sozusagen,
ich gehe durch alle Knoten des Grafen durch und will jetzt
bei jedem Knoten wissen, wie viele Nachbarn er hat.
Wie finde ich raus von einem Knoten,
wie viele Nachbarn er hat
Was würdet ihr sagen? Was wäre eine gute Möglichkeit,
das rauszufinden? Hat die mal eine Idee? Von dem Knoten
? Ja, klar. Es wäre eine gute Idee,
da zu gucken, weil man weiß ja nicht,
ob da vielleicht eine Operation implementiert ist.
Ist aber nicht, also diese Nummer hier,
das ist was anderes,
jeder Knoten hat in dieser Implementation
irgendwie eine eigene Nummer
Das heißt also, ich kann den Knoten nicht direkt fragen,
aber das ist schon genau der Hinweis, wo ich nämlich eigentlich hin will.
Wer ist dafür zuständig, die Frage zu beantworten,
wie viele Nachbarn einen Knoten hat? Ich mache jetzt erstmal hier
die Implementierung, wie man sie machen kann, weiter.
Das heißt, ich kann ja wieder einfach,
wahrscheinlich willst du es jetzt auch sagen,
durch alle Knoten, eigentlich alle Nachbarknoten des Knoten iterieren.
Also ich kann eine weitere Vorschläfe haben hier vor Knoten,
Nachbar, für jeden Nachbarknoten von K,
kann ich jetzt einfach sagen, erhöhe mal den Grad
Um eins. So, das heißt, ich gehe durch alle Nachbarn
durch und für jeden Nachbarn erhöhe ich meinen Zähler des Grades.
Und hinterher kann ich jetzt fragen,
if Grad größer als bisheriger Max Grad,
dann mach doch mal bitte Folgendes, dann soll Max Grad gleich diesem Grad sein.
So, okay, das lässt sich so übersetzen.
Und jetzt habe ich tatsächlich hier den Grad ermittelt.
Das heißt, ich kann jetzt das Ganze hier übersetzen
Ich kann mir hier nochmal diesen Grafen anzeigen lassen und kann jetzt
tatsächlich hier über eine Operation mir diesen visualisierten Graf geben lassen.
Ich kann mir also eine Referenz hier auf die Objektleiste legen
und habe jetzt hier kapitisch eine Referenz auf genau diesen Graf,
der hier visualisiert ist.
Man kann dann jetzt fragen, was ist denn der Grad?
Und das ist die Antwort sechs. Wunderbar, also das
funktioniert. Wir können auch mal eben einen Test machen.
Ich mache nochmal eben hier eine neue Verbindung von diesem,
zu diesem, damit dann jetzt auch hoffentlich was anderes rauskommt,
also gerade Ist hier sieben.
Wunderbar. Klappt also tatsächlich. So, aber darum ging
es mir jetzt gar nicht. Das habe ich ja,
glaube ich, schon mal in der letzten Folge so von SE1 gezeigt.
Das Spannende ist jetzt, das gehört gar nicht an der Stelle,
an die Stelle, an der ich es gerade gemacht habe.
Ich habe tatsächlich jetzt hier in der Klasse Graf etwas gemacht,
dass ich diesen Code geschrieben habe.
Also das hier, um den Grad eines Knotens zu ermitteln.
Und eigentlich sollte man sich fragen,
bin ich an dieser Stelle eigentlich dafür zuständig, rauszufinden,
was der Grat des Knoten ist
In einem objektnotierten System, würde man,
wenn man viel Erfahrung hat, würde man sagen,
nee, das ist gar nicht die Aufgabe von dem Klienten,
der das jetzt hier wissen will,
der Graf ist ja ein Klient von dem Knoten,
sondern das soll der Knoten selbst beantworten.
Eigentlich ist der Knoten dafür zuständig, antworten zu können,
wie viele Nachbarn er hat. Das heißt,
wenn hier jetzt eine Operation zur Verfügung stünde in der Klasse Knoten,
die da lautet, Anzahl Nachbarn oder Anzahl Kanten,
dann könnte ich jetzt hier einfach sagen,
ich würde nach wie vor durch alle Knoten durchgehen und würde mir hier dann den Grad liefern lassen.
Also ifknoten.grad, größer Max Grad,
dann Max Grad ist gleich Knoten.grad
Das wäre schlau. Und das ist ein Beispiel dafür,
dass man sich überlegen muss, wo denn die Dinge stehen sollen.
Weil warum soll das nicht hier stehen? Weil ich kann mir
ja vorstellen, dass es auch noch andere Klienten gibt,
die dir auch für ihre Algorithmen diesen Grad ermitteln müssen von einem Knoten.
Und jetzt würde diese unschuldige kleine Vorschleife,
würde jetzt in möglicher Weise mehreren Klienten stehen,
der gleiche Code, obwohl eigentlich das einmal implementiert sein könnte,
in dem Knoten selbst.
Und der Knoten könnte diese Frage beantworten.
Okay, dann habe ich noch ein anderes Beispiel
Auch für Zuständigkeiten, also Entwurf nach Zuständigkeiten, wer ist
für was zuständig. Ihr habt ja auf dem vorletzten Aufgabenblatt
das Vormerken einbauen sollen in die Mediathek. Das war alles vorgegeben,
die Gui war vorgegeben und es gab da einen Button,
der dann nur aktiviert werden sollte,
wenn alle ausgewählten Medien für den ausgewählten Kunden vormerkbar sind.
Das heißt, ich glaube, dass hier auch nochmal als
Clips dabei,
das war das System hier
Ja. Schnorch. So, das ist die Mediathek.
So kann sie aussehen, wenn sie implementiert ist.
Da gibt es hier die Vormerkansicht. Und dann war ja hier die Idee,
Katharina Klum kann irgendwie Garden State vormerken,
aber Kai Konrad zum Beispiel kann jetzt hier nicht das vormerken,
weil er schon Ausleiher ist.
Das war also hier die Frage, wie wird festgestellt,
ob dieser Button hier ausgegraut ist oder nicht?
Der Button soll aktiviert sein, wenn tatsächlich das vormerkbar ist
und in einem anderen Fall eben nicht
Das ist ein relativ großes System, was ihr da gesehen habt.
Also was ihr da in die Finger bekommen habt.
Und ihr musstet euch da so ein bisschen einlesen.
Das waren immerhin, weiß nicht, sind es,
glaube ich, 70 Klassen oder so.
Und die Frage, wer dafür zuständig ist,
das zu machen, ist gar nicht so einfach zu beantworten.
Also an dieser Stelle hier, klar, diese Oberfläche hier,
das ist die Vormerk an sich, das ist das Vormerk-Werkzeug.
Das ist auf jeden Fall in gewisser Weise zuständig.
Aber das Vormerkwerkzeug ist ja nur sozusagen das Werkzeug,
das die Guine an der Oberfläche anzeigt
Außerdem gibt es ja noch mindestens den Verleihservice, der auch zuständig sein muss,
Auskunft darüber zu geben, ob etwas ausleihbar ist oder nicht.
Und dann war es ja immer eine Frage,
wie ihr es modelliert habt, ob ihr dann eine eigene
Verleihkarte gemacht habt oder das auf die Vormerkkarte mit eingetragen habt,
wie auch immer.
Das sind verschiedene Teile, die dann diese Frage beantworten können.
Und keiner von diesen Beteiligten an dieser Frage,
konnten die Frage alleine beantworten.
Wieder das Vormerkwerkzeug alleine, noch die Verleihkarte.
Die Verleihkarte kann ja nur feststellen,
ob sie voll ist
Ob da noch ein Verleiher eingetragen werden kann,
aber sie Verleihkarte wusste zum Beispiel nicht,
die Vormerk-Karte wusste nicht, ob das Medium schon verliehen ist.
Das heißt, nur der Verleihservice wusste das.
Das heißt, das war gar nicht so einfach.
Hier jetzt diese Zuständigkeit klar zuzuordnen. Und ihr habt insbesondere dann auch,
das habe ich zumindest im letzten Jahr noch gesehen,
als ich noch in den Übungen dabei war,
da haben dann viele auch die Verleihkarte sehr nackt implementiert.
Aber da komme ich gleich nochmal drauf. Gut,
okay, also das sind jetzt so verschiedene Beispiele für Zuständigkeiten
Ganz wichtig ist, was auch, glaube ich,
Guido schon gesagt hat, ist,
dass man allgemein in Entwürften Fachlogik und Technik trennen sollte.
Also anwendungsfachliche Klassen, die vor allem Fachlogik modellieren,
sollten deutlich von rein technisch motivierten Klassen unterscheidbar sein.
Das ist auch in diesem Beispiel so, also hier die fachlichen Klassen sind Graf und Knoten.
Und das sind die Klassen, wo ich dann auch einen Mathematiker,
der sich mit Diskreta Mathematik und Grafentheorie gut auskennt.
Da kann ich mit denen drüber reden, wie man das modelliert. Während das hier,
also die ganze Logik, wie man das anzeigt mit hübschen,
kleinen Bildchen und so weiter und dass das bewegbar ist,
Das ist Technik von Java, die total, ja,
sagen wir, sprachabhängig ist, während das hier diese Modellierung,
die kann man auch in jeder anderen Sprache so machen.
Und das ist einfach sinnvoll, das auch tatsächlich zu tun,
also anwendungsfachliche Klassen von der Fachlogik zu trennen. Gut,
dann komme ich jetzt zu den ersten der beiden großen Begriffe,
nämlich die Frage der Kopplung. Und Kopplung ist ein Begriff,
der, ja, hier ist es gesagt,
den Grad der Abhängigkeiten zwischen den Einheiten eines Softwaresystems beschreibt.
Das heißt, Abhängigkeiten in einem objektuentierten System. Wisst ihr,
sind entweder solche Benutzbeziehungen oder solche Implementsbeziehungen oder auch Vererbungsbeziehungen?
Das sind die klassischen Beziehungen, die man haben kann an einem OO-System. Und den maximalen Grad,
den man haben kann an Beziehungen zwischen Objekten, ist,
wenn jedes Objekt, das es hier unten angedeutet,
wenn jedes Objekt, jedes andere Objekt kennt,
wenn also tatsächlich jedes Objekt mit jedem anderen irgendwie in Abhängigkeit steht,
ob das nur benutzt oder ob es eine Exemplarware davon hält oder ähnliches, wie auch immer,
Das ist etwas, was man relativ schnell einsehen kann, hoffe ich, dass das keine gute Idee ist.
Also die Elemente eines Softwaresystems alle miteinander zu verkoppeln.
Weil dann nämlich tatsächlich es schwierig wird, sich in einen Entwurf einzulesen.
Erstrebenswert ist eher sowas hier, hier ist so angedeutet,
hier ist so ein Element des Softwaresystems,
das kennt alle anderen, aber die untereinander kennen sich nicht.
Das ist eine deutlich niedrigere Kopplung zwischen den Einheiten
Und das ist so eine, sagen wir mal,
grafische Veranschaulichung eines Entwurfsmusters. Guido hat schon Entwurfsmuster vorgestellt im Prinzip
. Das ist ein Entwurfsmuster hier, der sogenannte Mediator,
also dieser schwarze Böbbel hier, ist offensichtlich ein Element im Klassenentwurf,
das alle anderen kennt und zwischen denen vermittelt.
Also wenn irgendwie der eine von dem anderen was will,
dann geht das alles über diesen schwarzen Böbbel hier.
Das ist der Mediator. Das ist also durchaus ein relevantes Entwurfsmuster. So,
und die Abhängigkeiten können halt Benutzbeziehungen sein für anderes Beziehungen,
habe ich schon gesagt. Und die Einheiten in einem O-System
können seinen Methoden, Klassen, Pakete, Subsysteme.
Also wenn ich große Systeme mir angucke,
dann geht das auch noch über die Paketebene hinaus
Und eigentlich ist es ganz einfach,
je mehr Abhängigkeiten ich habe, desto stärker ist die Kopplung.
Und das Ziel ist möglichst lose Kopplung. Möglichst wenig Kopplung haben in
einem objektnotierten System. Und das ist erstmal nicht einfach.
Und zweitens kann man auch nochmal fragen, warum denn überhaupt?
Naja, das ist erstrebenswert. Wenn ich eine einzelne Klasse verstehen will und ich weiß,
sie ist jetzt mit allen anderen Klassen im System verbunden,
dann muss ich erstmal möglicherweise alle anderen Klassen mir angucken,
um die verstehen zu können.
Das ist unglücklich. Wenn ich so einen Graf habe von Elementen,
im Softwaresystem existieren und ich habe so Blätter,
also so Klassen, die eigentlich keine anderen Klassen benutzen,
sondern nur von den anderen benutzt werden.
Das sind schon mal sehr elementare Klassen,
da hat man schon mal eine gute Hoffnung,
dass man die für sich alleine verstehen kann.
Oder man fängt eben halt bei einem Grafen,
bei einer baumartigen Struktur irgendwie weit oben an und sagt,
naja, da ist erstmal sozusagen der Top-Level-Ablauf zu erkennen.
Aber die werden dann auch wieder von niemandem benutzt.
Also das heißt, es ist auf jeden Fall schon mal hilfreich,
wenn die Kopplung niedrig ist, um sich in einen Entwurf einzulesen.
Außerdem ist es auch klar,
wenn ich jetzt eine der Klassen ändern will,
dann muss ich auf alle Klassen achten,
die diese Klasse benutzen
Und wenn mich viele Klassen benutzen,
dann muss ich eben halt auch viele andere Klassen achten.
Das heißt also, auch da, wenn ich ändern will,
ist es auch hilfreich, eine lose Kopplung zu haben.
Also lose Kopplung erleichtert die Wartung, keine Überraschung.
Jetzt ist die Kopplung, wie ich sie hier so bisher beschrieben habe,
so hier fast automatisiert sichtbar zu machen oder sichtbar machbar.
Ich kann also tatsächlich Blue Jay ja hier bitten,
diese ganzen Pfeile und so weiter anzuzeigen.
Das ist ja alles hier sichtbar. Man sieht also Melder zum Beispiel,
wird hier benutzt von dem Hauptfenster oder wird beerbt von dem Hauptfenster,
Grafpanel benutzt den Melder,
Graf benutzt Knoten,
das kann man sehen
Das sind Abhängigkeiten, Beziehungen, die aus dem Quelltext direkt abgelesen werden können.
Jedes Tool der Welt kann da sowas visualisieren, nicht nur
Blue Jay, da gibt es ganz viel Zeug dafür.
Das ist prima. Da gibt es auch wirklich gute Werkzeuge,
aber es gibt auch sowas wie implizite Kopplung.
Und das ist was ziemlich Gastiges, das ist eine Kopplung,
die nicht formal nachweisbar ist, also die man nicht durch Tools rausfinden kann.
Und das ist immer dann, wenn ein Entwickler,
der ein bestimmtes Modul oder eine Klasse schreibt,
Wissen über Interner einer anderen Klasse in diesen Quelltext hier reinkotiert und reinkodiert.
Ich habe das mal an einem Beispiel hier,
kann ich das ganz gut verdeutlichen, das habe ich selbst mal erlebt,
als ich noch Student war, vor langer Zeit.
Da haben wir in so einem Forschungsprojekt in einem Compiler geschraubt. Das war eine Forschungsarchitektur,
also wirklich eine eigene Rechenarchitektur und dann haben wir einen eigenen Compiler gehabt,
der hat so einen Pascal-Dialekt übersetzt, so ein Objekt orientiertes Pascal.
Und da gab es innerhalb dieses Compilers,
gab es dann so eine Situation, da ging es um fachliche Behälter.
Ich weiß gar nicht mehr, was da drin war,
das ist, was hat Guido ja schon eingegeführt,
den Begriff, also fachliche Behälter so, wo irgendwelche wichtigen Elemente,
die für die Fachlichkeit, für den Compiler wichtig waren,
drin waren.
Und es gab einen Teil des Compilers, der auf diese
fachlichen Behälter zugegriffen hat. Ich glaube, da waren die Token,
die da so bei der Sonntagsanalyse rauskamen, waren da drin oder ähnliches.
Wie auch immer. Auf jeden Fall hat der Klient auf
diesen fachlichen Bällen dazugegriffen und die Schnittstelle dieses fachlichen Behälters hat gesagt,
es ist möglich, alle Elemente, die da drin sind,
der Reihenfolge nacheinander abzufragen.
Also man kann sozusagen durch alle Elemente einmal durchiterieren.
Das war schon damals, selbst vor den Zeiten von Java,
gut möglich, beziehungsweise auch sehr,
sehr nützlich,
sowas machen zu können
Und implementiert war das, und das war dann natürlich so,
dass der Mensch, derselbe Mensch gewesen,
der den Klienten geschrieben hat, hat auch den Behälter geschrieben,
der hat das halt so implementiert,
dass die Elemente einfach alphabetisch sortiert rausgegeben wurden.
Die waren irgendwie in der internen Struktur, waren die alphabetisch abgelegt,
also konnte er die da auch relativ leicht alphabetisch sortiert rausgeben.
Das war aber nicht dokumentiert. Also hier stand nur an
der Schnittstelle, alle Elemente werden geliefert.
Hier wurden sie alphabetisch geliefert. So, das heißt,
der Klient hat hier das gewusst und hat das auch ausgenutzt,
hat sich so darauf verlassen,
dass die Dinge alphabetisch herauskommen,
alphabetisch sortiert
Dann sind ein paar Monate vergangen,
wie das immer mal so ist, in solchen Forschungsprojekten.
Dann kamen andere Leute. Ich glaube sogar,
wir selbst waren das, ich war das.
Und ersetzt die Implementation des fachlichen Behälters. Das heißt also,
da musste irgendwie aus Effizienzgründen dieser fachliche Behälter was anderes sein.
Da haben wir eine Hash-Implementation gewählt. Und wir haben uns
auch sauber an die Schnittstelle gehalten. Wir haben also tatsächlich hier gesagt,
naja, man kann alle Elemente der Nacheinfolge nach abfragen.
Dummerweise nicht mehr alphabetisch. Das wurde uns aber ja auch nicht,
das war ja nicht spezifiziert, dass die alphabetisch rauskommen sollen.
Aber hier hat es gekracht
Ja, an dieser Stelle, wo der Klient jetzt bisher davon ausgegangen war,
das kommt euthabetisch, jetzt kam es nicht mehr alphabetisch,
dadurch hat es irgendwie Probleme gegeben.
Die waren aber nicht sofort zu identifizieren.
Man wusste nicht sofort, woran es liegt. Das heißt,
man musste wirklich erst mal da reingehen,
den Code lesen und verstehen, was da passiert,
um dann zu sehen, oha, der Klient ist davon ausgegangen,
dass es alphabetisch kommt und es kommt nicht mehr alphabetisch.
Sowas ist extrem schwer zu finden. Das sind so,
das sind so Kopplungen, das ist ein Beispiel für eine implizite Kopplung,
die könnt ihr mit keinem Tour der Welt identifizieren.
Die ist einfach so da. Und da kann ziemlich viel schief gehen. Gut,
dann eine weitere wichtige Entwurfsregel für das Thema Kopplung,
also wenn ich über lose Kopplung reden will,
also wenn ich möglichst lose Kopplung erreichen will in einem Softwaresystem,
dann erreiche ich das ja,
indem ich einfach die Schnittstellen zwischen den Modulen klar definiere.
Das heißt also,
die Schnittstellen sind sehr wichtig,
sehr zentrales Konzept
Gebraucht, weil man es sehr, sehr erweitert.
Jetzt kannst du aber, was ist eines der Anwendungen von heftig?
Freiwillig, immer wieder in einer vielen Abarbeitung,
immer drei nacheinander. Das heißt, du rufst,
endet deine Nummer hoch und setzt nur einen, kombinieren sie.
Ich kann aber sagen, wir führen eine Wahl vor,
aber vorbereitet, liest man das schon, als sie drei,
das war so, die einmal zusammen liest.
Alle Grüße, die schließende Betriebe des Bahns,
in vielen Einungsfällen, aber es ist immer wieder,
das ist nicht so eine Erfahrung,
weil man ja immer vorrangig in die Hand hat,
von ob die Fachlichkeit ist auch in der Erfahrung
Sie soll möglichst klar sein, klar,
also ich möchte natürlich, dass meine Schnittstellen gut benutzbar sind.
Das heißt also, dass auch jeder Klient weiß,
wie er sie korrekt benutzen kann.
Konsistent. Konsistent, da habe ich ein kleines Beispiel dabei.
Für die Konsistenz von Schnittstellen, das ist hier im Collections Framework,
Java YouTill, da gibt es die Klasse Dect,
Java YouTill Date.
Mal gucken,
wo ist sie denn
Na? Deckt da. Das ist eine Klasse,
die ein Datum modelliert. So, ein Datum ist hier
auch irgendwie Specific Instant in time with milly second precision.
Okay, also das kann man sich vorstellen,
das ist irgendwie so ein ganz exakter Zeitpunkt.
Und dann steht hier relativ viel Text dazu,
was ist ein Datum denn so eigentlich ist und das ist ja auch nicht
so ganz einfach und das war sehr aufpassen und so.
Und dann sind hier so ein paar Erläuterungen dazu.
In all methods of Class Day,
that accept or return year months date hours minutes und so weiter,
the volling representations
are used
So, da steht hier, hier,
ist represented by The Intager Y, minus 1900. Also,
das ist irgendwie hier, die Jahre sind irgendwie so repräsentiert.
Ein Monat, es repräsentiert durch einen Intager von 0 bis 11.
Also 0 ist der Januar und 11 ist der Dezember.
Das finde ich schon mal ungewöhnlich, aber wir wissen ja,
Informatiker fangen gerne bei 0 an.
Also haben die hier die Monate von 0 beginnt gezählt.
Dann kommt A Date, also ein Tag auf einen Monat
, ist Represent by Intager from
Das heißt, die Tage innerhalb eines Monats, die fangen bei eins an,
aber die Monate innerhalb des Jahres, die fangen bei null an.
So, und ganz ernst, das ist nicht konsistent,
ja, das ist einfach komisch. Und es gibt mit Sicherheit
ziemlich viele Programmierer, die da schon irgendwie gescheitert sind.
Und das ist so ein Off-by-One-Fehler für Informatiker,
das ist ja, das ist ja das Lieblingsthema,
ja, plus minus eins, ja, immer so.
Und das ist genau das, was hier einfach auch unglücklich ist,
das ist nicht konsistent. Und wenn ich dann jetzt von
dieser Schnittstelle irgendwas wiederbekomme und dann jetzt eine Elf kriege und denke,
oh, das ist wahrscheinlich der November, weit gefehlt.
Es ist dann eben halt der Dezember.
Und da kann man sich ganz schön ins Knie schießen,
also von daher auch so konsistente Schnittstellen ist gar nicht so einfach.
Sollte man aber trotzdem darauf achten. Gut. Noch eine weitere Konkretisierung,
also Schnittstellen, möglichst gute Schnittstellen zu verwenden, auch um Kopplung zu verwenden.
Da habe ich ja schon in SE1 gesagt, Interfaces sind sehr,
sehr wichtig und die tauchen eigentlich immer wieder auf als sehr hilfreiches Konzept,
um gute Entwürfe zu bauen.
Man kann tatsächlich die Kopplung zwischen zwei Klassen verringern,
indem man ein Interface modelliert, das nur die Operationen zusammenfasst,
die von der einen Klasse benutzt wird.
Also irgendwie Klasse A benutzt Klasse B. Klasse B hat 20
Euro Methoden oder Operationen an der Schnittstelle. Und Klasse A benutzt aber nur drei davon. Dann ist es eine gute Idee,
zu sagen, ich ziehe da ein Interface dazwischen,
dass nur diese drei Operationen enthält,
weil ich dann sozusagen diese Klasse von dieser Klasse entkoppel,
weil sonst weiß ich ja gar nicht,
also in dem Diagramm, auch in diesem Diagramm hier,
sehe ich ja jetzt nicht,
Graf benutzt Knoten,
benutzt Graf tatsächlich alle Operationen von Knoten oder nur eine Untermenge
Das ist bei Graf und Knoten relativ, sagen wir,
relativ klein, da würde man jetzt kein Interface dazwischen ziehen,
aber es kann häufig sehr sinnvoll sein,
mit einem Interface explizit zu modellieren,
dass nur ein Teil der Schnittstelle wirklich benötigt wird.
Damit kann man tatsächlich statische Zyklen auflösen.
Ich habe gleich ein Beispiel dafür. Ganz generell,
wenn ihr den guten Gamma euch noch irgendwann nochmal ins Bücherregal
stellt und ihn vielleicht sogar mal lest,
sämtliche Entwurfsmuster haben mit als eine Entwurfsrichtlinie genau das.
Also da steht auch tatsächlich in der Einleitung dazu,
Program to in the Interface, not in implementation.
Man sollte also immer versuchen, auf eine Schnittstelle,
auf ein Interface hin zu programmieren und nicht auf eine Implementation.
Man sollte also versuchen, da weitgehend von den konkreten Implementationen zu abstrahieren.
Ich habe ein Beispiel hier, das ist, Moment,
das ist das Hauptfenster von diesem Graf-System.
Das sieht also ungefähr genauso aus wie das hier.
So, das ist es ungefähr
Und man hat also oben so eine Reihe von Buttons,
so ein Border-Layout, J-Framebit, einem Borderlay,
das kennt ihr jetzt ja alles, Borderlayout,
J-Buttons in der Nordkomponente, ein J-Label in der Südkomponente,
also hier so eine Meldungszahl,
da habt ihr vielleicht eben gar nicht gesehen,
aber hier ist auch noch so eine,
hier werden so Meldungen angezeigt, kann also hier irgendwie,
so, und da drin ist als Center-Komponente ein Graf-Panel,
also das Ding, was hier in dem Klassendiagramm der Graf-Panel ist,
das ist genau dieses Ding hier,
mit dem ich hier so ganz schick irgendwie diese Dinge hin und her bewegen kann.
Okay, und ein ganz einfaches Entwurfsproblem an der Stelle ist jetzt,
das umgebende Fenster, also der J. Frame,
der kennt natürlich die enthaltene Komponente.
Da ist die Benutzbeziehung offensichtlich oder die Abhängigkeit ist klar.
Aber in der Center-Komponente kann ich ja hier mit meiner Maus irgendwie so Dinge bewegen.
Und abhängig davon, was ich da mache,
soll ja die Meldungszeile hier unten aktualisiert werden.
Also irgendwie muss jetzt diese Center-Komponente, dieser Graph-Panel,
auch sein umgebendes Hauptfenster kennen und dann irgendwie sagen,
was da geschehen soll.
Das war tatsächlich ein erster Entwurf,
dass hier Hauptfenster und Graph-Panel sich direkt zyklisch gegenseitig benutzt haben
Und Zyklen komme ich gleich noch drauf, sind nicht schön.
Also ist das hier, oh ja, sehr schön.
Hier ist es aber jetzt anders gemacht. Man sieht hier,
dass da irgendwie ein Interface dazwischen ist. Das heißt,
ich habe eine klassische zyklische Abhängigkeit, die ich irgendwie auflösen wollte.
Das Problem war aber eigentlich gar nicht so dramatisch,
weil tatsächlich der Graf-Panel das Hauptfenster gar nicht unter seiner vollen Schnittstelle benutzen wollte.
Also wenn man hier guckt, das Hauptfenster hat irgendwie eine
ziemlich große Schnittstelle hier, irgendwie statisch allein schon.
Aber das braucht der Graf-Panel gar nicht. Der Graf-Panel will ja
nur Der will nur eine Meldung rausgeben. Das heißt,
ich habe dem das Hauptfenster jetzt ein Interface implementieren lassen,
ein Interface-Melder. Und das sieht hier wirklich ganz einfach aus,
dass es hier nur dieses ganz simple Interface melde,
eine Operation melde mit einem Training, gibt die übergebende Meldung aus.
Ganz einfach. Und plötzlich braucht der Graf-Panel das Hauptfenster
gar nicht mehr direkt zu kennen,
sondern er sagt einfach,
das Graf-Panel kennt irgendetwas vom Typ Melder und das Hauptfenster kann
dann den Graf-Panel erzeugen und dann dem Graf-Panel eine Referenz
auf sich selbst übergeben
Über das Interface-Melder. Das heißt, das Graph-Panel,
der Graph-Panel kennt das Hauptfenster nur über das Interface-Melder.
Und das ist tatsächlich dann hier das, was dann modelliert ist.
Das heißt, der direkte Zyklus zwischen den Klassen ist statisch aufgelöst.
Aber zur Laufzeit ist natürlich der Zyklus weiter da.
Es sind immer noch zwei Objekte, die sich gegenseitig benutzen.
Aber das ist längst nicht so schlimm, als wenn das
hier statisch ist. Gut. Das ist erstmal das,
was ich hier zu Abhängigkeiten sagen wollte und zu Interfaces.
Wir sind auch jetzt schon gut,
wir könnten jetzt fünf Minuten Pause machen.
Fünf Minuten Pause
Am... Gut, also das war jetzt ein Beispiel,
wo man die sehr enge Kopplung zwischen zwei Klassen
ein bisschen auflockern kann über ein Interface.
Eine weitere wichtige Sache beim Objekten und den Entwurf ist,
dass man Entwurfsentscheidungen kapseln sollte.
Das habe ich wahrscheinlich auch schon an diversen Stellen vorher mal erwähnt.
Man kann das nochmal aus einer anderen Sicht betrachten
Ich habe vorhin schon gesagt, entwerfen heißt Entscheidungen treffen.
Das heißt, während man ein Softwaresystem baut, muss man ständig Entscheidungen treffen.
Und eine relativ einfache Weisheit ist zu sagen,
naja, wenn ich eine bestimmte Entscheidung getroffen habe,
dann hängen möglicherweise von dieser Entscheidung nachfolgende Entscheidungen ab.
Das ist relativ häufig so. Und dann ist eine gute Heuristik zu sagen,
die Entscheidungen, die sich ziemlich wahrscheinlich nicht ändern werden im Laufe des Projektes,
die treffe ich zuerst.
Also da, wo am Wesen Wandel zu erwarten ist,
kann ich meine Entscheidung als erstes treffen und dann alles andere davon abhängig machen.
Ich möchte einen Entwurf so bauen, dass ich eine Flexibilität behalte.
Und die Flexibilität behalte ich, indem ich Entwurfsentscheidungen möglichst spät
aufschiebe, die sich schnell ändern können. Das heißt,
in einem System bei einer Versicherung zum Beispiel, wird es,
wenn es um Versicherungsverträge geht und um Laufzeiten von Versicherungsverträgen,
dann wird diese Fachlichkeit vermutlich sehr, sehr stabil sein.
Während so die Frage, mit welcher Gui-Technik man das Ganze jetzt darstellt,
Die kann sich im Laufe eines Projektes durchaus ändern.
Plötzlich muss dann eine Web-Schnittstelle gebaut werden, aber die fachliche Lobik bleibt gleich.
Das heißt, da ist es einfach schlau. Die Dinge,
die sich in hoher Wahrscheinlichkeit ändern, dass wir die tatsächlich kapseln.
Das heißt, wenn wir eine Schnittstelle definieren und dann dahinter
die Entwurfsentscheidung ändern können. Das ist einer der Vorteile,
wenn man Dinge kapselt, dass man dann das,
was dann gekapselt ist, leichter austauschbar ist und weniger Klienten betrifft.
Und was man natürlich braucht, ist dann ausreichend Informationen über
den Kontext, Erfahrung, Augenmaß.
Okay, das Geheimnisprinzip ist auch so eines der zentralen Konzepte
für das Kapseln von Entwurfsentscheidungen
Nur relevante Merkmale, einer Entwurfs- oder Konstruktionseinheit sollten für Klienten
sichtbar und zugänglich sein. Details der Implementierung sollten verborgen bleiben.
Und allein das Wissen über Interner kann schon missbraucht werden.
Wir haben eben dieses Beispiel gesehen mit diesem Compiler-Beispiel,
wo ein Klient interner wusste und sich auf diese interner
verlassen hat und damit sozusagen schon Missbrauch getrieben hat.
Das ist sehr ausführlich beschrieben worden von David Panas.
Den habe ich auch schon mal erwähnt.
Der ist auch schon an anderen Stellen erwähnt worden
Das hat er mal zu einer Zeit geschrieben, dieses Geheimnisprinzip,
als das noch allgemeine in der Informatik galt.
Wir machen einen Top-Down-Entwurf, wenn wir ein System bauen,
dann machen wir Step-wise, Refinement und machen das vom Großen ins Kleine.
Entlang des Kontrollflusses werden wir sozusagen verfeinern,
wie immer mehr, bis wir kleine ausprogrammierbare Schritte haben.
Und der gute Panas hat immer gesagt, das ist Quatsch.
Wir sollten eigentlich unser System anders modularisieren. Und er hat eben
halt dieses Geheimnisprinzip des Information Hidling Principle formuliert. Und dabei geht es,
wie gesagt, nicht um das Verstecken von Informationen,
dass man irgendwie Geheimnisse hat,
sondern dass man tatsächlich Dinge zurückhält,
damit Klienten erstens nicht überfrachtet sind mit diesen Informationen und zweitens Klienten auch nicht Missbrauch mit diesen Details treiben können.
Okay. Dann eine auch relativ einfache Entwurfsregel,
die aber nicht unbedingt mal leicht einzuhalten ist, ist Zyklen sollten vermieden werden.
Wir haben eben schon so ein Beispiel gesehen,
da waren zwei Klassen in einer zyklischen Abhängigkeit.
Und da gilt natürlich auch wieder,
wenn die Kopplung über Zyklen existiert,
dann lassen sich die beteiligten Klassen schlecht unabhängig voneinander testen
Klar, also wenn ich irgendwie drei Klassen in einem Zyklus habe,
welche kann ich dann testen, weil ich weiß ja nicht,
welche jetzt irgendwie von den anderen und so weiter.
Die Initalisierungsreihenfolge der entstehenden Objekte ist möglicherweise auch unklar und es
gibt eben halt keinen offensichtlichen Einstiegspunkt, um sich einzulesen.
Das gilt für Klassen, das gilt aber auch für Pakete oder Subsysteme.
Es gilt eigentlich sogar noch viel mehr für größere Einheiten
als für kleine Einheiten. Also bei einem System wird in
einem Paket zwei Klassen sich gegenseitig benutzen,
wie wir das gerade hier auch gesehen haben,
in diesem kleinen Beispiel hier
Da wäre es vielleicht nicht nötig gewesen, diese Abhängigkeit aufzulösen.
Das habe ich hier auch so ein bisschen zu Demonstrationszwecken gemacht.
Weil, wenn zwei Klassen so nah miteinander verbandelt sind,
dann ist das durchaus okay, wenn die auch sich gegenseitig kennen.
Also es gibt die Puristen, die sagen,
Zyklen sind immer schlecht und es gibt die Leute,
die das ein bisschen entspannter sehen.
Und ich bin eher in der zweiten Fraktion,
die sagen, wenn die Dinge nah genug beieinander sind,
dann können die sich ruhig gegenseitig benutzen.
Aber wenn ich hier eine Klasse habe in dem einen Paket und
eine Klasse in dem anderen Paket und die benutzen sich zyklisch,
dann haben wir eine ganz komische Situation.
Also Dinge, die nah beieinander sind, können ruhig sich zyklisch benutzen.
Das passiert auch relativ häufig bei allen geschachteten Klassen. Wenn ihr in Java,
habt ihr jetzt schon kennengelernt, es gibt die statisch geschachteten Klassen,
die habe ich mal hier vorgestellt und Fred hat die dynamischen,
geschachteten Klassen vorgestellt, wo man wirklich dann so ein Exemplar,
ein Wirtsobjekt hat, wo man dann dranhängt.
Und da ist es auch so, die kennen sich sehr gut,
die können sich gegenseitig tatsächlich sogar die privaten Exemplarvariablen ansehen.
Das heißt also, eng beieinander gut, entschuldbar,
wenn es einen Zyklus gibt, aber je weiter die Dinge auseinandergehen,
desto weiter sollte, desto stärker sollten Zyklen vermieden werden.
Das, was ganz schön ist, dass unsere Entwurfsregel,
die wir euch ja hier so wärmstens ans Herz legen für SE2,
die haben ja hier diese Elementtypen, Werkzeugservice, Material und Fachwert.
Und da sind ja auch die erlaubten Benutzbeziehungen festgelegt.
Das heißt also, tatsächlich sagen wir, Werkzeuge dürfen alles benutzen,
aber Fachwerte dürfen nur andere Fachwerte benutzen und so weiter.
Und wenn man das tatsächlich dann weiterführt, dass wir,
wir haben ja auch empfohlen,
dass wir dann diese Dinge entsprechend benannte Pakete legt
Das heißt, wir haben dann die entsprechende Pakete dazu,
die dann Werkzeuge, Services, Materialien und Fachwerte heißen.
Und der Vorteil ist, wenn man dann diese Entwurfsregeln auf der kleinen Ebene von Klassen einhält,
dass das dann natürlich auch transitiv auf der Paketebene richtig funktioniert.
Und dann sind tatsächlich auch diese Pakete unabhängig voneinander verstehbar.
Auch da ist es jetzt wieder so,
ich kann mich jetzt in den Entwurf einlesen,
indem ich sage, na gut, ich verstehe erstmal die Fachwerte,
dann gucke ich mir die Materialien an,
dann sehe ich mir die Services an und zum Schluss,
wie denn diese ganzen Dinge zusammen interaktiv mit einem Werkzeug
zugänglich gemacht werden
Also auch da dieses Zyklenvermeiden ist auf jeden Fall zu empfehlen.
Eine weitere Entwurfsrichtlinie, die heißt Law of the Mieter,
die gibt es auch schon ziemlich lange.
Ende der 80er ist die formuliert worden und hieß,
war von Anfang an eigentlich nur eine Entwurfsrichtlinie.
Wurde aber Law genannt, Law of the Mieter.
Die Miete war ein Softwaresystem, das es damals gab,
also so ein mehr oder weniger, heute würde man sagen ein Framework.
Und dieses Low auf die Miete können wir uns erstmal
selbst angucken. Also wie es denn eigentlich lautet,
wenn wir eine Methode M haben an einem Objekt O,
dann soll innerhalb dieser Methode ausschließlich Methoden von folgenden Objekten aufgerufen werden.
Das muss man sich so ein bisschen klar machen.
Also erstmal Methoden von O selbst, klar,
also in der Methode M kann ich natürlich eigene Methoden des Objektes aufrufen.
Dann, wenn die Methode M Parameter übergeben bekommt,
dann kann ich natürlich auch diese Objekte aufrufen.
Wenn in der Methode Objekte erzeugt werden, ist es sinnvoll,
dass man die auch dann die Methoden dieser Objekte aufrufen kann.
Und wenn O Exemplarvariabel hält, dann sind die natürlich auch nicht durch Zufall da.
Die sind natürlich da, damit die Methoden auf die Exemplarvarianten zugreifen können.
Die können auch aufgerufen werden. Aber sonst keine andere Methode.
Wenn man das konsequent zu Ende denkt, dann ist das eine relativ restriktive Regel.
Umgangssprachlich ist das hier so dargestellt, sprich nur mit deinen engsten Freunden.
Also nur mit den Objekten, die in deiner unmittelbaren Nähe sind.
Das hört schon damit auf,
wenn ich als Parameter eine Referenz bekomme auf ein Objekt,
jetzt dieses Objekt aufrufe und mir von diesem Objekt ein weiteres Objekt geben lasse,
dann kann ich an diesem Objekt,
sollte ich eigentlich schon nach dem Law auf dem Mieter keine Methoden mehr aufrufen.
Und so ein Bad Smell, also so ein Hinweis,
dass irgendwo das Law auf die Mieter verletzt ist, sind solche Sachen hier.
Solche langen Aufrufketten. Also irgendwie gibt es anscheinend eine Referenzjudent.
Nehmen wir mal an, dass es irgendwie in so einem
System wie Stine implementiert. Da geht es irgendwie darum,
dass man hier die 93 Punkte oder 93 Prozent,
was auch immer, eintragen soll bei einer einem Studenten in seinen Unterlagen,
hier, Student Get Record,
Get Exam Entry für eine SE2-Veranstaltung und dann wird da irgendwie ein Ergebnis eingetragen.
Das kann man so einigermaßen lesen, aber warum ist das
jetzt hier ein Problem? Warum ist das ein Drama?
Ein Drama ist es nicht, aber es ist möglicherweise ein Problem.
Es ist eine lange Aufrufzeile und eine lange Aufrufzeile ist implizit
eine hohe Kopplung oder nicht nur implizit, sogar explizit eine hohe Kopplung.
Wenn wir uns das Beispiel hier angucken, Student,
das scheint irgendwie hier die Klentenklasse zu sein,
der benutzt, der Klient benutzt den Student,
die Klasse ist tund,
offensichtlich irgendwie in einer Weise
Und dann wird aber hier jetzt tatsächlich an diesem Student ein Geard Record aufgerufen.
Also gibt es irgendwie einen Record, das irgendwie an dem Studentenwohl verankert ist.
Das wird hier benutzt. Und bei dem Record ist irgendwie
ein XM-Entry drin. Da kann man dann schon wieder so
einen Zusammenhang herstellen. Und das benutzt der Klient auch.
Und dann ruft er daran auf Ed Result. So,
das heißt, in dem Klient ist jetzt ausprogrammiert,
dass da diese Struktur existieren muss. Das heißt,
in einer unschuldigen Codezeile ist ein Zugriff auf ein ganzes Geflecht von Objekten festgelegt.
Und wenn ich jetzt diese Struktur hier in irgendeiner Weise ändern will,
wenn ich also sozusagen diesen Entwurf umbauen will,
Dann muss ich alles stellen, identifizieren,
an denen dieses ganze Zeug hier irgendwie steht.
Das heißt, es ist plötzlich hart codiert in meinem Programmtext,
dass diese Struktur genau so existieren muss.
Und das ist nicht gut, weil wir dadurch sozusagen,
weil wir dadurch die Klienten sehr stark an diese Struktur koppeln.
Also das ist ein gutes Beispiel für Kopplung.
Und dieses Low auf die Mieter ist sehr umstritten.
Es gibt also durchaus Kritiker, die sagen,
viel zu harte Regel, kann man gar nicht ernsthaft verwenden.
Aber es gibt zeitgemäße Anpassungen, die eigentlich in die
gleiche Richtung gehen
Und das heißt, eigentlich muss man,
dass dieses Law of the Mieter eher in seinem Geist verstehen,
also in dem, was damit eigentlich ausgesagt werden sollte,
nämlich diese Idee von, benutze nur die Objekte,
die dir direkt zur Verfügung stehen.
Und sobald du anfängst, darüber hinauszulaufen und irgendwelche Objektstrukturen zu retravisieren, muss irgendwas faul sein,
dann solltest du überlegen, ob du die Schnittstellen anders baust.
Das ist die zeitgemäßen Anpassungen sind dann hier,
if you delegate, delegate fully,
also wenn ich irgendwie eine Aufgabe erfüllt bekommen möchte,
dann sollte ich nicht irgendwie selber die Objektstruktur travisieren,
sondern einfach mal im nächsten Objekt sagen,
du,
mach du mal,
kümmer du dich mal drum
Eine andere, also sind alles Zitate aus Entwurfsrichtlinien,
die ich zusammengesucht habe. Don't message your delegates,
objects, objects. Also schickt keine Nachrichten an,
Objekte, die du nur sozusagen ein unmittelbarer Reichweite hast.
Don't be aware of house some object works,
don't work at a lower level than is necessary.
Da habe ich so ein Beispiel, ich weiß nicht,
wie das in diesem Jahr war, im letzten Jahr in SE2,
als ich noch in den Übungen mit dabei war,
da haben viele die Vormerkkarte gebaut.
Die Vormerkkarte hatte dann intern eine Q,
irgendwie intern implementiert,
für die ganzen Vormerker
Und wenn jetzt ein Klient wissen wollte,
ob eine Person, die jetzt als Vormerker darauf soll,
ob die da schon drin ist,
da haben viele dann gesagt, okay, Vormerkkarte,
gib mir mal deine Cue raus und dann hühnere ich selber durch die Cue durch und gucke nach,
ob es drin ist.
Richtig ist eigentlich das richtige Delegieren. Das heißt, ich sage,
der Formel-Karte als Klient, hallo, ist diese Person bei dir vorgemerkt?
Und dann soll sie das beantworten. Das soll sie also
an der Schnittstelle von der Formel-Karte implementiert sein. Und das ist noch kürzer hier,
das ist diese Regel, die ihr auch irgendwo mal,
wenn ihr jetzt googelt, finden werdet, Tell down ask,
das ist also auch im Objekt und den Entwurf durchaus etwas,
was häufig verwendet wird, sagt dem Objekt, was es machen soll,
nicht frag es nach seinen Innereien und mach selber.
Also da auch wieder, immer wieder diese Grundregel,
wer sollte für eine bestimmte Aufgabe zuständig sein. Das heißt,
wir können statt das hier zu machen,
Student Get Record, Get Isammen, bla bla bla,
sag einfach Student, Scored und dann Markt Exam,
das heißt, ich reiche den Studenten irgendwie die Examens,
also nicht dem Studenten, dem Studenten Objekt hier,
die gesamten Ergebnisse rein und der Student,
Student holt sich die selbst da raus.
Ich sage einfach hier, das muss jetzt bei
dem Studenten eingetragen werden, wie das passiert,
interessiert mich nicht
Anderes Beispiel hier, ich frage nicht einen Satelliten nach seinen Antriebsdüsen.
Gib mir mal deine Antriebsdüse und jetzt steuere ich mal selber,
sondern ich sage dem Satelliten, wie er sich neu ausrichten soll.
Also das sind so verschiedene Ausprägungen von diesem Law auf dem Meter,
wo man dann einfach sagen kann,
das ist dann doch die etwas bessere Sicht auf die Objekte.
Auch Ghetter und Setter, klassische Beispiele, wenn man
so sehr datengetrieben seine Systeme baut. Ich glaube,
Heinz hat das auch in der Vorlesung ein bisschen erzählt,
so in den datengetriebenen Entwurf gegenüber dem verhaltensgetriebenen Entwurf
Ich einfach nur Datenstrukturen habe, dann sage ich einfach,
naja, so ein Objekt besteht da aus Referenzen und die
kann ich mit Ghettern und Zettern irgendwie setzen und auch rauslesen.
Und wenn ich jetzt irgendwas haben will, dann hole ich
mir das Ding raus. Das sind alles Operationen,
die häufig dann letztendlich gegen das Lau auf die Mieter verstoßen.
Nicht immer, aber häufig. Gut, dann jetzt das
andere große Thema Korrision. Korrision ist ein Begriff,
der in der Informatik zweckentfremdet wurde, also angelehnt an andere Interpretationen von Korrision.
Einmal hier in der Chemie und der Physik wir ja hier
im richtigen Ort, molekulare Kraft zwischen Molekülen eines Stoffes.
Sie bewirkt den inneren Zusammenhalt eines Stoffes, also tatsächlich so,
das passt irgendwie gut zusammen, hält gut zusammen.
Das ist Korrision und in den Linguistik gibt es eine Deutung,
die sagt, Verknüpfung von Textelementen zu einer sinnvollen Einheit auf der Oberfläche.
Also auch da durchaus dieser Gedanke,
es soll eine sinnvolle Einheit bilden und in der Objektorientierung und
im Allgemeinen im Softwareentwurf bezeichnen wir mit Korrision den Grad der Aufgaben,
die eine Softwareeinheit zu erfüllen hat.
Und Die Anzahl der Aufgaben, die eine Softwareanzahl zu erfüllen hat,
also wenn eine Softwareanhalt viele Aufgaben erfüllt und sehr viele verschiedene Aufgaben erfüllt,
dann ist die Korrision niedrig.
Also das muss man hier ein bisschen aufpassen. Also eigentlich
nicht ganz miteinander in Beziehung zu setzen. Hohe Korrision heißt einfach,
jedes Objekt sollte für möglichst genau eine Aufgabe zuständig sein.
Oder die Aufgaben, wenn es mehrere sind,
sind eben halt sehr eng inhaltlich miteinander verwandt.
Und das kann man unterscheiden für Methoden und für Klassen.
Klar. Also wenn wir jetzt auf dem Objektivsystem schauen,
dann gibt es sowas wie Korrision von Methoden.
Und das ist hier so ein Beispiel, das habe ich
tatsächlich auch schon mal irgendwo in einem System gesehen.
Da gab es Entwurfsregeln, die sagten,
eine Klasse darf nicht mehr als zehn Methoden haben.
Wir wollen möglichst kleine Klassen haben, möglichst kleine,
auch kleine Methoden, aber auch kleine Klassen haben.
Und deswegen dürfen die nicht so viele Methoden haben. Okay,
und dann wurde es aber irgendwie eng,
dann hat man hier irgendwie eine Methode,
eine Klasse gehabt,
die trigonometrische Funktionen anbieten sollte
Und dann wurde kurzerhand hier ein Ähnung gebaut, Sinhos Cosinos,
Tange ins Arkus-Tangents. Und dann gibt es hier eine Operation,
eine Operation Trigo, eine Methode mit niedriger Korision,
die einen dieser trigonometrischen Funktionen übergeben bekommt und dann diese Funktion anwendet auf dieses Argument.
Also berechnet dann den Sinos oder den Tangents von diesem Wert X.
Eine Methode für vier verschiedene mathematische Funktionen. Ist jetzt vielleicht
nicht so eine gute Idee, das zu machen,
weil es ist deutlich sinnvoller, tatsächlich Methoden zu haben,
die dann so heißen, wie das, was sie wirklich tun.
Also Sinus, Cosi, Nustangiens, Argus,
Tangis und nicht so ein Sammelbecken,
wo dann über einen Parameter das entsprechend entschieden wird.
Das heißt also, der Zwang, kleine Schnittstellen zu haben,
führt zu ganz komischen, kann zu ganz komischen Entwürfen führen.
Das ist keine gute Idee. Und das ist hier so ein Beispiel,
das ich auch aus einer Literaturbeispiel mal rausgeholt habe,
tatsächlich hier so eine Klasse Programm,
die irgendwie anscheinend ziemlich viele Dinge tut, die wenig miteinander zu tun haben.
Da hat also irgendjemand wahrscheinlich gesagt, ah, alles,
was wir nicht zuordnen können, wo wir keine Zuständigkeit haben im System,
schmeißen wir mal in diese Klasse rein, die macht dann alles Mögliche.
Inelige Commands, Deck, Push-Command, Shutdown, Inmysselize,
Report for Metting und so weiter.
Also irgendwie komisches Zeug. Da ist offensichtlich,
sind sehr viele verschiedene Dinge in eine Klasse reingeworfen worden.
Im Gegensatz zu einer Klasse wie hier, Film,
die modelliert Filme und hat bestimmte Eigenschaften,
also werden Titellänge und FSK und so weiter als Konstruktorparameter übergeben.
Und dann kann man sich die Dinge halt geben lassen.
Das ist eine Klasse mit hoher Korision, klare,
eindeutige Zuordnung, was sie denn tut.
Und das hier ist so ein Ding mit einer niedrigen Korision
Eine Ausprägung tatsächlich, wo man auch Korrision daran diskutieren kann,
ist die Sache mit den Bezeichnern.
Ganz allgemein gilt, man soll Klassen durch ein treffendes Substantiv benennen.
Das gilt nicht immer. Es gibt ja durchaus auch Interfaces
wie Comparable oder ähnliches. Aber im Allgemeinen also konkrete Klassen,
die wirklich instanzierbar sind, von denen man Exemplare erzeugen kann,
die sollten durch ein Treffen substantiv benannt sein.
Methoden eher durch einen Verb, also tue etwas,
das macht durchaus Sinn. Und was ganz interessant ist,
es gibt eben Sehr häufig in Systemen, sogenannte Unbezeichner,
also einfügen und berechnen oder Starter und Vermittler. Und genau,
wenn man solche Namen in seinem System drin hat,
könnt ihr euch mal irgendwie überlegen,
ob ihr an solchen Systemen schon gearbeitet habt,
dann ist das eigentlich ein Hinweis, auch eine niedrige Korrision.
Weil dann die eine Methode, zwei Dinge tut, dies und das.
Das kann gut sein, das kann sinnvoll sein,
aber man muss immerhin schon mal überlegen, ob das möglicherweise ein Hinweis ist auf niedrige Korrision.
Eine extreme Ausprägung von dieser Idee,
den Quelltext Die Bezeichner entsprechend zu wählen, ist tatsächlich,
dass man den Quelltext so schreibt, dass auch ein Kunde ihn lesen könnte.
Also wenn wir hier jetzt sowas haben wie Graf und Knoten,
also wenn ich mit einem Mathematiker darüber rede,
dann wäre es schon wahrscheinlich gut möglich,
dass er diese Operationen, die ich hier implementiere,
auf so einem Graf auch versteht und nachvollziehen kann,
weil sie einfach fachlich genau dazu passen.
Und er dann tatsächlich auch nachvollziehen kann, was ich da mache.
Wenn du jetzt in diesem Code hier auch noch Dinge reinkodiert während für die Darstellung,
also hier zum Beispiel diese Gui-Darstellung,
wenn das hier in der Graf- und Knotenklasse mit drin wäre,
tatsächlich jemand, der die Fachlichkeit gut versteht,
aber abgelenkt werden durch Sachen,
die er nicht verstehen muss und auch nicht verstehen soll.
Also man kann tatsächlich die Fachlichkeit so isolieren in den eigenen Klassen,
dass man dann quasi Quelltext schreiben kann, den ein Kunde verstehen kann.
Wir haben das tatsächlich auch schon in Projekten gehabt.
Bei einer Versicherung zum Beispiel,
wo dann jemand aus einer Fachabteilung wirklich relativ nah mit dem
Programmierteam zusammengearbeitet hat und weite Teile des Quelltextes gut lesen konnte.
Oh ja, eine der schönen Fragen,
die ich auch tatsächlich schon öfter gefragt worden bin.
Also ich habe auf diversen Konferenzen ja schon mit Leuten über Software Techniklehre diskutiert.
Und eine Frage, die gerne gestellt wird, ist,
wie ist das denn mit, wie macht ihr das,
wenn ihr euren Studenten erzählt, wie die Bezeichner gewählt werden sollten?
Machen wir das mit Englischen? Machen wir es mit deutschen Begriffen?
Und die Antworten sind dann möglicherweise, ja, sehr viel cooler,
Englisch, klingt viel besser oder wir sind an der Uni,
selbstverständlich, alles Englisch oder Java ist ja auch Englisch.
Java liest sich ja eigentlich schon wie Englisch,
also sollten auch die Bezeichner alle auf Englisch sein.
Und es ist natürlich auch viel internationaler.
So, das finde ich tatsächlich gar nicht unbedingt
so einfach zu beantworten
Meine Antwort darauf ist, ob ich meine Klassennamen und meine Methoden namen,
Englisch oder Deutsch wähle, ist komplett kontextabhängig.
Das heißt, es sollte in jedem Projekt neu entschieden werden.
Eine Entscheidungshilfe ist ganz einfach, wenn ich mich daran halte,
dass ich meine fachlichen und meine technischen Klassen trenne,
dann kann ich die auch unterschiedlich benennen.
Und dann ist es einfach davon abhängig, welche Fachlichkeit ich denn habe.
Ich würde immer die technischen Klassen in Java auch mit englischen
Begriffen benennen. Also ein Button ist ein Button,
da brauche ich nicht jetzt irgendwie Schaltfläche zu sagen.
Das ist albern. Wenn ich alles übersetze, alles,
was Englisch ist ins deutsche Übersetzer. Aber wenn ich bei
einer Versicherung sitze und mit denen irgendwie über Versicherungsverträge,
über Laufzeiten und über was weiß ich was diskutiere,
dann sollten diese Begriffe nicht künstlich ins Englische übersetzt werden,
damit sie dann in meiner Software auch alles Englisch sein kann.
Sondern tatsächlich wenig in einem deutschen Kontext arbeite,
dann sollte ich auch den fachlichen Begriffe Deutsch wählen.
Das ist durchaus sinnvoll. Und genauso sinnvoll ist es
dann natürlich in einem Uni-Projekt über Robotik, international,
alles Englisch zu machen
Natürlich dann die Leute, die da dran arbeiten,
entsprechend auch international. Die international gelesene Sprache ist natürlich Englisch.
Also auch wenn ihr an einem Open-Source-Projekt arbeitet,
solltet ihr eure Klassen entsprechend Englisch wählen.
Aber es ist nicht falsch, deutsche Begriffe in einem Java-System zu haben.
Weder für Klassen noch für Methoden. Es ist sogar dann
noch eine Chance. Also man kann ja jetzt sagen,
na gut, das sieht ja ziemlich hässlich aus,
da sind ja deutsche und englische Begriffe gemischt.
Kommt also zu so einem Flickenteppich hier. Aber ich finde das
gar nicht so schlecht. Also wenn man tatsächlich dann sauber erkennen kann,
welcher Anteil ist jetzt Fachlichkeit und welcher Anteil ist Technik.
Also hier in der Mediathek haben wir mehrere solche Beispiele.
Wir haben hier zum Beispiel Bearbeiten-Button. Den gibt es tatsächlich
. So, Bearbeiten, das ist das Fachliche,
es geht um das Bearbeiten von irgendwelchen Materialien.
Und Button, ja, das ist eine Schaltfläche.
Oder Get Medienart. Wir haben ja jetzt nicht gesagt,
gib Medienart, weil das Get ist tatsächlich Konvention in Java,
ein Getter wird mit Get eingeleitet und Medienart ist aber das Fachliche,
was da ausgelesen wird.
Das ist eben halt
entsprechend deutsch modelliert
Und Rückgabe Goui ist genau das Gleiche. Rückgabe UI,
meine ich. Hier, Rückgabe, da geht es fachlich
um die Rückgabe von einem Medium, was ausgeliehen ist.
Und UI ist einfach nur eine User-Interface, also ein
technischer Begriff für, ich muss da eine Goui bauen.
Also das macht durchaus Sinn, Deutsch und Englisch,
in einem System zu mischen. Noch eine Konkretisierung,
die Bezeichner, die wir jetzt ja schon eine Weile diskutieren.
Das hat der gute Herr Puh hier beschrieben in einem Buch,
das auch lesenswert ist. Prefectoring,
hat sozusagen so eine Gegenthese zu der ganzen agilen Bewegung,
also so möglichst schnell Code erzeugen und möglichst schnell programmieren.
Der hat halt gesagt, man kann auch vorab einen Entwurf sehr genau machen.
Und es ist gut beim Entwerfen eines Systems,
bevor man es implementiert, sehr genau hinzugucken.
Also sehr genau schon die Begriffe zu differenzieren.
Ihr merkt ja auch, dass wir hier in SE1,
in SE2 sehr viel Wert auf Begriffe legen.
Und auch wenn ihr objektorientiert modelliert,
dann solltet ihr relativ schnell die Dinge,
die da modelliert werden sollen,
richtig benennen,
passend benennen
Und das kann man gar nicht genau genug tun.
Das ist zumindest die These, die der Herr Puh hat.
Er sagt nämlich, Verschmelzen ist leichter als Aufteiler.
Ich will es an einem Beispiel mal verdeutlichen. Wir haben also wieder hier
so eine Software für einen CD-Verleih, also ähnlich wie unsere Mediathek.
Und jetzt fangen wir relativ schnell an, was zu bauen und sagen,
wir machen einfach eine zentrale Abstraktion, ist die Klasse CD.
Und die wird jetzt gebaut, klasse CD wird implementiert,
wird überall benutzt. Jetzt stellen wir irgendwann fest,
als wir das System weiter ausbauen, oh Mensch,
wir haben eine Sache nicht so richtig berücksichtigt
Eine CD ist erstmal ein eigener Herr. Übrigens,
das ist übrigens, früher war das so,
dass CDs noch so Rohlinge waren, die man dann noch verleihen konnte.
Und da gab es dann eben mehrere Exemplare davon.
Das ist heutzutage natürlich alles überhaupt nicht mehr relevant,
aber da war das noch so,
dass man tatsächlich von einer CD,
von einem bestimmten Album eines Künstlers mehrere hatte und die konnte man dann an verschiedene Personen ausleihen.
Da musste man natürlich jetzt irgendwie zum einen die CD selbst benennen,
also das Album und dann die konkrete CD, die da verliehen werden kann.
Beide zu D zu nennen, ist relativ unglücklich. Das merkt man aber erst,
wenn man so ein System dann baut und dann so,
hätte man von vornherein diese Modellierungsüberlegung angestellt,
also das war jetzt nicht so schwer,
das vorab zu sehen
Hätte die verschieden benannt und hätte dann irgendwann festgestellt,
ach, eigentlich brauchen wir die Differenzierung gar nicht.
Wir haben gar nicht mehrere Exemplare von einem Ding.
Dann kann man die tatsächlich automatisiert in einem Software-System einfach den
einen in den anderen umbenennen. Dann werden die verschmolzen.
Verschmelzen ist total einfach. Zwei verschiedene Dinge zusammenzuschmeißen ist leicht.
Aber wenn ich jetzt 30 oder 500 Verwendungen des Typs CD
habe in dem System und jetzt jede Stelle angucken muss,
ist da jetzt die CD gemeint oder ist da diese andere CD gemeint?
Das ist deutlich aufwendiger. Also nachträglich so ein Aufsplitten eines Begriffs
ist schwieriger. Also das ist ein gutes Argument dafür,
sehr genau seinen Entwurf schon vorab zu machen
Gut, Code-Duplizierung. Ja, das ist sozusagen die Lieblingspest des Software-Technikers.
Das ist so ziemlich das Unangenehmste, was man so haben kann.
Ein Stück Quelltext ist in identischer Form an mehreren Stellen in
einem System vorhanden. Das gibt es von ganz klein,
also Einzeiler bis hin zu ganze Kotabschnitte.
Da gibt es auch wieder ganz viel Tool-Unterstützung dafür,
die also wirklich Software, die nichts weitermacht als Quelltexte durchzuforsten auf Code-Duplikate.
Und dann zu sagen, hier, diese fünf Zeilen,
die sind da und da nochmal zu sehen
Gute Plizierung ist problematisch. Zum einen, weil dann Dinge
implizit gekoppelt sind. Wenn ich also tatsächlich jetzt fünf Code-Zeilen habe,
die dann woanders hinkopiere und dann nochmal woanders hinkopiere,
dann habe ich drei Stellen, an denen ähnlicher Code steht.
Oder erstmal der gleiche Code. Dann stelle ich irgendwann fest
an einer Stelle, oh, das ist nicht ganz richtig,
das muss sich ändern, dann wird das hier geändert.
Aber der Mensch, der das jetzt hier ändert,
der weiß ja nicht, dass dasselbe nochmal da und dahin kopiert worden ist.
Da steht da nicht drin. Die Kopplung ist ja
nicht explizit gemacht worden, sondern die ist implizit.
Die sind einfach nur ähnliche Quelltextanfälle. Das heißt,
möglicherweise muss dieser Fehler oder diese Änderung,
die hier gemacht wurde, den beiden Stellen gemacht werden,
aber man weiß es ja nicht, dass die Stellen da sind.
Also implizite Kopplung. Und es ist niedrige Korhision,
weil nämlich tatsächlich, wenn ich an mehreren Stellen das Gleiche tue,
dann ist mindestens an einer von den Stellen die Zuständigkeit falsch zugeordnet.
Das sollte eigentlich anders machbar sein. Gut,
große Einheiten vermeiden, dass es wieder eine von den Entwurfsrägen,
wo man wieder sieht, ich habe gerade gesagt,
so eine harte Regel, Klasse sollte nicht mehr als zehn Methoden haben,
ist Quatsch, aber generell ist es schon eine gute Idee,
Softwareeinheiten nicht zu groß werden zu lassen
Ein bekanntes Anti-Pettern, die Gott-Klasse. Also eine Klasse,
die alles macht und 90 Prozent des Quelltextes enthält.
Habe ich alles schon gesehen. Auch so diese Beispiele,
ich habe eine statische Main-Methode und die ruft dann nur noch andere statische Methoden auf.
Alles sozusagen imperativ implementiert, kann man natürlich in Java alles machen.
Und Methoden, die mehrere Bildschirmseiten lang sind, sind schlecht wartbar.
Auch das ist eine relativ einfache Grundregel, versucht einfach eine Methode,
höchstens eine Bildschirmseite groß werden zu lassen. Es gibt Leute,
die sind Puristen, die sagen, höchstens fünf Zeilen,
also höchstens fünf Zeilen in einer Methode, ist ein bisschen übertrieben.
Aber sobald ich tatsächlich eine Methode nicht mehr auf den Bildschirm bekomme,
kann ich zum Beispiel nicht mehr erkennen,
ob irgendwo ein Return-Statement steht,
was ich nicht mehr direkt sofort sehen kann.
Also mehrere Ausstiege sind aus der Methode und damit kann ich
sie schlecht lesen. Klar, Klassen,
mehrere hundert Methoden oder Exemplar bei Jan sind meist zu groß.
Auch das immer wieder, wenn einzelne Einheiten zu groß sind,
ist das ein Zeichen von niedriger Korrision.
Eine Einheit macht sehr viele Aufgaben, dann erfüllt sie wahrscheinlich zu viele.
Gut, das war jetzt hier so ein kurzer Überblick,
den ich gegeben habe über die zentralen Entwurfsregeln,
die es für objektorientierte Systeme gibt
Es gibt noch eine sehr empfehlenswerte Quelle von ähnlichen Prinzipien,
die letztendlich aber alle im Kern auf niedriger Kopplung und hoher Korrision basieren.
Hier die sogenannten Solid-Prinzipien von Onkel Bob, von Robert Ziematen,
wenn ihr die mal im Internet euch angucken wollt
, da werdet ihr nochmal das eine oder andere wiedererkennen,
aber auch nochmal so ein paar andere Anregungen bekommen,
wie man denn OO-Systeme auch gut gestalten kann.
Okay, also innere Qualität eines Software-Systems,
bestimmt Wartbarkeit und Portierbarkeit
Die wird, die innere Qualität wird maßgeblich durch den Klassenentwurf
bestimmt und man kann eben halt ein System,
Kraut und Rühm entwerfen oder eben halt sich an bestimmte Richtlinien,
an bestimmte Richtlinien halten.
Auf der groben Linie solltet ihr den Kopf behalten,
hohe Kollision und niedrige Kopplung.
Wenn ihr das einigermaßen berücksichtigt, dann kommt ihr schon relativ weit.
Okay, ich habe hier noch, ach ja,
ein bisschen Sturési. Falls ihr noch in der Stimmung seid,
auf eine kleine Abstimmung.
SE 2 Vorlesungen,
Ja. Ich hoffe, ich bin hier noch im Internet.
Warum müsste eigentlich klappen? Mal ganz kurz gucken. Ah
, doch, sieht gut aus. Musst du gehen,
mal ganz kurz gucken, kann ich mal eben hier gucken,
ob ich hier irgendwo hinkomme?
Ja, also, das funktioniert. Also, das ist
die Frage. Ich habe hier vier Aussagen zum Thema
Objekte der Entwurf. Und welche der Aussagen ist die
am wenigsten falsche
Die erste Aussage ist, fünf Klassen,
die sich alle gegenseitig benutzen, sind sehr lose gekoppelt.
Aussage B ist, je weniger Methoden eine Klasse enthält,
desto besser ist sie strukturiert. Aussage C,
durch das Einführen von Interface,
das kann die Kopplung in einem System reduziert werden und Aussage D,
ein vernünftiger Entwurf ist nur mit Hilfe von UML-Klassendiagramm möglich.
Ich kann auch nochmal hier das Ding zeigen,
wenn das noch gebrauchen kann
Ja, man sollte mal sehen,
wie viele Stimmen wir heute zusammen bekommen.
Es sind auf alle Fälle mehr als zwölf Leute da.
Also von daher. Ja, ich glaube so 20 Stimmen
wären schon schön,
wenn wir die zusammen bekommen
Nimmt euch ein Herz, gebt eure Stimme ab.
Ja, seht ihr. Gut, das ist ja auch
noch eine relativ einfache Frage. Das ist ja total einfach
. Ich wette mal, also meine Erwartungshaltung ist jetzt,
dass ihr euch ziemlich schnell geeinigt habt auf eine Antwort hier, ja.
Genau, das ist so der Klassiker. 80 Prozent sind
natürlich dann, und das ist auch natürlich richtig,
das ist meiner Meinung nach, die am wenigsten falsche Aussage.
Durch das Einführen von Interface, das kann die Kopplung
reduziert werden. Es ist natürlich auch richtig,
dass je weniger Methode eine Klasse ist,
desto besser sie strukturiert ist,
aber das ist eben halt nicht immer richtig.
Da muss man
ein bisschen aufpassen
So, dann gucken, oh, diese Maus,
die macht mich wahnsinnig. Okay, das ist eher so
eine Diskussionsfrage, nur mit 50 Prozent Antwort hier, Möglichkeit.
Angenommen, wir haben das Beobachtermuster an einem System mit
einem Interface in einer abstrakten Klasse selbst gebaut.
Das habt ihr jetzt gerade, glaube ich,
in den Übungen gemacht. Und an verschiedenen Stellen eingesetzt.
Wie sollten denn die beiden Typen heißen? Beobachter und beobachtbar
oder ob Server und Observible? Okay, ich beleidige
eure Intelligenz,
ich weiß
Ich sehe da schon ganz viele genervte Gesichter. Obwohl,
im letzten Jahr war das durchaus eine gespaltene Meinung dazu.
Also das haben wir hier 22 Stimmen zusammen. 24,
wow, es geht jetzt richtig hier, oh,
es geht 25, Wahnsinn.
Also fällt das mal an. Guck mal, auf die Verteilung
seht ihr. Es ist tatsächlich gar nicht so klar.
60 zu 40, Beobachter und Beobachtbar oder ob Server und Observable
Na gut, also meine Antwort ist ja das hier.
Ob Server und Observe will. Warum das denn, warum
das denn? Ich kann es erklären, nach den,
also konsistent mit dem, was ich gerade erzählt habe,
ob Server und Observible, sind hier Englisch benannt,
weil das eine technische Eigenschaft ist.
Es geht einfach nur um das technische Objekte sollen sich gegenseitig beobachten.
Es hat mit der Fachlichkeit relativ wenig zu tun.
Also dass ich Software-Elemente, dass die reagieren können,
wenn andere Software-Elemente sich ändern, das ist komplette Technik,
die man braucht,
so ähnlich
wie ein Google-Framework
Und Beobachter, Beobachter würde nur Sinn machen,
wenn man tatsächlich, wenn es um fachliche Beobachtungen ginge,
die man in der Natur vornimmt oder ähnliches.
Natürlich kann man die trotzdem so nennen, es ist nicht falsch,
aber ich würde das eben halt eher Englisch machen,
um deutlich zu machen, dass es um eine technische Beziehung geht.
Nicht um eine fachliche Beziehung.
Juhu, es ist viertel vor. Vielen Dank für
eure Aufmerksamkeit