Ja, das sieht gut aus. Ihr seid soweit mit der Aufnahme.
Alles gut. Prima. Okay, herzlich willkommen zur zehnten
Vorlesung von SE2. Ich mache heute mal keine größeren Ansagen vorneweg.
Was machen wir heute? Wir schauen uns das Thema Klassenentwurf an. Das heißt,
wir schauen einmal, was es so für Erfahrungen gibt,
wie man Klassen in objektorientierten Systemen entwerfen sollte.
Da gibt es einiges. Das ist ein relativ umfangreiches Thema,
Objekte unter der Entwurf ist wohl mit das Anspruchsvollste,
was ist im Entwurf von Software so gibt.
Und wir werden hier uns auf so ein paar elementare Dinge beschränken.
Warum ich das mache? Kann man sicher fragen.
Und ich wiederhole dann einfach nochmal das,
was ich schon vor ein paar Wochen gesagt habe.
Wir wollen euch natürlich nicht nur beibringen, wie man Software schreibt,
also die irgendwie läuft und irgendwie das macht, was sie machen soll.
Oh, Entschuldigung. Ja, ich weiß schon.
Ich habe schon wieder hier das So, ha,
danke für den Hinweis. Also, Übersicht hier. Softwarequalität gleich,
als Motivation und dann Klassenentwurf und dann werden wir da so
ein bisschen draufschauen, was heute primär dran ist.
Zwei wichtige Begriffe, Kopplung und Korision heute. Gut. Also,
es geht nicht nur darum,
dass man Software zum Laufen kriegt, dass sie das macht,
was sie machen soll und das reicht uns für den Moment,
sondern es geht auch darum, Software so zu schreiben,
dass sie über einen längeren Zeitraum am Laufen gehalten werden kann.
Und wir fokussieren jetzt hier beim Entwurfsthema stärker auch die interne Qualität,
also darauf, wie verständlich,
wie wardbar,
wie modular ist der Quelltext aufgebaut
Klar ist das in der Benutzung nach außen hin nur das Wichtige ist,
was an äußeren Qualitäten vorhanden ist,
also wie schnell ist die Software wie zuverlässig,
auch die Funktionalität selbst natürlich, das ist das,
was für den Benutzer wichtig ist.
Wir sind aber ja hier eher Softwareentwickler, das heißt,
wir stehen auf der anderen Seite,
wir konsumieren keine Software, sondern wir produzieren Software.
Und da ist es schon wichtig, ein bisschen mehr zu
wissen über die guten Regeln, wie man Software entwirft.
Auch da könnte man jetzt sagen, gut,
vielleicht einige von euch wollen nicht Informatiker werden, wollen nicht Programmierer werden,
sondern haben halt Informatiken nur so Teilgebiet im Studium gewählt.
Und dann kann man jetzt sagen, müssen ja nicht alle lernen,
was wir hier so vermitteln. Und dann finde ich halt
immer wieder diese Folie sehr schön plakativ, die ich auch schon gezeigt habe.
Natürlich kann man Software auch einfach schreiben und Software zu schreiben,
kann einfach sein, aber nur in sehr,
sehr begrenzten Zusammenhängen, nämlich man wirklich was Kleines schreibt,
was nur eine Person benutzt, nur die eine Person schreibt,
die es auch nur einmal benutzt und dann wegschmeißt.
Jede andere Form von Software ist irgendwie dann doch relativ schwierig und das wird gerne unterschätzt. Also so oft kommt das vor,
dass man in Projekten Leute trifft,
die oder auch wenn man mit Leuten spricht, die Software entwickelt,
die sagen, ja, wir haben hier in der Physik,
haben wir irgendwie so unsere Software und das ach,
das fällt uns langsam auf die Füße,
das ist alles ein bisschen, ach,
nicht so toll.
Und je mehr wir das ausgebaut haben,
desto wilder wurde der Entwurf und so weiter.
Also eigentlich ist es für jede Form von Softwareentwicklung,
die nicht sofort weggeschmissen werden soll, relevant,
was wir uns jetzt hier ansehen.
Natürlich fokussiert auf objektionierte Software. Also es gibt sicherlich
auch einen Riesensatz an Entwurfsrichtlinien für emperative Software.
Das ist bei der Objektontierung
ein bisschen fortgeschritten
Der Entholzrichtlinien für funktionale Software, also für Software infunktionalen Fragen.
Wir fokussieren hier auf Objektorientierung. Das heißt, wir wollen
uns jetzt ansehen, wie wir die Qualität von Klassenentwürfen erhöhen können.
Und an der Stelle finde ich es immer ganz ergänzend,
ganz gut, wenn man ein bisschen differenziert.
Nämlich, was ist Entwurf überhaupt? Was heißt überhaupt Entwurf?
Und man kann das eigentlich so auf mindestens zwei Arten verstehen. Einmal,
das ist alles da, es besteht auf 100 Klassen boah,
der Entwurf sieht mir eigentlich ganz gut aus.
Das hat scheint eine ganz gute Architektur zu haben.
Entwurf in dem Sinne ist auch häufig Synonym verwendet zur Architektur.
Wie ist die Architektur des Systems? Wie ist die bestehende
Struktur? Welche Elemente existieren und wie arbeiten die zusammen?
Welche Regeln gibt es für das Zusammenspiel der Elemente?
Beispielsweise unsere SE2-Entwurfsregeln. Und dann gibt es aber natürlich auch
noch den Entwurf als Tätigkeit. Ich bin dabei,
ein Softwaresystem zu schreiben oder wir im Team und stellen fest,
dass wir an einer bestimmten Stelle eine neue Funktionalität einbauen sollen und stellen jetzt fest,
oh, Es ist jetzt nicht so ganz klar,
wie viele Klassen kommen da jetzt dazu?
Welche Klasse soll was machen? Das heißt,
die Aufteilung ist möglicherweise nicht so einfach. Das heißt,
beim Entwerfen, also bei der Tätigkeit,
da kommt immer auch so eine Planungskomponente mit rein.
Also man muss sich irgendwie überlegen, was man als erstes macht.
Und das muss man auch irgendwie begründen, warum man bestimmte
Dinge zuerst macht und bestimmte Dinge später macht. Häufig ist das so,
dass dann Leute, die so ein bisschen hobbymäßig Software basteln,
sich zuerst mit den grafischen Oberflächen beschäftigen und sehen, oh toll,
man kann da irgendwelche Buttons klicken und so, alles super.
Und dann möglicherweise die Fachlichkeit, die eigentlich abgebildet werden soll,
gar nicht so spannend finden und dann bauen sie irgendwie mit in die Gui mit rein.
Das führt dann zu solchen verwurbelten Guis,
wo dann Funktionalität einfach in den Gui-Komponenten verwurstelt ist.
Also diese Planungskomponente werden wir auch nochmal gleich ansprechen und dann ist es natürlich auch so,
wenn man entwirft, das habt ihr jetzt teilweise schon gemacht,
wir haben das bisher eigentlich nur im Kleinen mit euch gemacht,
das heißt eure, die Anforderungen an euch etwas zu entwerfen waren,
kleine Aufgaben nur.
Wenn man etwas größer wird, dann ist es auch spannend,
dann CRC-Karten einzusetzen, also tatsächlich diese Idee von die nach fünf Karten,
auf die man irgendwie den Namen einer Klasse schreibt und sagt,
was hat die wohl so für Zuständigkeiten?
Was soll die denn so leisten im System? Und mit wem
arbeitet ihr denn so zusammen? Responsibilities und Collaborations. Diese Karten,
die man dann so schön handschriftlich irgendwie schreiben kann,
sich an den Tisch setzen mit mehreren Personen,
kann diese Karten hin und her schieben, kann auch Rollenspiel damit machen.
Das heißt, man kann wirklich sagen,
jeder spielt eine Klasse und dann macht man mal so einen Walksthrough,
also so ein bisschen so mir mal an,
da klickt jemand auf der Oberfläche, welche Klasse wird als erster aufgerufen und so.
Und dann kann man das so durchspielen in einer Gruppe.
Extrem nützlich, um objektnotierten Entwurf gut zu verstehen
Und vor allen Dingen auch, um dann festzustellen,
wer denn für eine bestimmte Funktionalität zuständig sein sollte.
Ja, und jetzt ist natürlich die Frage,
wenn wir einen guten Entwurf anstreben,
also eine gute Struktur von Software, was müssen wir dabei beachten?
Und da gibt es eben halt, wie gesagt,
eine ganze Menge Erfahrung, ganze Menge Entwurfsregeln,
sind schon dokumentiert in der Literatur.
Und ich will hier so einen kleinen Überblick geben.
Ich sehe hier so einen, der Kompass deutet an,
wir geben nur so die Windrichtung vor,
von wo
der Wind primär weht
Was im guten Objekt orientierten Design sich eigentlich durchgesetzt hat,
ist der Entwurf nach Zuständigkeiten.
Da sage ich gleich noch mehr zu. Und dann zwei
sehr zentrale Begriffe, nämlich lose Koppelung und hohe Kollision.
Und diese beiden abstrakten Uber-Entwurfskonzepte können sich sehr, sehr unterschiedlich ausprägen.
Das werden wir also auch hier sehen,
wie man die konkreter fassen kann in konkreteren Regeln.
Also Entwurfsregeln gibt es von sehr abstrakt bis sehr konkret.
Teilweise auch sehr konkret für eine bestimmte Programmiersprache.
Das ist hier weitgehend Programmiersprachen unabhängig,
was ich jetzt erzähle
Ja, okay. Und das ist, sagen wir das,
was ich eben schon sagte, das zentrale Konzept von guten objektorientierten Entwürfen.
Nämlich, dass man einen Entwurf macht nach Zuständigkeiten. Die Leute,
die das damals definiert haben, Ende der 80er, ich glaube,
dieses Original-Paper von der Rebecca Wilfsbrock ist 89 auf der Upsler erschienen.
Die haben das in der damaligen Zeit gegenübergestellt,
einem Ansatz, der damals vorherrschte,
wenn man Systeme baute,
dass man über die Daten
ein System definierte
Also wenn ihr so mit Datenbanken umgeht, dann werdet ihr
schnell feststellen, man macht da gerne so einen datengetriebenen Entwurf.
Welche Daten gibt es denn, wie hängen die zusammen?
Entity Relationship Diagramm, juhu. Und dann gibt es noch
so ein bisschen Funktionalität. Also die Funktionen, die auf
den Daten arbeiten. Das ist die klassische imperative Trennung,
Daten und die Funktionen, die auf den Daten arbeiten.
Das ist in der Objektorientierung anders. Das wisst ihr.
Wir versuchen ja irgendwie die Daten zu kapseln in Objekten,
in Klassen und dann diese Klassen und Objekte irgendwie interagieren zu lassen in einem sinnvollen Zusammenspiel.
Und nach welchen Kriterien macht man den Entwurf?
Nicht datengetrieben, sondern Responsibility driven.
Also nach Zuständigkeiten
Und die Idee ist hier so ein bisschen auf den Punkt
gebracht, das ist natürlich alles ein bisschen mehr.
Da gibt es ganze Bücher drüber, aber Objects are not,
not just simple bundles of lagic and data,
they are responsible members of an object community.
Das heißt, man versucht eigentlich immer, sich zu überlegen,
wofür ist denn jedes Element im Softwaresystem zuständig.
Und das sollte man auch gut beantworten können. Also es
sollte für jedes Objekt klar sein, wofür es zuständig ist.
Wenn man das nicht gut benennen kann, vielleicht in einem kurzen Satz,
dann ist schon ein bisschen was am Entwurf komisch.
Ich auch noch gleich ein bisschen mehr drauf ein
Ist natürlich in der noch abstrakteren Grundidee auch nichts Neues.
Das ist also schon der gute Darkstra,
hat in den 60ern gesagt, hier, Separation of Concerns.
Also irgendwie Teile, die Zuständigkeiten sinnvoll auf in einem System.
Nur die Frage, nach welchen Kriterien man das macht,
das ist eben halt durchaus unterschiedlich interpretiert worden.
Wenn man sich an den Daten orientiert, kann man zu
einem Entwurf kommen. Das hat auch Guido, glaube ich,
vor ein paar Wochen schon erzählt, bei dem Bibliotheksbeispiel.
Wenn man sich an dem Verhalten orientiert,
kommt man zu einem objektantierten Entwurf.
Oder zu einem zuständigkeitsgetriebenen Entwurf. Und weil das so herrlich
abstrakt ist,
will ich mal
ein konkretes Beispiel bringen
Das hier ist ein Blu-Jay-Projekt, das ihr eventuell kennt, wahrscheinlich nicht.
Das ist das Grafenprojekt aus SE1. Das war Blatt 40. Ihr erinnert euch,
dass ihr alle Blatt 14 bearbeitet habt,
weil es ja nicht Teil der Scheinbedingungen war.
Wir hatten früher 14 Übungswochen, da war das Blatt 14,
irgendwie noch mit in den Scheinbedingungen drin. Dann hatten wir irgendwann,
wurden wir geschoben mit der Vorlesung von Montag auf Mittwoch,
dann konnten wir nur noch so eine halbe Woche machen.
Also mussten, haben wir vor und hin eine halbe Woche
verloren, also insgesamt haben wir nur noch 13 Übungswochen.
Deswegen gibt es auch noch 13 Übungsblätter in SE1.
Und dieses wunderbare Blatt, dieses wunderbare Aufgaben. Und dieses
wunderbare Projekt, die sind dann so ein bisschen hinten übergefallen.
Diejenigen von euch, die das mal bearbeitet haben, die kennen das vielleicht schon.
Ich zeige es erstmal nur, es geht tatsächlich darum,
so ein bisschen Grafen zu thematisieren und Algorithmen auf Grafen.
Ich habe also ja auch in der letzten Vorlesung von SE1
ein bisschen was über Grafen erzählt und ihr habt auch in
der diskreten Mathematik durchaus.
Können die Tür bitte zumachen? Danke. In der
diskreten Mathematik habt ihr ja euch auch schon mit Grafen beschäftigt
Ich zeige erst mal, wie das ganze System hier läuft.
Ich starte einfach mal diese Mengen-Methode, die hier drin steckt.
Übrigens nicht wundern, dieses Blue Jay,
was ich hier verwende, ein bisschen gepatcht.
Das werde ich gleich auch noch mal erläutern. Nicht,
dass ihr euch wundert, warum hier zum Beispiel dieses Ding weiß ist.
Das hat schon eine gewisse Bewandtnis. So, das ist
einfach die grafische Benutzungsoberfläche. Man sieht hier irgendwie so ein
Graf angedeutet. Das ist ein gewichteter, ungerichteter Graf.
Also die Kanten haben hier keine Pfeile dran. Das heißt,
sie sind irgendwie ungerichtet und sie sind gewichtet. Das heißt,
es gibt sowas wie Kantengewicht, das heißt also,
ich kann mir das vorstellen wie so ein Netz für Verbindung zwischen Städten oder ähnliches.
Also hier ist dann das Gewicht dran, wie teuer es ist
von dem roten Knoten, zu dem blauen Knoten zu kommen.
So, und das ist hier so ein bisschen nett animiert.
Also man sieht hier,
das ist hier so ganz hübsch und man kann hier irgendwie Dinge tun,
man kann auch neue Knoten hinzufügen und man kann auch irgendwie hier Knoten miteinander verbinden.
Dann sage ich hier irgendwie, klick, klick und dann
werden hier so. Also das ist ganz hübsch.
Und man sieht hier, das ist eine grafische Benutzungsoberfläche.
Die habe ich damals gebaut, damit irgendwie dieses Thema Grafen ein bisschen anschaulich
Für SE1. Aber das ist eigentlich gar nicht so wichtig,
weil viel wichtiger ist ja eigentlich für uns jetzt hier der Entwurf.
So, und was hier gebaut ist in diesem System ist,
ihr seht hier dieses Hauptfenster, das ist mehr oder weniger die Gree-Komponente,
jetzt alles weggekapselt, was da so an Komplexität dahinter steckt.
Also man sieht dann nur diese eine Klasse, die irgendwie anscheinend den Graf benutzt.
Ja. Den visualisiert. Okay, und dann ist ein Graf,
ja, kann man sich angucken, relativ einfach implementiert,
diese Klasse modelliert, gewichtete, ungerettete Grafen,
über die Schnittstelle können Knoten und gewichtete Kanten in einen Grafen eingefügt werden.
So, und was ganz nett ist,
ein Graf wird angesehen als eine Menge von Knoten.
Macht ja Sinn, ne? Und was wir hier
auch sehen, ist,
dass dieses
Ich kann mit einer erweiterten Vorschleife über alle Knoten des Grafen iterieren.
Das ist ganz elegant. Das ist ganz schön gemacht. So,
und dann habe ich hier einen Konstruktor und dann kann ich einen neuen Knoten hinzufügen,
dann wird hier der Knotenmenge einfach dieser Knoten hinzugefügt,
kann den Knoten wieder entfernen und so weiter.
Und ich kann auch direkt verbinden, also das ist was,
was ich, wenn ich in der GUI gerade dieses,
diesen Klick gemacht habe und diese Verbindung eingefügt habe,
dann ist unter der Haube ist natürlich dieses Verbinde direkt zwischen
zwei Knoten aufgerufen worden.
So, und dann kann man fragen,
ob der Knoten der Grafen
bestimmt Knoten ändert
Man kann die Anzahl der Knoten abfragen. Das ist also
alles möglich. Tatsächlich ist hier auch schon einiges davon implementiert.
Also ich kann zum Beispiel jetzt hier mir diesen visualisierten Graf geben lassen.
Das ist jetzt, das ist jetzt meine Java-Referenz auf den Graf,
der jetzt hier gerade visualisiert wird. Da ist er.
So, und da kann ich jetzt zum Beispiel fragen,
wie viele Knoten hast du denn, Anzahl Knoten?
Und dann sagt er mir, es sind elf,
elf Knoten, wir zählen nach.
Da sieht man schon, das ist was,
was man mit draufschauen nicht mehr so direkt sieht,
wird es jetzt noch mehr werden, sowieso nicht mehr.
Klar,
aber es sind wohl ungefähr elf Knoten hier in diesem Grafen
Ja. So, und jetzt ist es so,
dass eine Aufgabe war in C1, dass man jetzt,
ja, ich meine, ich kann noch mal kurz den Knoten zeigen,
dass ihr ihn auch noch mal seht,
wie der so aussieht, vielleicht erst mal die Schnittstelle.
Also man kann irgendwie den Knoten erzeugen mit einer Nummer,
das ist relativ unwichtig, die wird gar nicht benutzt.
Und dann ist es auch hier so, es gibt ein Verbinde mit,
der ein kleines Knoten mit einem anderen Knoten,
da gibt man das Gewicht mit an und dann kann man
das Verbindungsgewicht zu einem anderen Toten abfragen.
Man kann fragen, ob ein Knoten eine Verbindung hat zu
einem anderen Knoten und so weiter. Also das ist
relativ einfach gestrickt. Auch das ist dieses Interface Bitterible.
Das heißt, bei einem einzelnen Knoten kann ich über seine Namen iterieren.
Also wenn ich jetzt mir tatsächlich die Harfen hier angucke,
dann hat dieser Knoten hier einen Nachbarn,
der Knoten hat zwei Nachbarn. Ich kann,
also wenn ich den Knoten nach seinen Nachbarn frage,
einfach über den Knoten, über den Nachbarn des Knoten iteriere,
kriege ich erst den gelben und erst dann den Raum oder umgekehrt.
Das ist eine Frage. Das Licht ausmachen, okay,
ich versuche mal hier das Licht dunkler zu machen auf
alle Fälle.
Falls das viel hilft
Ist das besser? Man kann das noch erkennen, sonst sagt Bescheid.
Sorry, dass das jetzt nicht gut zu erkennen war.
Also, wir haben jetzt hier diesen visualisierten Grafen und
ich kann ihn hier auch manipulieren.
Und es sollte jetzt als eine Aufgabe implementiert werden,
was ist der Grat dieses Grafen? So,
also wenn ich jetzt zum Beispiel diesen Grafen hier habe,
was ist denn der Grat eines Grafen?
Wer kann mir das mal eben kurz an den Kopf werfen?
Wie ist das definiert bei Grafen?
Weiß das jemand zufällig? Die Anzahl der Beziehungen,
nicht ganz,
Die Anzahl der Kanten, die bei einem Graf an
einen Knoten anstoßen, jein, auch nicht ganz richtig.
Der längste Weg im Knoten, noch jetzt wird es kreativ.
Okay, ich mache es kurz. Der Grad eines
einzelnen Knoten ist einfach die Anzahl seiner Nachbarn und der Grad
eines Grafen ist dann der maximale Grad seiner Knoten.
Also, wenn wir den Grad dieses Knoten abfragen,
dann ist der Grad zwei, weil der hat zwei Nachbarn,
der Grad dieses ist auch zwei.
Der Grad dieses Knoten ist 1, 2, 3,
4, 5, 6. Und deswegen ist der Grad des Grafen 6.
Das ist sozusagen der maximale Grad aller seiner Knoten
Okay. Das wollen wir jetzt implementieren. So,
und jetzt ist natürlich das eigentlich ganz einfach.
Ich kann jetzt hier mir diese,
ich kann mal eben erstmal das Ding beenden hier,
das brauchen wir erstmal nicht mehr.
Ich implementiere jetzt bei dem Graf hier diese Operation,
Anzahl Komponenten, Grad. Und der ist bisher implementiert,
liefert den Grad dieses Grafen, also den maximalen Grad seiner Knoten.
Wie kann ich das implementieren? Hat jemand eine Idee,
wie man das jetzt möglicherweise relativ leicht implementieren kann hier
Vater, Vater, Vater, Vater, Vater,
Vater, Vater, Vater, Vater, Vater, Vater,
die, den Grad jeweils sich merken und beziehungsweise sozusagen die gerade jenes Knotens ermitteln und dann sich den maximalen Grad merken.
Okay, das finde ich gut. Ich definiere mal
eine Variable Max Grad und die ist gleich null.
Und ich sage am Ende Return, Max Grad.
So, das ist ja schon mal ganz gut.
Jetzt will ich über alle Knoten in dem Grafen iterieren.
So, ganz kurz gucken, wie hier die
ganzen Knoten heißen
Die heißen Knotenmenge. Hier, Knotenmenge,
das ist die Menge aller Knoten in dem Grafen.
Also sage ich jetzt einfach hier, vor Knoten K in der Knotenmenge,
also für jeden Knoten K in der Knotenmenge, mache irgendwas.
Naja, was mache ich jetzt? Jetzt will ich
irgendwie hier in den Grad dieses Knoten ermitteln.
Also ich habe also jetzt für jeden Knoten K habe ich,
gehe ich erst mal davon,
dass er
keine Nachbarn hat
Wie ermittel ich jetzt von einem einzelnen Knoten, seine Nachbarn?
Gut, haben wir so ähnlich eben schon gehört.
Also ich muss jetzt einfach über alle seine Nachbarn iterieren.
Also sage ich jetzt einfach vor Knoten-Nachbar,
für jeden Nachbar von K. Ich habe ja jetzt hier
sozusagen meinen K aus der äußeren Schleife.
Und in der inneren Schleife iteriere ich jetzt überall den Nachbarn
dieses Knoten. Ja, und dann sage ich hier einfach
Grad ist gleich Grad. Machen wir so Grad Plus 1.
So,
ganz explizit
Das heißt, hinterher, an dieser Stelle habe ich
jetzt hier den Grad eines einzelnen Knoten ermittelt.
Und wenn dieser Grad jetzt, if Grad größer bisheriger Max Grad,
dann sagen wir einfach, Max Grad ist gleich Grad.
So, ups. Das müsste eigentlich funktionieren. Funktioniert das
so? Wer ist dafür, dass es so funktioniert?
Wer ist zuversichtlich, dass das so funktioniert?
Ja, die meisten schon,
die sich beteiligen
Okay, das heißt, ich kann das jetzt mal versuchen zu übersetzen.
Das lässt sich übersetzen, sehr schön. Jetzt können wir
das mal zumachen, ich kann das hier alles übersetzen.
Ich starte wieder das Main, damit ich erstmal wieder
einen Grafen habe. Da ist er. So,
und jetzt lasse ich mir wieder diesen visualisierten Grafen geben,
hole mir den hier auf die Objektleiste und kann jetzt hier
Grad aufrufen und es sollte jetzt hoffentlich sechs rauskommen.
Und es kommt sechs raus. Vorher kam da halt nur
null raus. Sehr schön, wir haben das implementiert.
Das war leicht, das war ein bisschen Algorithmik,
das war so ein bisschen,
da ging es halt so um Algorithmus
auf Grafen
Jetzt geht es um den Entwurf nach Zuständigkeiten.
Und wer hat jetzt vielleicht eine Anmerkung dazu,
was an dieser Implementation jetzt eigentlich unschön ist?
Was ist an dieser Implementierung eigentlich nicht schön?
Wer hat eine Idee? Ja. Aha, okay,
genau richtig. Er sagt, dass wir den Grad eines
einzelnen Knotens hier berechnet und nicht in der Knotenklasse.
Das heißt, was wir hier gemacht haben,
wir wollen hier den Grad eines Grafen ermitteln.
Klar, der Code um den Grad eines Grafens zu ermitteln,
soll genau in der Klasse Graf stehen.
Dafür ist sie zuständig. Aber der Grad eines einzelnen Knotens
ist hier eigentlich dieser ganze Graf
Vollknoten. Also einfach, dass ein einzelner Knoten seinen Grad
berechelt bekommt. Warum kriegt der Knoten seinen Grad berechnet?
Warum berechnet der Knoten das nicht selbst? Der Knoten ist
eigentlich damit zuständig. Den sollte man eigentlich danach fragen.
Was ist der Grad eines Knotens nachfragen oder den Knoten selber?
Statt dass wir irgendwie den Knoten irgendwie traversieren,
weil dieser Code, jetzt kommen ganz viele Dinge,
die ich im Laufe der Friseur noch erwähnen werde,
wenn ich diesen Code jetzt hier hinschreibe und ich schreibe ihn an einer anderen Stelle,
wo ich auch den Grad benutze,
aber für einen anderen Zweck
Nochmal, dann habe ich denselben Code an zwei Stellen stehen.
Eigentlich ist aber ja die klasse Knoten direkt dafür zuständig.
Die sollte eigentlich diese Operation anbieten. Das heißt,
wenn wir das jetzt richtig bauen würden,
würden wir in Knoten eine Operation Grad reinbauen.
Und dann würden wir hier nur noch für jeden Knoten den Grad
uns geben lassen und den mit dem Max Grad vergleichen.
Okay, das ist so ein ganz einfaches Beispiel für
diese Geschichte mit dem Entwurf nach Zuständigkeiten.
Ich habe ein etwas umfangreicheres Beispiel auch nochmal mir überlegt,
für die Vorlesung heute. Und zwar Ihr erinnert euch noch
an die Mediathek. Juhu, hier ist sie.
Hier sollte sie sein, muss ich mal eben starten.
Da immer noch nicht. Na gut, muss ich halt klicken.
Hier ist die Mediathek und ich zeige euch die Musterlösung für Platz 6.
Das ist hier Mediathek, Platz 6, Musterlösung.
Kann ich einfach mal ausführen und sieht ungefähr so aus
Ihr erinnert euch, das war diese Geschichte mit Ausleih an sich,
Rückgabe, Ansicht, Vormerke, Ansicht und so weiter.
Ihr solltet in dieses System ja diese Vormerkansicht so ausbauen,
dass sie funktioniert. Also, was steht hier auf der Folie?
Die Funkzeit, die das vormerken solltet ihr einbauen.
Die GUI hatte das schon drin. Die GUI war schon vorgegeben.
Ihr solltet eigentlich nur die Services und die Materialien erweitern.
In der GUI gibt es einen Button,
der nur dann aktiviert werden soll,
wenn alle ausgewählten Medien für den ausgewählten Kunden vormerkbar sind.
Also, das ist hier die Oberfläche.
Hier ist die Vormerkansicht. Ihr seht hier der Vormerken,
Button ist ausgegraut
Wenn ich jetzt hier auf Anna Conicover klicke und damit
einen Kunden ausgewählt habe, passiert hier noch nichts.
Jetzt sage ich hier Gartenstate soll vorgemerkt werden und
jetzt ist der Button aktiviert, ich kann ihn klicken.
Das heißt, in der GUI gibt es irgendwie einen Button und der wird aktiviert und deaktiviert.
Nach welchen Kriterien denn und wer ist dafür zuständig?
Das ist jetzt eine interessante Frage, weil wir,
wenn wir das System ein bisschen kennen und ihr habt es
hoffentlich noch so halbwegs in Erinnerung,
haben das ja euch so bauen lassen,
dass ihr in dem Vormerkwerkzeug irgendwie schon ein bisschen Code drin hattet.
Und dann sollte aufgerufen werden, ist Vormerken möglich.
Ja, und dieses ist Vormerken möglich.
Wer beantwortet das? Und das ist tatsächlich wieder genau dieser,
der ist für was zuständig. Jetzt könnte man sagen,
ganz einfach, es muss immer genau eine Klasse zuständig sein,
dann würde man das in eine Klasse rein programmieren.
Tatsächlich werden wir uns an den Entwurf ansehen,
wie er gebaut ist und werden sehen,
dass es tatsächlich über verschiedene Stellen verteilt ist,
wie das beantwortet wird, ob Vormerken möglich ist.
Also ich zeige hier mal diese entsprechende Darstellung.
Also wir haben die Musterlösung hier,
wir haben unsere Pakete und haben hier die ganzen Werkzeuge.
Und hier gibt es das Vormerken, hier Hauptwerkzeug und dann gibt es hier das Vormerkwerkzeug.
Und in diesem Vormerkwerkzeug, das ist hier ja schon offen,
toll, habe ich ja wieder schon gut vorbereitet.
Da gibt es diese Operation Bulien ist vormerken möglich.
Die wird von der GUI benutzt, mehr oder weniger,
um den Button zu aktivieren oder nicht zu deaktivieren.
Okay. Okay, jetzt haben wir also hier Ich mache
es mal groß. Diese Operation ist vor Merken möglich.
Was macht die? Die lässt sich von dem Medienauflister-Werkzeug,
die geselektierten Medien geben, gibt eine Liste von Medien zurück,
liefert eine Liste von Medien drin.
Und es wird ein Kunde geliefert. So, also das heißt,
in der Oberfläche, wenn ihr das seht hier,
ich habe hier was geklickt, ich kann ja hier auch mehrere selektieren,
die kann ich jetzt, die kann Anakronicover offensichtlich alle sehr vormerken,
weil der Button ist immer noch nicht ausgegraut.
Okay. Das ist genau das, was hier passiert.
Das war auch von uns vorgegeben. Und dann ist hier
Bulienvormerken ist möglich, ist erstens Kunde aus ungleich null und
die Medienliste ist nicht leer
Mhm. Und jetzt wird hier weiterhin gemacht vormerken, möglich ist gleich Vormerken möglich,
also wenn das überhaupt schon möglich ist. Das heißt,
wenn weder ein Kunde selektiert, noch irgendein Medium,
dann ist es vormerken möglich hier Falls und dann
wird dieser Ausdruck hier ausgewertet von links nach rechts,
das ist ja so, bei dem und Operator hier.
Und dann liefert das schon Falls, das heißt,
der Rest wird gar nicht mehr geprüft.
Wenn aber tatsächlich etwas selektiert ist, dann wird jetzt ja weitergeprüft.
Jetzt wird ja hier aufpassen an dem Verleihservice,
können alle vorgemerkt werden. Das heißt also hier,
haben, das Vormerkwerkzeug, das ist irgendwie schon zuständig,
diese Frage zu beantworten, weil es ja nur weiß,
was ist in der Oberfläche selektiert.
Dann haben wir hier den Verleihser. Der wird jetzt auch noch gefragt,
weil der Verleihservice hat ja Informationen darüber, was schon verliehen ist.
Und deswegen wird man den wahrscheinlich auch fragen müssen, ob das denn vormerkbar ist.
Okay, jetzt gehe ich hier mal auf dieses,
können alle vorgemerkt werden, gehe ich in die Implementation rein,
gehe ich in die Verleihservice Imple.
So, können alle vorgemerkt werden, ist implementiert über,
gehe über alle Medien rüber und frage für jedes Medium,
ob es von dem Kunden ausgeliehen werden kann
Das ist also auch hier relativ einfach implementiert.
Können alle vorgemerkt werden, so. Das heißt,
das wird zurückgeführt auf, kann vorgemerkt werden. Okay,
dann gibt es also hier, irgendwo kann vorgemerkt werden,
wo es kann vorgemerkt werden.
Ich gehe einfach drauf, ist ja immer das Einfachste.
Also können alle vorgemerkt werden, dann ist hier Open Implementation da.
Kann vorgemerkt werden, im Verleih Service, so.
Jetzt haben wir also wieder einen Kunden und ein Medium und
nicht im Verleih Service soll das geprüft werden,
ob der
vorgemerkt werden kann
Was macht der jetzt hier? Der lässt sich irgendwie eine Vormerkkarte geben,
eine Verleihkarte und so weiter und sagt erst mal,
fragt erst mal, aha, interessanterweise die Vormerkkarte,
kann vorgemerkt werden und verknüpft dann das Ergebnis irgendwie mit,
ob die Verleihkarte vielleicht auch schon sagt,
dass der schon irgendwie verliehen ist.
Also das ist einfach die Frage,
ob das bereits von dem Kunden entliehen ist.
Wir haben ja, erinnert euch, man konzentriert vormerken,
wenn man der Verleih ist. Das heißt,
diese Information, ob es verliehen ist,
kennt ja
nur der Verleih
Das weiß, was ich weiss, was verliehen ist.
Und dann ist es aber so, dass hier eher die Vormerkkarte fragt,
kann denn bei dir auf der Karte noch was vorgemerkt werden?
Die Vormerkkarte weiß nichts darüber, ob das Ding verliehen ist,
das muss die auch gar nicht wissen.
Die ist dafür nicht zuständig, sondern die Vormerkkarte, ja,
wie sie ist, die implementiert, können wir auch mal draufgehen.
Kann vorgemerkt werden von Kunde. Das ist implementiert über Ist-Vormerken
möglich und ist nicht schon vorgemerkt.
Also wenn jemand als Vormerker schon drauf ist,
kann er natürlich nicht noch als Vormerk eingetragen werden.
Und dieses ist Vormerken möglich, können wir uns auch mal
eben angucken,
wie das aussieht
Einfach nur, ob die Anzahl der Vormerker kleiner als drei ist. Das heißt,
aus Sicht der Karte, die Vormerkkarte kann beantworten,
ich habe schon drei Vermerker drauf, geht nicht,
oder ich habe weniger als drei, also geht das?
Ich habe noch Platz, eigentlich wäre es vielleicht ein besserer Name,
hier zu sagen, hat noch Platz für Vormerkel.
Und dann ist eben halt hier die zweite Bedingung,
dass es nicht schon vorgemerkt ist.
Das kann auf der Karte beantwortet werden. Das heißt,
lups, das war nicht das, was ich wollte.
Das heißt, der Verleihservice hat hier eine Menge von Verleihkarten,
eine Menge von Vormerkkarten und prüft eben halt ob das möglich ist.
Das heißt, diese Frage letztendlich zusammengefasst, ob der Button aktiviert wird oder nicht,
wird von drei verschiedenen Klassen beantwortet, in Teilen.
Das Vormerkwerkzeug sagt, nur wenn mir überhaupt was selektiert ist,
muss ich diesen Button überhaupt nur aktivieren. Wenn das der Fall ist,
dann muss ich immer noch den Verleihservice fahren, ob es geht.
Der Verleihservice guckt bei den Verleihkarten, ob das der Verleiher ist,
weil dann kann es nicht vorgemerkt werden vom Verleiher.
Und die Formelkarte guckt einfach nur, ist da noch Platz
auf der Formelkarte und ist es nicht schon vorgemerkt vom Gleichen,
von der gleichen Person.
Ihr seht, das kann man durchaus auch verteilen.
Aber wir haben Entwürfe gesehen, da war es dann so,
Service, die Datenstruktur, der Vorteile sich hat geben lassen,
also die Vormerkkarte hielt irgendwie an eine Liste der Verleiher, der Vormerker.
Und dann ging der Verleihsservice erstmal schön daran und hat sich das rausgeholt,
hat selber in den Daten gewühlt. Es war also nicht so,
dass der Verleihsservice einfach gesagt hat, hey, Vormerkkarte,
sag mal, ist bei dir noch Platz,
sondern er hat einfach gesagt, voll meine Karte,
gib mir deine Karten, ich wühl mal eben selber dran rum.
Das ist wieder genau das, was wir nehmen wollen. Das heißt,
nach den Zuständigkeiten, Zugeordneten sollte es so sein,
dass die Vormerkkarte zuständig ist für die Frage, für die Beantwortung der Frage,
ist da noch Platz drauf, für diese Person.
Und zuständig für die Frage, ob dazwischen und verlierst,
ist natürlich der Verleihservice zuständig,
ob was selektiert ist in der Oberfläche, ist das vom Werkwerkzeug.
Okay, wir werden diese beiden Beispiele jetzt noch mal so
ein bisschen genauer uns ansehen in den folgenden Minuten.
Was ich auch wichtig finde,
wenn man einen guten Entwurf machen will, ist,
das haben wir auch versucht mit unseren Entwurfsregeln euch ja schon nahe zu legen.
Deswegen ist das hier auch eine Freude, die schon bei
den Entwurfsregeln gezeigt wurde. Das ist diese Idee,
dass man Fachlogik und Tech-Technik in einem Software-System gut trennen sollte.
Also anwendungsfachliche Klassen, die vor allen Dingen Fachlogik modellieren,
sollten deutlich von rein technisch motivierten Klassen unterscheidbar sein
Okay, wenn ich mir jetzt zum Beispiel dieses Ding hier angucke,
dann ist die Fachlichkeit, um die es hier geht,
Grafen und Knoten, Knoten und Kanten, also irgendwie Verbindungen zwischen Knoten.
Das ist diese beiden Klassen hier, die modellieren die Fachlichkeit.
Darum geht es. Diese Klasse hier ist eigentlich nur dazu da,
um das zu visualisieren. Wenn ihr das System euch ein bisschen genauer anguckt,
dann seht ihr, dass wir diese fachlichen Klassen nicht so gebaut haben,
dass wenn sich in ihnen etwas ändert, dass dann Beobachter benachrichtigt werden.
Normalerweise würde man ein grafisches System,
habt ihr jetzt inzwischen gelernt, so bauen, dass irgendwie,
wenn sich etwas an dem, was angezeigt werden soll,
Schickt das eine Nachricht raus irgendwie und dann alle Beobachter,
zum Beispiel die Wuis, aktualisieren sich.
Das haben wir hier bewusst nicht gebaut in SE1,
weil wir natürlich wollten, dass diese beiden Klassen Urfachlichkeit sind,
weil daran die erarbeiten, daran solltet ihr programmieren,
nicht irgendwas mit Code zu tun haben,
wo dann irgendwie ein Beobachter benachrichtigt wird oder sowas,
was ja nichts damit zu tun hat.
Also haben wir das so gebaut,
dass alle Veränderungen nur über die GUI möglich sind.
Das heißt also, die GUI weiß einfach genau,
wie die Fachlichkeit ist.
Das ist jetzt hier
ein bisschen zusammengeklappt
Das Projekt sah eigentlich ein bisschen anders aus.
Ich kann das hier mal zeigen. So sah das im Original
aus, als wir es ausgeliefert haben. Das heißt,
hier waren drei Klassen, die nur dafür da waren, das zu visualisieren.
Ich werde diese Klassen leider mal ein bisschen genauer erläutern,
aber ihr seht hier, das ist einfach nur Gui-Darstellung des Ganzen, Visualisierung.
Und hier die Fachlichkeit. Also ein Mini-Beispiel dafür,
dass man fachliche Klassen sehr gut trennen kann von technisch motivierten Klassen.
Hier geht es nur um Swinggehubere und um Farben von Knoten,
also zum Beispiel hier
Einen kleinen Beispiel. Die Knoten haben irgendwie eine Farbe und
diese Knoten. Und das ist alles überhaupt nicht in der...
Also hier sind irgendwie die Farben drin und ein ähnliches Zeug.
Das ist ja reine Visualisierung. Okay. Also das ist
nur zur Erinnerung immer eine gute Idee,
auch in unseren Entwurfsregeln ist das ja so.
Wir sagen Materialien, Services, Fachwerte sollten Fachlichkeit darstellen.
Werkzeuge sollten eher technisch motiviert sein,
also so Gui-Darstellungsfolgen
Und... Kommen wir jetzt zum ersten zentralen Begriff,
wenn man sich mit Objekt notiertem Entwurf beschäftigt.
Das ist die Frage der Kopplung. Dass dieser Begriff bezeichnet,
ganz einfach nur den Grad der Abhängigkeiten zwischen den Einheiten eines Softwaresystems.
Das heißt, jede Beziehung, die in einem Klassendiagramm darstellbar sind,
sind Abhängigkeiten. Also eine Benutzsbeziehung ist eine Abhängigkeit,
eine Vererbungsbeziehung ist eine Abhängigkeit. Und die Einheiten,
zwischen denen diese Abhängigkeiten untersucht werden können, seien Methoden,
Klassenpakete, Subsysteme, je nachdem,
auf welcher Granularitätsstufe man sich seine Systeme ansieht
Je mehr Abhängigkeiten in einem System existieren, desto stärker ist
die Kopplung. Ganz einfach. Erstmal so rein technisch,
was ist überhaupt Kopplung. Das heißt also,
wenn wir uns das hier angucken, diesen schwarzen Knoten,
der ist mit jeder, mit jedem anderen Knoten offensichtlich verbunden.
Da scheint irgendwie eine Abhängigkeit zu bestehen.
Das ist auch momentan hier nicht gerichtet. Das ist jetzt
nur eine Richtung ist oder beide, weiß man nicht.
Auf jeden Fall ist dieser Knoten mit allen anderen Knoten gekoppelt.
Während in diesem Graf es so aussieht, dass tatsächlich jeder Knoten
jeden kennt. Das ist der maximale Grad der Kopplung,
der möglich ist in einem System. Das heißt also,
erstmal muss man sich diese Abstraktion antun, dass man sich
ein Objekt unter dieses System vorstellt als einen Grafen von Knoten und Kanten.
Was aber nicht weiter schwierig ist, weil Klassendiagramme sind ja ziemlich genau das.
Und das Ziel sollte sein, möglichst lose zu koppeln.
Das heißt, wir wollen möglichst wenig Kopplung haben zwischen den Einheiten.
Das heißt, sowas hier ist der Albtraum eines jeden Softwareentwicklers.
Sowas sieht schon eher ganz gut aus
Da sieht es dann so aus,
dass dieses Objekt zwar alle anderen kennt,
aber die anderen sind zumindest sehr klar,
nur entweder von sich selbst abhängig oder eben halt von diesem Vermittlungsobjekt,
Vermittler, Mediator, das ist so eine Visualisierung eines Entwurfsmusters,
was es auch gibt.
Okay. Ja, also, warum, jetzt kann man
sagen, warum ist hier jetzt lose Kopplung so erstrebenswert?
Was soll denn das? Lass das doch ruhig da
ganz viele Verbindungen haben. Ist doch alles wunderbar,
wenn die Verbindung notwendig ist,
dann muss sie da auch hin
Man kann eben halt, das habt ihr ja inzwischen hoffentlich auch mitgekriegt,
jedes System nicht nur auf eine Art und Weise bauen,
sondern auf Zischenarten und Weisen.
Es gibt ganz viele Möglichkeiten, eine bestimmte Funktionalität in einem Software-System umzusetzen.
Man kann es mit einem guten Entwurf machen,
man kann es mit einem schlechten Entwurf machen.
Beides ist möglich. Und warum lose Kopplung jetzt gut ist?
Naja, es gibt eben halt verschiedene Kriterien, die ganz relevant sind. Also zum einen ist es so,
wenn ich mir eine einzelne Klasse angucke und ich will verstehen,
was die Klasse macht, dann ist es ziemlich blöd,
wenn ich, wenn diese Klasse mit sieben anderen Klassen gekoppelt ist,
dann muss ich nämlich wahrscheinlich nicht nur diese Klasse mehr ansehen,
sondern auch noch die sieben anderen Klassen
Das ist ziemlich lästig. Wenn ich diese Klasse jetzt ändern will und sie ist oder
sieben Klassen sind davon abhängig oder sie ist von sieben Klassen abhängig,
muss ich mir auch wieder angucken, wie,
welche anderen Klassen sind denn von dieser Änderung betroffen?
Je mehr Klassen diese Klasse kennen,
desto schwieriger ist es möglicherweise diese Änderung an dieser Klasse durchzuführen.
Also Änderungen werden erschwert. Das heißt also,
insgesamt ist es einfach so, je loser ein System gekoppelt ist,
je weniger Kopplung in einem System existiert,
desto besser lässt es sich warten.
Das ist erstmal recht abstrakt und das ist aber,
wie gesagt,
an verschiedenen Stellen
auch konkretisierbar
So, jetzt haben wir gesagt,
Kopplung ist irgendwo diese Beziehung zwischen Elementen in einem Softwaresystem.
Wir haben jetzt hier noch eine Unterscheidung an zwei Begriffe vorgenommen,
nämlich einmal explizite Kopplung und implizite Kopplung.
Explizit ist Kopplung, wenn Sie formal nachweisbar ist.
Das heißt, wenn dein Compiler in der Lage ist,
mir ein Klassendiagramm zu generieren, wo man diese Abhängigkeiten sieht,
das seht ihr in Blue Jay.
Wenn ihr ein Blue Jay an ein Java-Projekt öffnet,
dann kann Blue Jay irgendwie genau diesen Graf berechnen und euch anzeigen.
Das ist also ganz formal ableitbar.
Diese Klasse hat eine Exemplarvariable vom Typ dieser Klasse,
also ist da wohl
eine Benutzbeziehung
Diese Beziehungen sind ja dann berechenbar. Die meisten Analyse-Werkzeuge können das eben darstellen.
Das ist schön, wenn das explizit gemacht ist.
Also wenn man überhaupt objektorientierten Entwurf in dieser Art und Weise
visualisiert bekommt, das ist von Vorteil.
Implizite Kopplung ist aber sehr viel unangenehmer.
Die ist nämlich formal nicht nachweisbar. Wie sieht jetzt so
ein Beispiel für eine implizite Kopplung aus?
Das klingt jetzt erstmal ominös, die ist nicht nachweisbar.
UH, was ist denn das? Naja,
also generell ist es so,
ein Entwickler kann sein Wissen über die Interne eines Dienstleisters
in eine Klientenklasse reinprogrammieren
Ich habe dieses Beispiel hier dabei,
dass ich selbst erlebt habe, in einem Projekt,
als ich noch selbst studiert habe,
da hatten wir an einer Forschungsarchitektur gearbeitet, die verschiedene spannende Eigenschaften,
einen großen virtuellen Speicher mit ganz viel Adressen und Capabilities,
um den Schutz zu gewährleisten.
Also alles ganz total cooles Zeug. Und da hatten wir einen Klassencomparter.
Wir haben also für diese Architektur auch einen Geiler gehabt,
so eine Pascal-Variante übersetzt. Und in diesem Klassencompiler Gab es dann
einen fachlichen Behälter? Also irgendwie eine Menge von Tokens,
die irgendwie von dem Compiler da verarbeitet wurden.
So, und es gab einen Klient,
der hat diese Schnittstelle benutzt von diesem fachlichen Behälter.
Und die Schnittstelle von diesem fachlichen Behälter hielt unter anderem die Möglichkeit,
über alle Elemente, die in dem Behälter drin sind, zu iterieren.
Also ein Token nach dem anderen zu bearbeiten.
Ich weiß nicht mehr genau, was die Details waren,
aber das ist ja, sagen wir mal, abstrakt nachvollziehbar.
So, was jetzt gebaut war, das ist die Schnittstelle.
Das hat also einfach hier gesagt,
man kann mit einer Operation Get Next oder so,
da irgendwie das nächste Element aus dem Behälter bekommen.
Um sie alle zu verarbeiten. Das heißt, erklären konnte,
an alle Elemente rankommen. Die Implementation hat diese Elemente alphabetisch sortiert.
Warum nicht? Ist eine willkürliche Entscheidung, kann man machen.
Also Elemente alphabetisch sortiert. Der implementiere das Klienten, kannte
auch den Compiler. Das war sozusagen die gleiche Person.
Das heißt, diese Person, die den Klienten gebaut hat,
wusste, dass hier die Elemente alphabetisch sortiert kommen.
Und die hat sich tatsächlich darauf verlassen. Das heißt,
der hat die auch alphabetisch sortiert gebraucht und hat sie
dann entsprechend auch weiterverarbeitet
Aber er hat das sozusagen, er hat sich darauf verlassen,
naja, ich weiß ja, dass es alphabetisch kommt,
also hat das auch entsprechend benutzt.
Aber es stand nicht an der Schnittstelle, dass es alphabetisch sortiert sein muss.
So, jetzt Monate später kommt ein anderer Programmierer,
der mit dem System bisher nichts zu tun hatte und soll
jetzt hier die Implementation dieses fachlichen Behälters ersetzen.
Also diese Implementation, die ja austauschen. Guckt sich
die Schnittstelle an, ist ja so eine Art Spezifikation.
Was muss ich bauen? Aha, ich muss hier
alle Elemente der Reihenfolge nach rausgeben. Die Datenstruktur,
die er da drunter gelegt hat, war irgendwie effizienter,
keine Ahnung, was es genau war.
Aber der Effekt war, das kam nicht mehr alphabetisch raus
Das ist eigentlich völlig in Ordnung. Der hat hier nichts falsch gemacht.
Der hat sich an die Spezifikation gehalten und hat das genauso umgesetzt.
Es kommt halt nun zufällig nicht mehr sortiert raus,
sondern nach einer willkürlichen Reihenfolge, die nach der,
die sich aus der Implementation ergibt.
Das Blöde ist, hier, es Klienten kurz zerbrochen.
Das heißt, da ist irgendetwas schiefgelaufen. Plötzlich war das
nicht mehr sortiert und dann ist da irgendwas in der Darstellung schiefgelaufen.
Das ganze System war dann so ein bisschen kaputt. So,
und jetzt ist es spannend, rauszufinden, was ist jetzt hier falsch?
Das kann man nämlich nicht sofort erkennen. Man sieht irgendwie,
funktioniert hier nicht mehr. Warum? Bis man dann herausgefunden hat,
dass, also bis man verstanden hat,
dass der Klient implizit eine Annahme gemacht hat,
dass das sortiert sein muss, damit er hier sein,
damit das hier funktioniert, was der Klient macht,
da braucht man eine Weile für.
Das ist so ein richtig so ein typisches Beispiel für implizite Kordnung.
Das ist nicht formal nachweisbar. Wissen über interner von hier
sind hier in den Code rein programmiert,
ohne dass das irgendwie nachgewiesen werden kann.
Und das ist sozusagen durch irgendein Tool extrahiert werden kann.
Das nennt sich implizite Kopplung,
also eine Abhängigkeit,
die nicht
nochmal nachweisbar ist
Okay. Ein weiterer Punkt, der bei guten Entwürfen beachtet
werden sollte, ist, man sollte geeignete Schnittstellen wählen.
Wir haben ja früh in SE1 schon von Interfaces eingeführt,
mit Interfaces können wir Schnittstellen definieren. Und es ist natürlich so,
wenn ich jetzt über Koppelung Gedanken mache,
dann würde ich natürlich mal sagen, naja,
der Grad der Koppelung wird wahrscheinlich primär bestimmt über die Schnittstellen.
Wenn ich bei einer Klasse sage, im Interface,
ich brauche einen Parameter von Klasse B,
dann ist vermutlich das Interface mit der Klasse B
irgendwie gekoppelt,
weil der irgendwie dazugehört
Das heißt, die meisten Abhängigkeiten, diese meisten Kopplungen entstehen über Schnittstellen.
Und man kann bei Schnittstellen versuchen, die Kopplung zu reduzieren.
Ich habe hier unter anderem den Horstmann mal hergenommen,
um den so ein bisschen zu zitieren. Und der hat halt
mehrere Kriterien aufgestellt, wie eine gute Schnittstelle aussehen sollte.
So, sie sollte vollständig sein. Naja, das ist offensichtlich.
Also ich muss natürlich das anbieten, was man braucht.
Wobei auch das kann man schon differenzieren. Kommen wir noch
drauf. Klar. Ja, klar sollen die klar sein.
Das muss schon irgendwie verständlich sein, was da passiert
Konsistent. Okay, konsistent heißt,
wenn ich in drei Methoden nach einem bestimmten Prinzip arbeite,
sollte ich nicht in drei anderen Methoden in demselben Interface
nach einem anderen Prinzip arbeiten.
Das sollte irgendwie konsistent zueinander sein. Konsistent, da habe ich
ein Beispiel hier. Hier habe ich die API von Java
. Es gibt eine Klasse, die heißt Date.
Java You Till Date. Ich zeige mal Java You Till
Date. Java You Till. Die Klasse Date.
Die Klasse Date ist zuständig um einen Zeitpunkt darzustellen
Datum. Das ist alles wunderbar. Man kann hier irgendwie
Dinge irgendwie, das ist alles mögliche erläutert hier.
Entscheidend ist das hier. Hier ist represented by the integrer y minus 1990,
also irgendwie, das sind die Jahre seit 1900, nicht 1919, 1900.
Und ein Monat ist repräsentiert durch eine ganze Zahl von 0 bis 11. Ja,
der Januar ist die 0 und der Dezember ist die 11.
Und ein Tag im Monat ist repräsentiert durch eine ganze Zahl
von 1 bis
So, jetzt kann man sagen, ist das konsistent?
Ja, die Monate sind von null Beginn nummeriert.
Die Tage im Monat sind von 1 Beginn nummeriert. Da würde ich sagen,
das ist ein klassisches Beispiel von einer nicht konsistenten Schnittstelle.
Ich möchte nicht wissen, wie viele Programmierfehler schon begangen worden sind,
weil genau diese unausgewogene Ordnungsmöglichkeit da gewählt wurde.
Informatiker wissen ja, man muss immer aufpassen,
ob bei 0 oder bei 1 angefangen wird, klar.
Aber warum jetzt bei dem einen so,
beim anderen so gemacht wurde, ist eigentlich nicht nachvollziehbar.
Was nützt das, dass der Monat bei 0 anfängt?
Also plus minus 1 Fehler lieben wir ja alle.
Wir sind ja alle inzwischen gut verseucht durch Programmierung.
Also das ist so ein Beispiel von konsistent, dass dich
irgendwie nicht zwei Schnittstellen irgendwie zwei Operationen in der Schnittstelle irgendwie unterschiedlich verhalten.
Minimal, möglichst klein heißt das. Und bequem benutzbar.
Auch ein wichtiger Punkt. Schnittstellen sind dafür da, benutzt
zu werden. Also ein Dienstleister bietet eine Schnittstelle an,
ein Klient soll die ja benutzen.
Dann wäre es blöd, wenn der Dienstleister es zu kompliziert macht.
Also möglichst einfach benutzbar, bequem
Das ist ganz schön, aber das widerstrebt eigentlich dem Anspruch,
dass sie minimal sein soll. Wenn man eine minimale Schnittstelle baut,
versucht man möglichst viele Operationen reinzusetzen, möglichst wenig Operationen,
aber möglichst bequem kann halt heißen, naja,
häufig muss ich um eine bestimmte Dienstleistung auszuführen.
Hier als Klient, erst die, dann die,
dann die Operation aufrufen. Das kann man eigentlich zusammenfassen in
eine Convenience-Operation, die diese drei Dinge auf einmal macht.
Mache ich das, habe eine weitere Operation.
Dann ist das bequem benutzbar, aber nicht mehr minimal.
Das passiert sehr viel
Also man sieht hier auch, diese entwurfsliche Linien können sich
auch teilweise widersprechen. Das ist also mal wieder eine Abwägungssache.
Es gibt kein richtig und falsch bei Objekte und hiertem Entwurf.
Es gibt nur gut geeignet und weniger gut geeignet.
Konkretisierung für diese Idee von Schnittstellen, wie man sie denn
verwenden kann, ist, wir sollten Interfaces verwenden.
Ich habe ja schon gesagt, in SE1 haben wir das früh eingeführt.
Also ein explizites Sprachkonstrukt von Java, mit dem ich die Schnittstelle
einer Klasse modellieren kann. Häufig ist es tatsächlich möglich,
die Kopplung zwischen zwei Klassen zu verringern, Interface modelliere,
dass nur die Operation zusammenfasst, die die eine von der anderen benutzt.
Also das und erfüllt dann unter anderem die Forderung nach schmalen Schnittstellen,
macht eine konkrete Kopplung deutlicher und kann helfen, statisches aufzulösen.
Diese Idee von Interfaces, dass man die verwenden sollte,
das Interface ist, dass den Entwurf verbessern,
je mehr Interfaces in einem Entwurf drin sind,
wenn sie denn vernünftig gewählt sind, das stammt auch schon,
steht auch schon lange sozusagen in der Community im Raum,
nämlich dieses Zitat aus dem Gamma,
aus dem Entwurfsmusterbuch,
Programm to interface not inplementation
Das ist tatsächlich etwas, was wir hier relativ früh euch vermitteln.
Und ein konkretes Beispiel dafür, wie man mit Interfaces Kopplung
auflösen kann oder reduzieren kann, will ich mal zeigen.
Das ist nämlich genau dieses Beispiel hier in diesem Blue-Jay-System.
Hier haben wir das Hauptfenster und den Grafpendeln. Das Hauptfenster
kennt den Grafpendel, der Grafpendel, kennt das Hauptfenster.
Warum? Ich habe das hier auf der Folie,
Hauptfenster, ein J Frame mit Border Layout.
Das ist also mehr oder weniger obflächlich, hier sind
die Buttons, wo ich irgendwie neue Knoten hinzuwüfen kann.
Hier ist diese Meldungszeile
So, als Center-Komponente von dem Border-Layout ist hier dieses Graph-Panel eingesetzt.
Also so ein Graph-Panel ist genau dieses Ding,
was hier in der Mitte diese Knoten hält,
wo ich dann hier auch drauf rumklicken kann und das Graph-Panel
reagiert tatsächlich auf meine Mausklicks und auf meine Mausbewegung.
Ihr seht, wenn ich jetzt hier irgendwie was bewege,
dann wird hier unten diese Meldung aktualisiert.
Das heißt also, der Graf-Panel muss dieses umgebende Fenster kennen,
damit er eben halt diese Meldung da in die Statungszahlung geben kann.
Umgekehrt muss natürlich dieses umgebende Fenster dieses Panel kennen,
weil der gehört ja da rein und insbesondere kann ich zum Beispiel hier,
wenn hier auf neue Knoten geklickt wird,
dann soll ja der Knoten da auch eingefügt werden.
Also muss ja das umgebende Ding, das enthaltende Ding kennen.
Das heißt, die kennen sich gegenseitig
So, Hauptfenster muss Graf-Panel kennen und Graf-Panel muss auf dem Fenster kennen.
Wir haben eine zyklische Abhängigkeit. Zyklische Abhängigkeiten sind nicht schön,
werde ich gleich noch sagen. Wie kann man das auflösen?
Naja, ich mache einfach die Abhängigkeit per Interface explizit.
Der Graf-Panel muss ja nicht die gesamte Schnittstelle des Hauptfensters kennen.
Wenn ihr euch das anguckt hier, diese klasse Hauptfenster,
die hat irgendwie eine ziemlich große Schnittstelle hier, also ziemlich bescheuert.
Das sind die statischen Operationen und dann, ja,
das muss der Graf-Panel ja gar nicht kennen.
Der Graf-Panel ist ja nur so ein kleines Ding,
ist zuständig dafür, das zu visualisieren,
diese bunten kleinen Dinger,
die man hin und her
schieben kann
Und ab und zu mal eine Meldung rauszuspucken.
Und wie diese Meldung aussieht und wem er sie übergibt,
muss er eigentlich gar nicht so genau wissen.
Deswegen ist hier ein Interface-Melder definiert worden.
Das sieht sehr einfach aus. Eine Operation melde,
dass diese übergebende Meldung ausgibt. So. Und das heißt,
das Hauptfenster erzockt den Graf-Panel und übergibt dann dem Graf-Panel eine Referenz auf sich selbst,
aber unter dem Interface, unter dem statischen Typ Melder.
Das heißt, der Graf-Panel, dann sieht man hier
der statische Zyklus zwischen diesen beiden Klassen ist aufgelöst.
Der Graf-Panel kennt nur noch irgendeinen abstrakten Melder,
was dahinter steht, ist ihm egal.
Und das Hauptfenster ergibt sich dann eben halt auch als Melder
Okay, das ist das,
was hier mehr oder weniger tatsächlich auf der Folie steht.
Dadurch haben wir den Zyklus aufgelöst. Zur Laufzeit ist es natürlich
trotzdem noch so, dass die beiden Objekte sich gegenseitig kennen.
Aber es ist so ähnlich wie bei dem Beobachtermuster.
Das löst ja auch nur Abhängigkeiten statisch auf. Okay,
Entwurfsentscheidungen, Kapseln, auch eine generell gute Regel.
Modellieren ist nämlich allgemein das Fällen von Entwurfsentscheidungen. Also wenn
ihr ein System baut, dann fällt ihr Entscheidungen und jede Entscheidung,
die ihr trefft, wirkt sich auf nachfolgende Entscheidungen aus.
Das ist relativ automatisch so. Fast immer ist das so,
dass irgendwie Entscheidungen abhängig davon sind, was vorher entschieden wurde.
Das wiederum sollte einen aber dazu bringen, dass man die Dinge,
die sich mit großer Wahrscheinlichkeit nicht ändern werden, zuerst festlegen sollte.
Weil man dann weniger Probleme hat hinterher, Änderungen durchzuführen. Wenn ich etwas,
was ich häufig ändere, als erstes entscheide,
festlege und dann alle anderen Entscheidungen davon abhängig machen,
jetzt ändert sich das ständig, dann müssen alle Entscheidungen danach ständig wieder neu überdacht werden.
Unschön. Das heißt also, es ist schlau,
die Kernabstraktion zuerst zu identifizieren,
auch zuerst zu modellieren die fachlichen Einheiten üblicherweise,
wenn die sich die Fachlichkeit nicht so oft ändert.
Und andere Sachen, wie zum Beispiel,
welches Greenframework verwende ich denn, um das zu anzuzeigen,
das kann sich eben halt auch mal ändern.
Damit will man Dinge leichter austauschbar halten. Das ist natürlich
alles nicht so leicht. Man braucht da ein bisschen Erfahrung und
auch ein bisschen Augenmaß und natürlich auch Informationen über den Kontext,
nämlich was sich wie oft ändert.
Aber es ist auf alle Fälle eine gute Entwurfsrichtlinie.
Ja, ich glaube, wir machen erstmal eben kleine Pause.
Fünf Minuten und dann mache ich weiter mit dem Geheimnisprinzip.
Dann würde ich sagen, machen wir weiter.
Bitte wieder reinkommen, bitte wieder Tür schließen
Okay,
also eine weitere Ausprägung von dem Streben nach möglichstloser Kopplung,
nach möglichst wenig Abhängigkeiten zwischen Einheiten in einem System
ist das Geheimnisprinzip
Nur relevante Merkmale an Entwurfs- und Konstruktionszeit sollten für Klienten sichtbar und zugänglich sein. Das,
was wir euch von Anfang an schon so ein bisschen gezeigt haben,
ist ja, Exemplarware haben sollten Private sein,
Operationen Public, das, was man nach außen anbietet,
sollte zur Schnittstelle gehören und man sollte möglichst viel verstecken.
Das ist auch eine Ausprägung dieser Idee, Entwurfsentscheidungen irgendwie kapseln.
In dem Moment, wo ich Dinge privat mache bei einer Klasse,
habe ich die Möglichkeit, sie relativ leicht zu ändern,
ohne dass irgendwelche Klienten davon betroffen sind.
Das ist ein Vorteil
Das ist letztendlich als Grundprinzip von David Parners Anfang der 70er Jahre formuliert worden.
Damals war er noch, das haben wir unverstanden,
viele Leute haben da nur den Kopf drüber geschüttelt,
was er gesagt hat, er hat sich komplett durchgesetzt mit dem,
was er da gesagt hat.
Und das findet sich eben halt heutzutage in objektorientierten Entwürfen auch wieder.
Man kann das hier noch mal so ein bisschen sich angucken in
diesem Blue-Jay-System. Hier habe ich zum Beispiel eben gesagt,
dass in dieser Oberfläche hier diese Böbbel, das Gelbe,
das Rote und so weiter,
das sind ja Knoten
Visualisierung von Knoten. Tatsächlich ist es so, dass in dieser Klasse Graf-Panel,
die ja diese Visualisierung vornimmt, zwei weitere Klassen geschachtelt sind.
Das ist eine relativ große Klasse, die man sieht, nicht so schön. Und man sieht hier unten,
gibt es jetzt einmal eine Klasse Knotensicht,
das ist eine private Klasse,
die also geschachtelt innerhalb dieser anderen Klasse, eine statische,
geschachtelte Klasse,
die nennt sich Knotensicht und knoten sich das tatsächlich so eine Sicht auf so einen Knoten,
kann also eine Farbe haben und hat eine gewisse Ausdehnung und so,
geht also nur um die Visualisierung und dann gibt es hier
noch eine weitere geschachtelte Klasse,
die heißt Verbindung
Die Verbindung in dem Original, in der Fachlichkeit, in der Klasse hier.
Hier sind Verbindungen implizit modelliert. Das heißt,
ein Knoten kennt andere Knoten, hat Referenzen auf andere Knoten.
Und damit ist die Verbindung damit implizit modelliert. Aber hier
in der Visualisierung müssen ja diese komischen Linien gezeichnet werden.
Also muss man irgendwie idealerweise eine Klasse haben,
die so eine Verbindung hier repräsentiert.
Unter anderem um diese Richtung da auch so drauf zu malen.
Da ist so eine Zahl drauf gesetzt,
das ist auch schön in der Mitte bleibt und so weiter.
Das muss nicht
irgendwo gemacht werden
Eben halt in dieser geschachtelten Klasse Verbindung gemacht. Die haben natürlich
überhaupt nichts mit der Veröffentlichkeit zu tun. Das heißt,
ich habe, als ich das System gebaut habe, diese Klassen da reingegekapselt,
weil sie für die Aufgabenstellung von diesem Projekt völlig irrelevant sind.
Also es würde ganz anders aussehen, wenn diese Klassen jetzt
hier auch noch mit dem Diagramm drin wären.
Ich habe das in einem weiteren Projekt hier gemacht. Und
wenn ich mir hier jetzt alles in der Implementationssicht anzahle,
also würde das so aussehen
Das heißt, eigentlich geht es uns nur um in der
Aufgabenstellung für Blatt 14 zwischen Graf und Knoten, soll irgendwas passieren.
Alles andere ist nicht so wichtig. Aber dann sind hier
irgendwie sechs Klassen, die irgendwie nur damit beschäftigt sind,
irgendwie diese Visualisierung zu zeigen.
Knotensicht, eine Verbindung hat irgendwie, kennt die Knotensicht,
der Graf-Panel kennt die beiden. So, das ist natürlich
total blöd. Und ich habe es deswegen geschachtelt.
Das ist also eine Entwurfsentscheidung, die eigentlich gar nicht relevant ist.
Man sieht hier auch, dass die ja hier die Dinger benutzen.
Also die Knotensicht benutzt natürlich, das ist eine Sicht auf den Knoten.
Also sollte natürlich eine Knoten sich den Knoten kennen.
Umgekehrt kennt der Knoten nicht die Knotensicht.
Das ist also schon mal
ganz sauber
So, und was aber ja schon mal hier deutlich wird,
hier sind lauter Klassen, die eigentlich keine Relevanz haben für die eigentliche fachliche Aufgabe.
Das heißt, es ist einfach eine Frage von Abstraktionslevel,
mit dem ich da drauf gucke. Wenn ich mich damit
beschäftigen will, dann brauche ich das nicht sehen. Man kann tatsächlich,
was euch vielleicht klar ist oder was ihr euch ja immer,
wenn ich mir jetzt zum Beispiel diese Klasse angucke, Verbindung,
dann kann man sich ja anzeigen lassen, einmal den Quelltext,
da sehe ich sozusagen den gesamten Quelltext oder ich lasse mir die Dokumentation anzeigen,
da sehe ich eigentlich nur die Schnittstelle.
So, so was haben wir uns überlegt,
sollte eigentlich auch in dem Klassendiagramm
möglich sein
Das heißt, hier sehe ich jetzt Graph Panel benutzt in
seiner Implementation, Knotensicht und Verbindung. Das heißt,
eigentlich sind diese beiden Klassen ein Implementationsdetail des Graph Panels.
Und ich kann auch im UML-Klassendiagramm jetzt doch eigentlich auch so
eine Sicht haben auf so eine Klasse,
dass ich sage, ich will eigentlich nur wissen,
wie sieht die Schnittstelle des Graph Panel aus.
Und deswegen kann ich hier mir die Schnittstellen an sich zeigen lassen,
das ist also eine Erweiterung von Bluegee, die wir gebaut haben.
Und dann werden diese beiden Klassen ausgeblendet, weil sie zur Implementation gehören.
Also das ist dann hier so angedeutet, wenn es weiß,
abgedeckt ist, dann sieht man nur noch die Schnittstelle.
Also die Abhängigkeiten,
die sich aus der Schnittstelle von Graph Panel ergeben
Wenn ich das jetzt wieder aufklappe,
dann sieht man wieder auch in der Implementation tauchen weitere Klassen auf,
aber die sind nur für die Implementation wichtig.
Das kann ich also hier zuklappen und auch wieder aufklappen.
Und genauso kann ich auch das Hauptfenster hier,
das Hauptfenster benutzt ein Graph-Panel und damit das entkoppelt ist,
habe ich hier dieses Interface dazwischen gebaut.
Das ist natürlich auch eigentlich Lärm aus einer gewissen Sicht.
Ich möchte davon abstrahieren, kann es also auch hier sagen,
zeig mir die Schnittstellenansicht, also das,
was ein Klient wissen muss, um es zu benutzen,
und dann sehe ich nur noch das hier.
Das ist nämlich ein Hauptfenster, den Graph benutzt.
Und dann reicht es mir einfach,
die Schnittstelle zu sehen, wo ich sagen kann,
es gibt Main und es gibt irgendwie die Möglichkeit,
sich den visualisierten Grafen geben zu lassen
Also das nur so als kleine Andeutung,
dass man tatsächlich auch diese Abstraktionsniveaus, was gehört zur Implementation,
was ist für die Schnittstelle relevant,
auch in einem UML-Diagramm durchaus unterschiedlich gewichten kann und sehen kann.
Und wir haben halt so ein Patch gebaut für Blue Jay,
der wird hoffentlich demnächst dann auch als Extension der Welt zur Verfügung stehen.
Das ist momentan noch ein Forschungsding. Okay,
dass das Geheimnisprinzip taucht an verschiedenen Stellen noch auf.
Zyklen vermeiden habe ich eben schon erwähnt, ist blöd.
Man versucht also zu vermeiden, dass Zyklen bestehen, warum?
Erstens lassen sich zyklisch abhängige Klassen schlecht, unabhängig voneinander testen.
Ich kann halt nicht isoliert eine Klasse testen, wenn durch eine Abhängigkeit stehen.
Offensichtlich. Initialisierungsreihenfolge ist auch nicht klar, wenn da eine
typische Abhängigkeit ist, mache ich zuerst diese oder zuerst dieses Exemplar.
Wie kann das überhaupt gehen? Gibt auch keinen offensichtlichen Einstiegspunkt,
um sich einen Entwurf einzulesen. Man kann das ein bisschen relativieren.
Wenn Klassen sehr nah beieinander sind, also im selben Paket,
zwei Klassen, die sehr eng zusammenhängen,
wie zum Beispiel dieses hier Graph Panel und Hauptfenster,
dann kann man auch sagen, na gut, typische Abhängigkeit ist okay.
Wir haben uns hier die Mühe gegeben,
das mit dem Interface aufzulösen, aber ist in gewisser Weise okay,
weil sie dicht beieinander sind.
Viel schlimmer sind, typische Abhängigkeiten über Pakete hinweg
Generell sollte man das vermeiden. Je größer der Zyklus,
desto erstens schwerer zu erkennen und zweitens, desto unangenehmer.
Man sollte eigentlich in wie größeren Systemen keine Zyklen haben.
Auf kleiner Ebene noch okay, je größer, desto schlechter.
Deswegen haben wir auch hier diese SE2-Entwurfsregeln, die wir euch
gegeben haben, die spielen das auch sehr schön wieder.
Wir haben diese vier Elementtypen. Und wenn man hier sieht,
da ist auch keine zyklische Abhängigkeit,
ist also immer relativ klar, Fachwerte kennen nur Fachwerte,
Materialien, Fachwerte und Materialien, Services kennen,
Services,
Material und Fachwerte und Werkzeuge
kennen die alle
Und wenn man diese Beziehung einhält und dann auch diese entsprechenden Elemente in entsprechende Pakete legt,
Werkzeuge, Services und so weiter,
dann entstehen daraus dann sie tief genau diese Abhängigkeiten,
dass auch diese Pakete nur so die anderen Pakete kennen.
Das ist also auch durchaus ein wichtiger Aspekt. Also Zyklen vermeiden,
dann habe ich hier das Lore of die Miete. Ein,
naja, eigentlich schon damals, als es formuliert wurde,
war es kein Gesetz, sondern ein, naja, eine Entwurfsrichtlinie.
Es gab ein Demitersystem Objekt natürlich das System aus den 80er Jahren.
Und die haben damals diese Entwurfsrichtung hier aufgestellt.
Wie lautet die Richtlinie erstmal im Original? Also was ist,
was besagt das Law auf die Mieter? Wenn ich ein
Objekt O habe von einer bestimmten Klasse und innerhalb der Methode M
mich befinde. Welche Methoden sollte einem dann aufrufen können?
Gut, erst alle Operationen von O. Klar,
ein Objekt kann seine eigenen Operationen aufrufen, seine eigene Methoden.
Dann alles, was als Parameter an die Methode übergeben wird,
kann natürlich auch benutzt werden.
Da kann natürlich auch Sachen dran aufgerufen werden. Dann alles,
was in der Methode erzeugt wird, Sollte man natürlich auch benutzen können.
Und die Exemplarvariaben von O. Natürlich, die sollte man
auch benutzen können in den Methoden eines Objektes.
Aber nicht mehr. Das heißt, wenn man sich sowas anguckt,
hier sowas wie, nehmen wir an,
wir befinden uns ins Diene und es soll für einen Studenten
in seine Daten eine Note eingetragen werden.
Man kann das irgendwie so aussehen,
Student hat irgendwie eine Operation, wo man seinen Get Record,
also sozusagen seine Studienerfolgsdaten irgendwie abgelegt sind, bekommen kann.
Dann kann man den Eintrag für SE2 bekommen, zum Beispiel für
2006. Und dann kann man daran aufrufen,
Ed Result 93.
Also solche langen Aufrufketten
Die so hintereinander stehen,
die sind ein Zeichen für die Verletzung des Law of the Mieter,
weil natürlich hier Student irgendwie Get Record,
das heißt also irgendwie hat man diesen Parameter als Tütenden vielleicht reingereicht bekommen in die Methode M.
Jetzt ruft man darauf Get Record an,
auf und dann über Aufrufketten weiteren.
Das heißt, man geht eigentlich deutlich darüber hinaus,
was eigentlich in meinem Ängsten Bekanntenkreis eigentlich vorhanden ist.
Warum ist das so
Struktur traversiert wird. Also Objekte stehen in Abhängigkeiten zueinander, dieses Objektgeflecht wird traversiert.
Und damit steht hier hardcodiert, wie diese Struktur sein muss.
Wenn sich jetzt an dieser Struktur irgendwas ändern sollte,
die Flexibilität braucht man eigentlich ja,
dass man auch mal Dinge ändern kann,
dann ist dieser Aufruf möglicherweise nicht mehr korrekt auf einem bestimmten Punkt.
Das heißt, dieser Aufruf hier, diese ganze Kette,
hängt davon ab, dass genau diese Struktur so bleibt.
Also eine ausimplementierte, hardcodierte Festschreibung einer bestimmten Struktur.
Das ist nicht schön
Also hier lange Aufrufzahlen, gleich hohe Kopplung.
Also was man hier sieht, ist der Klient,
ruft anscheinend den Studenten auf. Der Aufruf,
also dieser Methodenaufruf steht mit einem Klienten drin und der ruft
den Ständen auf, lässt sich von dem einen Rekord geben.
Das hat der Student anscheinend irgendwie intern gespeichert. Das wird benutzt. Dann,
in dem Rekord gibt es ein Exam-Entry, das lässt man
sich von dem Rekord geben und dann benutzt der Klient dieses Exam-Entry.
Ihr seht, das ist eine hohe Kopplung,
der hier kennt diese ganzen Dinger
Und auch diese Zusammenhänge. Also wenn wir das travisieren von Objektgeflechten in einer Methode ausimplementieren,
koppeln wir die Methode sehr stark an die Struktur.
Und dadurch wird ihre Änderung erschwert. Das Law of the
Mieter ist auch kritisiert worden. Das ist die Mietersystem war
sehr datengetrieben modelliert. Und die haben also da sozusagen als
die Wolf's Brock mit ihrem Responsibility-Driven Design kann, sagt er sie,
oh, law of the Mieter, ganz schrecklich.
Ich finde es gar nicht so schlecht. Es ist tatsächlich eigentlich
eine gute Richtlinie. Es gibt inzwischen zeitgemäßere Anpassungen von diesem
Konzept. Also diese Idee von, if you delegate,
delegate fully, also elegiere deine Tätigkeit an deine eigenen Aufgaben.
Wenn es geht, I don't much your delegate objects, objects.
Das heißt also, wenn ich irgendwie Objekte habe,
mit denen ich zusammenarbeite,
dann sollte ich nicht Nachrichten an Objekte schicken oder Operationen aufrufen von Objekten,
die da noch wieder dranhängen.
Don't be aware of house home object works,
don't work at a low level than is necessary.
Das ist eine ganz gute Richtlinie. Das war dieses Beispiel,
was wir am Anfang gesehen haben mit der Vormerkkarte.
Im Service wäre es möglich gewesen, bei der Vormerkkarte in
die Datenstruktur reinzufummeln und da irgendwie selbst zu suchen.
Oder man sagt, im Service, hey,
Vormerkkarte ist bei dir noch Vormerken möglich
Tell, don't ask. Sage der Karte, dass sie mir jetzt irgendwie
die Informationen geben sollen, nicht wühle selbst drin rum.
Auch beim Einhalten des Law of the Metaheld immer wieder
diese Frage, wer sollte für diese Aufgabe zuständig sein?
Also tatsächlich da im Fall sehr hilfreich. Das heißt,
wenn ich jetzt das hier etwas besser machen will, dieses Juden,
diese ganze Kette, sollte ich eigentlich sowas haben, naja,
ich möchte bei dem Studenten, was will denn der Klient tun?
Er will ein Ergebnis einer Note eintragen. Na gut,
dann macht er sich irgendwie, gibt es vielleicht irgendwie ein Objekt,
dass diese Noten von der ganzen Klausur enthält.
Und dann reiche ich dem Student Das ist ja hier nur
die Modellierung am Programm. Ihr wisst, wie ich das
gemeint ist. Dann wird dem Studentenobjekt hier das Ding reingereicht.
Und der Student kann natürlich selber seine eigene Matrikelnummer,
ist wahrscheinlich schnell gespeichert. Das heißt, er kann das selbst
da aus dem Exame rausholen und bei sich selbst eintragen.
Und wie ist da bei dem Studenten eingetragen ist, will der Klient gar nicht wissen.
Man kann das also sehr viel besser kapseln. Auch hier zum Beispiel,
der ist ein bisschen illustriert,
bin ich einen Satelliten habe und er hat Antriebsdüsen und frage ich den Satelliten nicht nach,
gib mir mal deine Antriebsdüse, so jetzt Düse,
du gibst mal mehr Gast,
sondern eigentlich will ich ja als Klient möglicherweise einfach Satelliten neu ausrichten.
Also sage ich dem Klienten, richte dich neu aus.
Dann muss er halt diese Operation auch anbieten. Also ja,
Getter & Zetter sind auch häufig als Verstoß gegen das Low
auf die Mieter benannt worden. Gut, dann kommen wir
jetzt noch in den letzten zehn Minuten auf das Thema Kohision.
Das zweite große Thema. Das ist einfach, ja,
der Grad der Aufgaben, die eine Software einhat, zu erfüllen hat.
Idealerweise sollte jede Softwareeinheit nur genau eine Aufgabe erfüllen.
Habe ich schon gesagt, Entwurf nach Zuständigkeiten.
Sollte klar sein, wofür sie da ist.
Wenn eine Einheit für genau eine logische Aufgabe zuständig ist,
dann haben wir eine hohe Korrision
Der Begriff kommt aus verschiedenen Kontexten, also in der Chemie und Physik
, gibt es so diese Idee des irren Zusammenhaltes.
Also das ist ganz, ganz gut,
wenn so irgendwie ein Modul so irgendwie einen guten Zusammenhalt hat,
wenn die Dinge eng miteinander zu tun haben,
sollten sie auch zusammengehalten werden und da auch zusammen in einer Klasse formuliert werden.
Oder auch in der Linguistik eine sinnvolle Einheit.
Also möglichst hohe Kurision heißt, möglichst klar definierte Einheiten.
Das kann man runterbrechen auf Kurision von Methoden und Kurision von Klassen.
Hier ist mal so ein ganz einfaches Beispiel. Ich habe
eine trikonometrische Funktionsbibliothek. Da gibt es eine Operation Trigo.
Da gebe ich als ersten Parameter rein, welche trikonometrische Funktion berennt werden soll.
Und dann gebe ich denen Parameter rein. Für denen,
die Funktion angewendet werden sollen. Kann man so machen.
Also irgendwie gibt es hier diesen enormen Sinus, Kursinus, Tangents.
Das ist niedrige Korrision, weil da irgendwie ganz schön viel passiert.
Während das hier sehr viel klarer ist,
weil tatsächlich klar ist, diese Operation bringt den Sinus,
wie sie den Kursinus online Sowas kann man machen,
wenn man gezwungen wird, kleine Schnittstellen zu haben.
Wenn es irgendwie in Betten, der Projektleiter sagt,
so, unsere Klassen dürfen höchstens zehn Methoden haben.
So, dann fangt ihr an, oh Gott,
ich habe aber hier irgendwie mehr davon.
Na, da muss ich halt irgendwie anfangen,
die zusammen zu quetschen in einzelne Methoden, vollkommener Quatsch.
Das senkt die Korrision dieser Methode. Kollision von Klassen hier
ist zum Beispiel, was ich aus einem Buch habe,
also irgendwie Public Class Programm und dann sieht man hier lauter verschiedene Operationen,
die ganz unterschiedliche Dinge tun.
Also wenn da ganz viel zusammenkommt,
was nicht miteinander offensichtlich zusammenhängt,
dann hat man was mit niedriger Hochkollis
Etwas eine sehr klare, hohe Porision hat,
dann ist das sowas wie das Beispiel hier, Klaas Film.
Also das modelliert einen Film und genau die Zuständigkeit
ist Informationen über einen Film zu liefern. Okay,
das kann man auch durch geeignete Bezeichnungen ganz gut mit hinkriegen.
Hohe Kollision zeigt sich unter anderem daran, dass geeignete Bezeichner
gewählt werden können. Wenn ich etwas nicht gut benennen kann,
ist es ein Zeichen, dass es möglicherweise keine gute Korision hat.
Immer dann, wenn ich Unbezeichner habe,
sowas wie Einfügen und Berechnen oder Starter und Vermittler oder irgendwie sowas,
wenn ich so einen Namen wählen muss und und,
puh,
ist schon schlecht
Werden da zwei Dinge gemacht in einer Einheit. Ist meist
nicht so gut. Also unsere Start-up-Klassen in unserem System,
die wir hier in SE2 verwenden, die machen halt nur das Start-up.
Als extreme Ausprägung gibt es dann diese Richtlinie Schreibe-Quertex so,
dass der Kunde ihn auch lesen kann.
Wenn ich in einer Versicherung arbeite, dann wäre es cool,
wenn ich mit den Leuten aus der Fachabteilung eigentlich meinen Quelltext zeigen kann und sage,
guck mal,
hier ist der Versicherungsvertrag und der hat die Beziehung zu denen und die Laufzeit und so.
Das ist eine extreme Ausprägung. Ich kann mal die Leute
nicht wirklich in die Programmierung mit einbeziehen. Eine Frage,
die gern diskutiert wird, ist, Englisch oder deutsche Begriffe im Quelltext?
Englische Methodennamen, englische Klassennamen. Habe ich mehrfach schon
von Kollegen die Frage bekommen, so auf Konferenzen,
sag mal, wie haltet die das überhaupt?
Lasst ihr eure Ständen immer nur englische Sachen schreiben?
Wir machen immer nur Englisch. Es ist tatsächlich so viel cooler,
Englisch, klar. Java ist ja selber auch Englisch,
also schreiben wir auch alles Englisch. Wir sind an der Uni,
klar, alles Englisch und es ist auch internationaler.
Ist natürlich ein bisschen polemisch, diese Folie. Es ist tatsächlich kontextabhängig. Das heißt,
in jedem Projekt ist das anders zu beantworten möglicherweise,
wenn ihr in einem Kontext arbeitet,
wo ihr zum Beispiel bei einer Versicherung verentwickelt.
Und die haben so Begriffe wie Vertragslaufzeit, Vertragsnummer,
Kundennummer, Kunde, dann würde ich jetzt nicht anfangen,
diese ganzen fachlichen Begriffe in meiner Software Englisch zu machen.
Das ist total künstlich. Wenn die Fachlichkeit deutsch ist,
dann nehme ich deutsche Bezeichner. Wenn die Fachlichkeit aber
Englisch ist, dann nehme ich natürlich englische Bezeichner.
Also ein Forschungsprojekt über Robotik, mit internationaler Beteiligung,
da werde ich natürlich auch die fachlichen Klassen Englisch spenden.
Dann wäre es ja doof, wenn ich das irgendwie deutsch machen würde.
Es kommt also auf den Kontext an. Und das führt
tatsächlich dazu, dass man das auch mischen kann.
Und dass das vielleicht sogar gut ist
Gemischt ist. Ich kann nämlich dann in einem deutschsprachigen,
fachlichen Kontext direkt erkennen, ist das jetzt eine fachliche Phase,
sie ist Deutsch benannt oder es ist eine technische Klasse,
es geht nur irgendwie um Gui-Layout oder ähnliches.
Man sieht das auch hier, dass das durchaus gemischt sein kann,
sowas wie Bearbeiten, Button, naja klar,
das ist technischen Button, aber fachlich geht es darum,
dass das Bearbeiten angestoßen wird. Get Medienart, ja,
Medienart ist das fachliche, Get ist einfach nur,
gib's mir ein Getter, Rückgabe, UI, UI,
User Interface, aber Rückgabe, da geht es darum,
dass bei einem Verleihen auch was zurückgegeben werden kann.
Also durchaus sinnvoll, das zu mischen,
dass es so ein Flickenteppich kann sogar
richtig sinnvoll sein
Konkretisierung ist möglichst genauer Bezeichner zu wählen. Das heißt,
man möchte, wenn man modelliert, häufig schon loslegen oder irgendwas programmieren,
dann versucht man möglichst einfach einen Anfangsentwurf zu machen.
Was allerdings diese Regel besagt ist, man sollte eigentlich von Anfang an möglichst genau hinschauen.
Also zum Beispiel bei der Mediathek, ich stelle irgendwann fest,
oh, ich brauche ja nicht nur das einzelne Album,
irgendeine CD, die ich da verleihen will in der Mediathek,
sondern die kann ja mehrfach da sein.
Also einfach physisch kann es ja mehrere Exemplare von
dieser CD geben
Dann muss ich mir eigentlich schon mit der Modellierung überlegen, dass die unterschiedlich heißen sollten.
Das eine ist CD-Album und das andere ist CD-Exemplar oder so, keine Ahnung.
So kann man das machen. Wenn man das aber schon früh so trennt und auch entsprechend modelliert,
also auch wirklich in der Modellierung den Begriffen unterscheidet,
es ist deutlich leichter, hinterher festzustellen, aha,
ich brauche die Unterscheidung gar nicht,
weil es gibt immer nur ein Exemplar,
dann kann ich automatisiert in der Software mit irgendwelchen Tools die Dinge zusammenschmeißen.
Dann kann ich einfach sagen, ich identifiziere das.
Schwieriger ist es, wenn ihr es auseinandernehmen müsst, später im Endeffekt
Da müsst ihr nämlich euch jede Stelle angucken,
an dem der Begriff verwendet wird und sagen,
ist es jetzt dieser oder ist es dieser?
So diese Idee von Verschmelzen ist leichter als Aufteilung.
Okay, hallo. Dürfen wir einmal kurz dazwischen fucken.
Wir wollen ja vorher versammelt und morgen ankündigen. Ja,
bitte ganz schnell. Ich bin nämlich auch knapp mit der Zeit heute. Gut,
und morgen, 14 Uhr, im Automarkt gibt es zwischen ländlichen Leuten,
da kommt die letzte Vollversammlung, dreht es sich darum,
was wir in der Schulter bekommen,
bringt sich hervorragend und selber verrückt zu wollen.
Wir müssen uns aber darüber verständigen,
dass nichts davon bringen kann, oder den jetzigen Finanzierungsbedingungen überhaupt
nicht ist
Übergang mit Venture zu Master zu 100 Prozent zu machen.
Das heißt ja zum Beispiel, mehr Wasserschwimmen,
was man zu schaffen holt und alles Mögliche.
Alles, was gelöblich zu ist, kostet da mehr Geld. Real,
das Problem der Schuldgerätsel in der Stadt, im Land,
da haben wir jetzt eine Sache Sparpolitik für Europa und weltweit.
Und die führt zu großen Problemen. Klar,
auch was Universitäten angeht, die überhaupt das ganze Leben.
Wir müssen uns darüber verständigen, wie wir uns damit auseinandersetzen.
Im kranken Zukunft haben wir im letzten Semester schon einiges in
dieser richtigen Unternommenheit, darüber auch eine neue Kultur der
Solidarität herausgebildet
Haben auch einiges erreicht in der Auseinandersetzung mit der Staffelbüro.
Das beläuft sich aber bisher eher auf die Ebene von Verschlechterungen zurückdrängen.
Und das ist überhaupt nicht dem angemessen, worum es geht.
Ich habe zwei Sachen kurz festmachen. Ja, es ist weniger Zerstörung,
immer noch Zerstörung. Zweitens. Ich muss es darum gehen,
dass die Bildung und Wissenschaftler zu beitragen,
dass die gesellschaftlichen Jesus tatsächlich überwunden werden kann.
Was zum Beispiel heißen mag, wie man jetzt die hypokonomischen...
Sorry, mach nicht zu lang, bitte.
Es ist klar, Vollversammlung ist es wichtig
Die Leute, die wissen, glaube ich, alle,
dass es um dieses Thema geht, nicht so lange.
Die Ankündigung, wann, wo, morgen, bitte.
Aber jetzt sage ich kurz was zum Problem. Nein.
Es geht ganz schnell. Das ist das richtige. Ja,
du möchtest doch vorstellen, dass alle da hingehen und sich beteiligen.
Wenn ich jetzt frage, wer kommt,
melden sich drei Leute und ich will zu diesem Problem,
es geht ganz schnell, irgendwie schneller,
als wenn wir uns jetzt darauf verständigen,
ob ich das so.
Es gibt kein Problem und das besteht darin,
dass weit verbreitet ist, die Auffassung,
dass die Aufklärung wenig bringt
Von diesem Hintergrund wäre nicht erklärbar, wieso die Uni
noch hier ist, wo sie aber weg sollte.
Diesem Hintergrund ist nicht erklärbar, wieso die Studie geführt,
abgeschafft werden. Das hat nicht der ist gewesen,
sondern die Bewegung. Darüber müssen wir uns verständigen,
denn anders können wir auch nicht wirklich entwickeln,
wie denn Wissenschaften insgesamt dazu beitragen sollen, dass die Welt eine Lebenswerte wird.
Deswegen grüßen wir uns das Erfolg und verständigen, wie wir
dazu was kommen. Auch diejenigen, die jetzt meinen,
das ist ärgerlich, dass so viel zerstört wird,
aber leider, leider kann man daran noch was tun.
Diese Auffassung schützt ja das Problematische, nämlich diese Schuldrettern.
Das sollten wir uns selber nicht anziehen als Schuld
Guten Morgen, Fatze, Uhr, Odyvers. Alles klar, danke. Okay,
ich wollte noch ein paar Minuten was sagen zum Thema Code-Doplizierung.
Das ist eigentlich auch ein Thema, da könnte ich
eine halbe Stunde drüber reden, gar keine Frage.
Code-Doplizierung ist ein ziemliches Übel. Das ist einfach die Bezeichnung für
den Umstand, dass im Quelltext an mehreren Stellen dasselbe steht.
Wir hatten eben das Beispiel mit diesem Gradberechnen eines Knotens,
wenn das an mehreren Klienten steht, Ist das nicht gut?
Es ist gut, wenn es an einer Stelle steht.
Kultupizierung ist tatsächlich deshalb auch besonders problematisch, weil es meist nicht erkennbar ist.
Ich habe jetzt diese Berechnung des Grades an dieser einen Trailer programmiert.
Und ich weiß nicht, an wie vielen anderen Stellen es auch noch programmiert ist.
Also wie viele andere Klienten das auch noch getan haben.
Das ist relativ ärgerlich, wenn dann irgendetwas sich ändert.
Wenn ich also jetzt feststelle,
es ändert sich was an dem Knoten an der Implementation,
dann muss ich jetzt alle Klienten stellen,
die das benutzt haben, abklappern.
Die sind aber nicht irgendwie findbar. Das ist ein
ziemlich unangenehmes Problem. Das heißt,
wenn ich an einem Dublikat was ändere,
muss ich eigentlich
auch alle anderen Dublikate ändern
Das ist hier im Zusammenhang mit Korrision diskutiert,
weil das natürlich ein Zeichen von niedriger Korrision ist.
Wenn zwei Einheiten dieselbe Aufgabe, die Dinge ist bei
mindestens einer von beiden, die Zuständigkeit falsch zugeordnet.
Okay, dann gibt es noch den Hinweis,
möglichst große Einheiten, also Großeinheiten möglichst zu vermeiden.
Ein bekanntes Antipattern, die Gottklasse. Das ist für alles
zuständig und hält 90 Prozent des Quelltextes.
Es gibt Leute, die schreiben solche Software, Methoden,
die mehrere Bildschirmseiten lang sind, sind schlecht wartbar.
Klassen mit mehreren hundert Methoden und Exemplarwaren sind meist zu groß.
Auch das ist wieder Hinweis auf niedrige Korrision. Wenn einer
auf einer halt sehr viele Aufgaben erfüllt, erfüllt sie vermutlich zu viele Aufgaben.
Okay, was ich nochmal eben einen Hinweis geben wollte,
ist hier, kurz vor Schluss, zum Nachlesen, die Solid-Prinzipien.
Das ist von Robert See Martin. Sind das mehrere Entwurfsprinzipien
für Objekte und Systeme? Und die könnt ihr einfach im
Netz gut finden. Verschiedene Prinzipien, die dann auch teilweise
das widerspiegeln, was ich jetzt hier gesagt habe.
Also Single Responsibility Principle, ja, ist genau Korrision.
LizCov Substitution Principle, da geht es vor allen Dingen darum,
dass ich Subtypen überall da einsetzen können, sollte,
wo Supertypen erwartet werden. Interface segregation principle ist eigentlich das,
was wir gemacht haben mit dem Interface, was wir rausgezogen haben,
wenn zwei Klassen typisch abhängig sind oder bei Abhängigkeit da ist,
der eine benutzt die, dann sollte ich eigentlich die Schnittstelle,
die der hier benutzt, in einem Interface beschreiben.
Weil dieses Interface kann kleiner sein als die ganze Schnittstelle der Klasse.
Das sind alles Dinge, die letztendlich auch zielen,
die Kopplung zu minimieren und die Provision möglichst zu erhöhen.
Okay, also das einfach heute alles zum Thema Klassenentwurf.
Also es geht um Wartbarkeit, Portierbarkeit, ganz wichtig.
Wie macht man das her, indem man einen guten Klassenentwurf baut?
Es gibt ein paar Richtlinien und insbesondere sollte man auf eine
hohe Kurision achten und eine niedrige Kopplung zwischen den Bestandteilen eines Systems.
Das war heute der Schnelldurchlauf, ich bin so ein bisschen gehetzt gewesen,
weil ich dachte, es wäre heute um Punktviertel vor Schluss,
weil irgendwelche anderen Leute hier reinkommen wollen.
Das ist nicht der Fall. Wenn noch Fragen sind und wir
nicht rausgeschmissen werden, können wir die jetzt noch besprechen.
Geht es noch Fragen jetzt zu dem, was ich erzählt habe?
Nein, gibt es nicht, dann vielen Dank