Einen schönen guten Tag, meine Damen und Herren.
Ich begrüße Sie zur Vorlesung Softwaretechnik 1.
Heute habe ich ein zweites Mal die Ehre, Herrn Tichy zu vertreten.
Er kommt heute, just in diesem Moment, ist er im Flieger und kommt
zurück.
Also nächste Woche Montag dürfen Sie ihn wieder persönlich hier vorne
begutachten.
Schade, okay.
Okay, ich habe Ihnen wieder ein Rätsel mitgebracht.
Schauen Sie mal hier hin.
Wir haben hier unsere klassische Main-Methode, die also von der
Commanderzeile aus aufgerufen werden kann.
Und wir haben hier vorne vier Initialisierungen der Klasse BigInteger,
jeweils mit unterschiedlichen variablen Namen.
Einmal initialisieren wir das mit 5000, einmal mit 50 und einmal mit
500, jeweils 1000 und die letzte hier mit dieser Konstante 0.
Und die letzte Summe hier unten nehmen wir dann und führen darauf dann
die Methode add aus, auf den jeweils zuvor initialisierten Variablen
und dann drucken wir die Summe hier unten aus.
Und die spannende Frage ist natürlich, was kommt da raus?
Okay, wir haben hier immer die Optionen Übersetzerfehler,
Laufzeitfehler.
Wir haben die Option, es kommt 0 heraus, also nur das Initialisierte
hier unten.
Wir haben die Option, es kommt 500.000 heraus, also nur die letzte
Addition hier unten hat funktioniert.
Wir haben die Option, 5000 kommt heraus, also das hier oben hat dann
augenscheinlich funktioniert.
Und wir haben natürlich noch die Option, dass es tut, was es soll.
Es kommt also 555.000 raus, wenn wir auf 0 diese drei Zahlen addieren
und dann tatsächlich 555.000 rauskommt.
Dann streichen wir die letzte Option mal, weil das Erwartete kommt ja
nie raus.
Also 555.000 kommt nicht raus.
Wer denkt, dass ein Laufzeitfehler kommt?
Wer denkt, dass hier ein Übersetzerfehler geschieht?
Wer denkt, dass 5000 rauskommt?
Ich bin ja gespannt.
Wer denkt, dass 500.000 rauskommt?
Ganz schwaches Verhalten, Summen.
Wer denkt, dass hier 0 rauskommt?
Oh, sehr deutlich, okay, das scheint sehr eindeutig zu sein.
Warum?
Also wenn ja, warum?
Okay, der Hinweis war, bei der Initialisierung hier oben benutzen wir
Anführungszeichen, das würde diese 5000 hier nicht zu einer Zahl
machen, sondern zu einer Zeichenkette, zu einem String.
Und das würde hier also zu sehr seltsamen Effekten führen.
Aber Sie haben sich jetzt bei der 0 gemeldet.
Das heißt, die 0 machen Sie deshalb, weil das hier unten nach einer
sauberen Initialisierung aussieht.
Aber wenn das hier bei Ihnen so seltsam aussieht, müsste das hier oben
dann nicht ein Laufzeit- oder ein Übersetzerfehler vielleicht auch
werfen?
Oder Sie denken, da passiert irgendwas ganz seltsames?
Okay, also 0 ist richtig, Erklärung ist leider falsch.
Also das funktioniert so, man kann Big Integers so initialisieren.
Das klappt so, da oben ist eine Meldung.
Das war der richtige Hinweis.
Der Hinweis war, wenn Sie auf einem Objekt vom Typ Big Integer, wie
hier die Summe, die Methode add aufrufen, dann addiert diese Funktion
zwar zu dem, was in der Summe drin steht, dieses Objekt hier drauf,
aber das Ergebnis wird nicht auf die Summe dann drauf addiert, sondern
zurückgeliefert.
Sie bekommen also hier vorne eigentlich ein neues Big Integer-Objekt
zurück, das die Summe aus den beiden enthält, aber das ursprüngliche
Objekt wird nicht angefasst.
Man nennt diese Klassen, die so etwas tun im Allgemeinen in Java,
immutable.
Immutable Klassen haben die Eigenschaft, dass sie sich selbst nicht
ändern, wie der Name schon sagt, sondern dass sie, wenn der Inhalt der
Klasse des Objektes irgendwie geändert werden soll, dann liefern sie
das Geänderte als Ergebnis der Operation zurück.
Und das hier war auch schon der Reparaturversuch dazu.
Wenn Sie also 555.000 bei diesem Beispiel hier rauskriegen wollen,
müssen Sie also hier vorne das Ergebnis abgreifen, das könnte auch ein
neuer Big Integer sein, das muss nicht der gleiche wie hier sein, und
jeweils darauf wieder addieren und dann am Schluss die Summe
zurückgeben.
Nur so bekommen Sie auch tatsächlich immer die Folgesumme auf einem
Objekt vom Typ Big Integer.
Haben Sie das Gefühl, das ist in Ihrem Gehirn gesickert, was hier die
Fehlerursache war?
Gut, sehr schön.
Also achten Sie darauf, wenn Sie Immutable Klassen vor sich haben, was
die Methoden dort tun.
String ist übrigens auch eine Immutable Klasse.
Was immer Sie auf einem String machen, liefert ein neues String-Objekt
zurück.
Das nur so als Hinweis und das sollte die Lehre des heutigen Rätsels
sein.
Möchten Sie, bevor wir loslegen, noch eine kleine Anekdote, einen
Schwank aus meiner Vergangenheit haben?
Sehr schön.
Es freut mich, dass sich so viele junge Menschen für meine
Vergangenheit interessieren.
Das muss ich mir gleich aufschreiben.
Okay.
Also es geht ganz schnell.
Ich war jung und unschuldig und Werkstudent bei Web.de.
Und es ging darum, dass, Sie kennen sicher diese etwas nervige Werbung
von Web.de, die da immer kommt, mit diesem, machen Sie hier ein
Bankkonto auf, machen Sie dort ein Bankkonto auf und so.
Und als ich Werkstudent war, es ist schon länger her, da fing das
gerade so an mit diesen Bankwerbungen.
Und der Punkt ist, die Banken hatten für diese Art von Werbung, dass
also ein Massenerreicher von potenziellen Kunden diese Werbung
verteilt, damit hatten die Banken noch nicht so besonders viel
Erfahrung.
Und das läuft also im Allgemeinen dann so ab.
Die Bank kommt zu Web.de, kam her und hat gesagt, hallo, wir würden
gerne Werbung beischalten.
Oder Web.de kam zu Ihnen und hat gesagt, Jungs, wir schalten für euch
Werbung, wie auch immer.
Es wurde ein Vertrag geschlossen, dass am so und sovielten, um so und
soviel Uhr, für drei Tage am Stück auf dem ganzen Portal die Werbung
dieser spezifischen Bank geschaltet werden solle.
Und wie das so ist, beim Klick auf diesen Werbungsbanner und so, kommt
man dann auf die Kontoeröffnungsseite der Bank etc.
etc.
Und Web.de sagte also, unserer Erfahrung nach kommen so und soviel
Promille unserer Besucher auf den Seiten, dann bei Ihnen auf der Seite
an.
Also das war so eine Lastaussage.
Wenn wir so und so viele Millionen Klicks pro Stunde auf unseren
Diensten haben, dann rechnen Sie damit, dass Sie so und so viel
tausend pro Stunde bei Ihnen auf dem Server landen und dann so und so
viel hundert pro Stunde bei Ihnen diese Kontoeröffnungsanträge
ausfüllen würden.
Die Bank hat sich das angeschaut und hat gesagt, aha, ist schön, das
glauben wir erstmal nicht.
Außerdem, wenn wir das befolgen würden, dann müssten wir unsere
Serverkapazitäten, um diesem Ansturm gerecht zu werden, erstmal
verdreifachen oder so.
Naja, der Tag X und die Stunde X kam und Sie ahnen schon, was
passierte.
Etwa eine Dreiviertelstunde, nachdem also die Werbung online ging,
klingelte in der IT recht hektisch schon am Klingeln zu hören das
Telefon und dran war also die andere Bank und schrie Hilfe, wir haben
einen DDoS-Angriff, helft uns mal.
Das war wirklich O-Ton.
Wir haben hier einen Angriff, der kommt von euren Diensten irgendwie
weitergeleitet und unsere Server krachen zusammen und wir kriegen das
nicht mehr hin.
Alles klar, kleinen Moment, wir schauen mal, was los ist.
Halbe Stunde später der Rückruf aus der IT, das ist kein DDoS-Angriff,
das sind Hunden.
Es kam noch die kleine juristische Feinheit hinzu, dass der Vertrag,
der abgeschlossen wurde, nicht zuließ, rein juristisch, die Werbung
vorzeitig wieder abzulehnen.
Sie also denken, was da an Blutdruck entstand bei der Bank in deren IT
-Abteilung in dieser Zeit.
Und ein halbes Jahr später kam dieselbe Bank dann wieder und hat bei
Web.de dann Beratungsleistung dafür eingekauft, wie sie ihre Server zu
dimensionieren haben für die neue Werbeaktion.
Das war's schon im Rahmen von Anforderungsanalyse und sowas.
Okay, gehen wir wieder zurück zur Softwaretechnik.
Wir waren letzten Mittwoch bei der Abstrak...
Wenn hier von abstrakter Maschine die Rede ist, dann versuchen Sie das
bitte nicht allzu technisch zu sehen.
Mit Maschine ist hier ein softwaretechnisches Abstraktionskonstrukt
gemeint, nicht im Sinne von etwas, was Sie mit Strom betreiben und
dann läuft das Ganze, sondern es ist eben gemeint als ein Mechanismus,
der Ihnen abstrakt einen darunterliegenden technischen Aspekt verbirgt
und mit einer generischeren Schnittstelle bereitstellt.
Und das geschieht eben häufig mit so einer API, mit einem Application
Programming Interface.
Beispiele für abstrakte Maschinen gibt es viele.
Die Java VM kennen Sie schon, die versteckt mithilfe der Byte-Sprache,
die Ihnen die VM zur Verfügung stellt, die darunterliegenden Details
der Maschine.
Sie abstrahiert also von der konkreten Architektur darunter.
Das Betriebssystem können Sie als eine andere Variante von abstrakter
Maschine ansehen, das durch die Systemaufrufe, die es Ihnen zur
Verfügung stellt, eben auch von der konkreten darunterliegenden
Maschine abstrahiert und Sie damit Ihr Programm eben auf dem gleichen
Betriebssystem, aber auf unterschiedlichen Rechnern laufen lassen
können, weil das Betriebssystem eben entsprechend von der Hardware an
der Stelle abstrahiert.
Genau, die Java Maschine hatten wir gerade schon erwähnt.
Hier liegt die Abstraktion eben nicht auf der zugrunde liegenden
Hardware im Sinne des Betriebssystems, sondern im Sinne von zwischen
den betriebssystemspezifischen Aufrufen und dem Bytecode, den Sie
ausführen können, liegt eben hier die VM, die Ihnen den Bytecode auf
die entsprechenden Aufrufe des Betriebssystems übersetzt.
Ein anderes Beispiel, was wir noch nicht angesprochen hatten, waren
hier unten diese Makros.
Damit ist gemeint, mit Makro ist gemeint, dass Sie einen Baustein von
Programmierquelltext irgendwo haben und dieser Baustein, den haben Sie
irgendwo hingelegt und den benutzen Sie nun einfach mehr oder weniger
blind.
Das könnten Sie auch als eine Art von abstrakter Maschine betrachten,
weil dieses Makro vielleicht einen gewissen technischen Aspekt vor
Ihrem Programm wegkapselt, versteckt oder so.
Das ist aber nur bedingt dazu geeignet, weil Makros eben aus
technischer Sicht trotzdem Ihr Programm sind und Ihr Quelltext sind
und Sie dort eben solche Unschönheiten haben, wie dass Nebeneffekte in
Ihrem Programm auch Ihr Makro stören könnten.
Und eine abstrakte Maschine sorgt eben genau dafür, dass Ihnen solche
Dinge nicht passieren können.
Genau.
Ein anderer Begriff aus dem Bereich der Architekturstile ist die
Produktlinie bzw.
die Programmfamilie.
Das war, glaube ich, die Folie, mit der wir konkret aufgehört hatten
am letzten Mittwoch.
Es geht hier darum, dass wenn Sie ein Produkt haben und davon
Varianten anbieten möchten in unterschiedlichen Geschmacksrichtungen,
dann sind das in der Regel Produkte der gleichen Produktfamilie, die
Sie eben in unterschiedlichen Ausprägungen Ihrem Kunden anbieten.
So ganz typische Produktfamilien ist von Microsoft die Windows- oder
die Office-Linie oder so, die es in diesen verschiedenen auf- oder
abgespeckten Versionen kaufen können, aber das gibt es auch von
anderen Herstellern in verschiedenen auf- oder abgespeckten Versionen.
Das hat teilweise technische Gründe, weil ein kleiner Kunde vielleicht
nicht die riesengroße Mega-Enterprise-Version installieren will.
Das hat teilweise aber auch Marketing- und verkaufstechnische Gründe,
weil vielleicht sich die Kunden überlegen, dann doch das teurere
Produkt zu kaufen und dafür 50 Euro mehr Arbeitsplatz auf den Tisch zu
legen.
Genau, was möchte man hier tun?
Aus softwaretechnischer Sicht, naja, mit einer Produktfamilie möchten
Sie als Entwickler die Gemeinsamkeiten der Produkte auch gemeinsam
pflegen.
Sie möchten ja nicht für die Klein-Office und mittlere Office und die
Groß -Office-Variante Ihren Anwendungskern dreimal pflegen, sondern
Sie möchten den nur einmal pflegen und das können Sie mit einer
Produktlinie an der Stelle tun, indem Sie eben Ihre Entwürfe und
Spezifikationen und die Komponenten, die Sie in Ihrem Anwendungskern
immer brauchen, in egal welcher Variante der Familie, eben
wiederverwenden können.
Die Mitglieder einer Produktfamilie können sich extern und intern
unterscheiden.
Mit extern ist gemeint, was der Kunde sozusagen von außen sieht, was
man von außen der Software ansehen kann.
Und das wäre zum Beispiel so etwas wie, Sie haben eine Produktfamilie,
die sowohl unter macOS als auch unter Linux als auch unter Windows
laufen würde.
Das wäre eine Form von Produktfamilie, die im Kern das Gleiche tut,
aber nach außen hin haben Sie eben an der richtigen Stelle
Abstraktionen eingebaut, sodass derselbe Anwendungskern auch auf
unterschiedlichen Betriebssystemen laufen kann.
Möglicherweise unterscheiden Sie sich im Format der Ein- und Ausgabe,
das wäre möglicherweise eher so ein marketingtechnischer Grund.
Die Kleinoffice-Variante kann eben mit bestimmten Dateiformaten, die
im Großfirmenbereich üblich sind, nicht umgehen.
Wenn man die haben möchte, muss man sie dazu kaufen beispielsweise.
Und der dritte Punkt gehört auch in diese Aufzählung da mit rein.
Sie können sich natürlich auch intern unterscheiden, wenn es zum
Beispiel darum geht, dass Sie die gleiche Anwendung zwar auf dem
gleichen Betriebssystem, aber vielleicht auf unterschiedlich fähigen
Rechnern ausführen lassen möchten.
Spontan fällt mir ein, Sie möchten eine App schreiben und die eine
möchte auf dem Smartphone laufen und die andere möchte auf einem
Tablet laufen.
Dann haben Sie da wahrscheinlich große Teile Ihrer Anwendung
gemeinsam, aber Sie möchten wahrscheinlich auf einem Tablet eine etwas
andere Bedienoberfläche haben, die Knöpfe ein bisschen anders
anordnen, die Größen ein bisschen anders anordnen, ein bisschen
reichhaltiger sein und auf dem Telefon vielleicht nicht.
Dann könnten Sie auch hier überlegen, das mit einer Produktlinie zu
machen.
Die Begriffe allgemein und flexibel.
Was ist der Unterschied?
Allgemein heißt, Sie haben Ihr Programm so entworfen und programmiert,
dass es sofort und instantan in sehr, sehr vielen Fällen so
funktioniert, wie das gewünscht ist, ohne dass es angefasst werden
muss.
Oft haben Sie dann, als Sie entworfen haben, sind Sie bereits
gedanklich die verschiedenen Varianten durchgegangen, in welchen
Szenarien das Programm möglicherweise eingesetzt werden könnte.
Sie haben es eben so geschrieben, dass es in sehr, sehr vielen
Szenarien sofort einsetzbar ist.
Flexibel ist eine Eigenschaft des Entwurfs.
Flexibel ist ein Entwurf dann, wenn sie, nachdem der Entwurf schon mal
steht, auch danach wieder geändert werden kann, ohne dass es allzu
viele Teile betrifft.
Sie haben ja auf dem aktuellen Übungsblatt da ein schönes Beispiel
dazu.
Allgemein und flexibel.
Die allgemeinen Überlegungen, wenn Sie einen Entwurf flexibel haben,
dann erleichtern Sie dadurch nicht nur die Änderung, wenn nachdem der
Entwurf einmal umgesetzt wurde, nochmal geändert werden sollte,
sondern Sie erleichtern auch die Wartung, wenn ein Kunde eine neue
Funktionalität haben möchte, wenn Sie eine Sicherheitsaktualisierung
brauchen, wenn Sie andere Arten von Aktualisierungen brauchen, dann
erleichtert Ihnen ein flexibler Entwurf an der Stelle einfach das zu
tun.
Und möglicherweise haben Sie auch, Stichwort Kapselungsprinzip,
geschickterweise die Entwurfsentscheidung, die Sie nun ändern müssen,
schon hinter einer Schnittstelle verborgen, sodass Sie am Entwurf gar
nicht mehr viel machen müssen.
Dann haben Sie einen schönen, flexiblen Entwurf an der Stelle erzeugt.
Ja, was gibt es so im Allgemeinen für Architekturstile?
Es geht jetzt hier um eine sehr, sehr grobkörnige Betrachtung, wie Sie
Ihre Anwendungen ganz grob anlegen können.
Sie können zum Beispiel, wir schauen uns das alle gleich im Detail an,
Sie können zum Beispiel mit einer Schichtenarchitektur arbeiten.
Eine Schichtenarchitektur, weil die Definition hier so schön ist, lese
ich die mal vor, besteht aus einer Menge von Softwarekomponenten mit
einer wohl definierten Schnittstelle, nutzt die darunterliegenden
Schichten und stellt...
Sie können sich das also sehr grafisch schon als Schichtenmodell an
der Stelle vorstellen.
Wie sieht sowas konkret aus?
Na ja, zum Beispiel so, Sie haben hier unten eine Basisschicht.
Charakteristisch für diese Schicht ist, dass es keine Schicht unter
ihr gibt, die diese Schicht benutzt, sondern dass es die Basisschicht,
die die ersten möglichen Dienste bereitstellt.
Dann gibt es hier eine Schicht 1, die Dienste der Basisschicht hier
unten benutzt, also hier diese Benutzrelation B ist das Interessante.
Und dann gibt es möglicherweise noch eine Schicht darüber, die genau
das gleiche mit der Schicht B macht.
Warum ist dieser Pfeil C hier rot?
Das sehen Sie gleich.
Es geht darum, das haben wir glaube ich schon diskutiert, es geht
darum, dass Sie transparente und intransparente Schichten haben
können.
Das ist eine Entwurfsfrage, ob Sie das möchten oder nicht.
Dürfte ich Sie bitten, wieder ein bisschen ruhiger zu werden?
Ein klein wenig Gemurmel habe ich überhaupt nichts dagegen, aber ich
fühle mich langsam hier vorne gestört.
Das wäre ich Ihnen sehr dankbar, wenn Sie das ein bisschen
einschränken könnten.
Dankeschön.
Ja, wo war ich?
Es geht darum, Sie können Ihre Schichten im Schichtenarchitekturmodell
so entwerfen, dass die Schichten einander durchlässig sind, das ist
natürlich nur sinnvoll, wenn Sie mehr als zwei Schichten haben, oder
dass das nicht geht.
Sie erinnern sich eben an diesen roten Pfeil C, das hier unten war
unser A, das war unser B, also die Schichten können direkt auf die
unter ihr liegende Schicht zugreifen, aber sie können die Schicht B
zum Beispiel hier nicht überspringen, das war dieser Pfeil C eben aus
der Grafik.
Das wäre, wenn Sie eine intransparente Schichtenarchitektur verwenden,
wenn Sie nicht durchlässige Schichten haben.
Praktisch immer zum Schichtmodell gehört dazu, weil das ist so der
Kern des Schichtmodells an sich, dass Sie nicht nach oben rufen
können, also die unterliegendste Schicht, die Sie als solche definiert
haben, die ruft keine Funktionen aus darüber liegenden Schichten auf.
Das gilt auf jeder Ebene der Hierarchie und natürlich gilt das auch
für schichtübergreifend.
Also diese linke Seite ist für Schichtenarchitekturen immer Bubu und
die rechte Seite hier lassen Sie deshalb weg, weil wir jetzt im Moment
gerade eine intransparente Schichtenarchitektur haben.
Wenn wir eine transparente Schichtenarchitektur haben, Sie ahnen es,
dann ist dieser Pfeil in Ordnung.
Dann dürfen Sie auch von Schicht A mal über Schicht B hinweg auf
Schicht C zugreifen und Schicht B damit umgehen, aber auch hier, wie
gesagt, die linke Seite, das Fachwort ist Bubu, Sie dürfen das nicht
machen, weil sonst haben Sie keine Schichtenarchitektur mehr.
Warum möchte man das nicht?
Naja, wenn Sie das machen, erinnern Sie sich an letzte Woche, dann
führen Sie Zyklen ein in Ihre Benutztrelation.
Dann haben Sie das Problem, wie wollen Sie denn Schicht A oder Schicht
C testen, wenn die jeweils andere Schicht noch nicht da ist, wenn Sie
da getrennt entwickeln.
Sie haben ja aus irgendwelchen Gründen das Ganze hier als Schichten
eingeteilt.
Wenn Sie das eingeteilt haben, aber Sie können die Funktionalität
sowieso nur testen, wenn beide Schichten dann endlich funktionieren
und da sind, dann haben Sie Ihren Entwurf hier ein bisschen ad
absurdum geführt.
Genau, was hat die Schichtenarchitektur für Vorteile?
Naja, sie ist relativ gut vorstellbar, Sie haben klare
Abstraktionsschichten an der Stelle.
Ah genau, Sie können die Abstraktionsschichten auch als die virtuellen
Maschinen ansehen, die wir gerade eben hatten.
Jede Schicht stellt über eine wohldefinierte Schnittstelle eine
Funktionalität der darüberliegenden Schicht zur Verfügung und
abstrahiert von dem Kram, der in der Schicht stattfindet,
beziehungsweise darunter stattfindet.
Das wäre so eine Sichtweise auf die Schichten einer
Schichtenarchitektur.
Genau, Sie können sich entscheiden, ob Sie transparente oder
intransparente Schichten möchten und Sie können natürlich Schichten
einfach austauschen oder ändern oder hinzufügen und insbesondere auch
innerhalb einer Schicht den Entwurf oder die Implementierung ändern
und wenn die Schnittstelle zwischen den Schichten gleich bleibt, dann
interessiert das an der Stelle die anderen Schichten nicht und das
wäre an der Stelle möglicherweise ein flexibler Entwurf.
Welche Probleme haben Sie?
Naja, wenn Sie diese intransparente Geschichte machen, dass Schicht A
nicht Schicht B umgehen darf und direkt auf Schicht C zugreifen darf,
dann haben Sie möglicherweise Effizienzverluste, weil wenn das
Meerschichten sind und Schicht A muss auf Schicht K zugreifen, dann
haben Sie hier eben einiges an Aufrufen auf die jeweiligen Schichten
dazwischen, das kann ineffizient werden.
Andererseits sollten Sie sich überlegen, ob Sie das zulassen, denn die
Aufrufe sind zwar dann immer schön von oben nach unten, aber wenn jede
Schicht jede darunterliegende an jeder beliebigen Stelle aufrufen
darf, können Sie sich vorstellen, dass Sie sich vielleicht andere
Probleme einhandeln, denn jetzt müssen Sie, wenn Sie eine Schicht
ändern oder austauschen wollen, nicht mehr nur auf die Schnittstelle
zur direkt darüber liegenden Schicht Rücksicht nehmen, sondern
eigentlich zu allen darüber liegenden Schichten und auf diese
Rücksicht nehmen, denn wenn Sie transparente Schichten haben und sie
umgehen dürfen, dann greift ja potenziell jede darüber liegende
Schicht auf die Schicht zu, die Sie gerade ändern möchten.
Und natürlich müssen Sie irgendwie Schichten auch definieren können
und sich definieren lassen, das kann manchmal ein bisschen schwierig
sein, aber das hängt eben vom konkreten Problem ab, das Sie vor sich
haben und für das Sie einen Entwurf erstellen sollen.
Man liest immer mal wieder von einer 3- oder einer 4-Schichten
-Architektur, einer 3-Tier-Architektur, 3-Tier, 4-Tier, wie auch
immer.
Gemeint ist damit, dass Sie per Definition aus diesen drei oder vier
Schichten einen Entwurf gebaut haben, der vielleicht so aussieht.
Sie haben hier unten einen Anwendungskern, der die Logik bereitstellt,
dann haben Sie ein paar Grundfunktionen, die auf dem Kern arbeiten,
aber nicht von allen Anwendungsteilen gebraucht werden und dann haben
Sie oben möglicherweise die Benutzerschnittstelle.
Das ist nur so ein ganz generisches Beispiel.
Man findet das häufiger in weborientierten Diensten, dass Sie eben
ganz unten, als diese Kernschicht hier unten, die Datenbank haben, die
Ihre eigentlichen Daten verwaltet, dann haben Sie oberhalb der
Datenbank einen Dienst, der aus der Datenbank die präsentierbaren
Inhalte oder die zu präsentierenden Inhalte generiert.
Dann haben Sie einen Webserver, der diese Daten schließlich nach außen
hin ausliefert.
Möglicherweise gibt es mehrere Webserver, die auf einen und denselben
Inhalteerzeuger zugreifen und dann haben Sie schließlich einen
Webbrowser, der von dem Webserver die Inhalte bekommt und
herunterlädt.
Wie Sie hier schon sehen, eine drei- oder vier-Schichten-Architektur
heißt auf architektonischer Ebene nicht zwangsläufig, dass alle diese
Schichten auch von Ihnen programmiert werden müssen.
Das ist eine derart generische Sichtweise auf den Entwurf, dass Sie
hier unten eine Datenbank haben, die programmieren Sie vermutlich
heutzutage nicht mehr selbst und Sie benutzen ein Datenbanksystem, das
Ihnen geeignet erscheint und vermutlich programmieren Sie auch heute
Ihren Webbrowser nicht mehr selbst und Sie benutzen einen, der Ihnen
für Ihre Anwendung geeignet erscheint oder schreiben Ihr Programm
sogar so, dass Sie das gar nicht brauchen.
Das ist aber dann trotzdem eine drei- oder vier-Schichten-Architektur.
Was gibt es noch für Beispiele?
Betriebssysteme, offengestanden halte ich das für ein mäßig geeignetes
Beispiel, denn wie Sie vielleicht noch oder bald noch in der Vorlesung
Betriebssysteme erfahren werden, dort ist genau im Betriebssystem
diese Schwierigkeit mit dem klaren Einteilen der Schichten.
Dort ist es eben nicht immer ganz klar, zwischen welchen dieser hier
aufgezählten Komponenten tatsächlich eine echte Schichten- und
Benutzthierarchie herrscht, die also nur von oben nach unten zugreifen
oder ob vielleicht auch nachbarschaftlich zugegriffen wird oder sogar
auch wieder von unten nach oben gerufen wird.
Betriebssysteme sind heute noch so in der Art entworfen, aber fragen
Sie mal die Betriebssystem-Jungs, die haben da inzwischen auch schöne
andere Ideen an der Stelle und gehen von diesem Schichten-Modell
inzwischen etwas weg.
Die Protokolltürme, wenn Sie letzte Woche da waren, dann erinnern Sie
sich vielleicht an dieses ISO-OSI-Modell, was ich erwähnt hatte mit
der untersten Hardware-Schicht und ganz oben ist der Browser, der
Ihnen irgendwelche Inhalte abruft.
Wir hatten das im Zusammenhang mit Mail-Verschicken und sowas.
Damit ist dieser Protokollturm gemeint, Sie haben ganz oben die
Anwendung, die sehen Sie als grafische Benutzeroberfläche, da tippen
Sie Ihre Nachricht ein, die Nachricht wird dann zerlegt in
entsprechende Netzwerkpakete, Einzelteile, bis sie schließlich ganz
unten als Nachrichtensignal über den Ether, entweder drahtlos oder mit
Draht, wandert.
Aber ganz unten die Schicht, die sich darum kümmert, dass die
einzelnen Bits auf den Ether wandern, die weiß erstens nichts davon,
dass das oben mal eine Mail war oder dass da ein Mail-Programm oben
dran hängt und das Mail-Programm ganz oben hat auch keine Ahnung
davon, wie es die Bits, die es irgendwo erzeugt, unten auf den Ether
bringen soll.
Das ist genau der Sinn dieser Schichtenarchitektur dahinter, dass die
beiden Schichten an der Stelle nichts voneinander wissen müssen, aber
über diese Zwischenschichten eine definierte Übersetzung stattfindet.
Die Fassade ist ein Entwurfsmuster, das wir im nächsten Kapitel noch
etwas genauer besprechen.
Wenn Sie ein Subsystem haben, Sie haben einen Entwurf, das sieht
irgendwie so aus.
Und jetzt haben Sie mit diesem schwarzen Kästchen hier eine Untermenge
von Modulen ausgemacht, die Sie jetzt zu einem Subsystem machen
wollen, die Sie jetzt krapseln möchten.
Dann ist die Fassade das Werkzeug, was genau diesen Pfeil hier
darstellt.
Die Fassade schalten Sie nun als eine Art Tor, wenn Sie so möchten,
vor dieses Subsystem, das Sie identifiziert haben und zwingen nun per
Entwurf, dass alle Kommunikation von innen nach draußen und vor allem
auch alle Kommunikation von außen nach innen in das Subsystem über die
Methode, über die Schnittstelle der Fassade läuft.
So nennt man einfach das Entwurfsmuster, wenn Sie so etwas in dieser
Art betreiben möchten.
Ja, ein anderes Architekturmuster ist ein Client-Server.
Das könnten Sie auch, wenn Sie möchten, als zweistufiges
Schichtenmodell auffassen.
Es ist aber jetzt hier so ein Spezialfall, dass Sie eben auf der
rechten Seite hier einen Dienstgeber haben, der Ihnen gewisse Dienste
anbietet und auf der linken Seite eben einen Klienten, der diese
Dienste abfordert.
Mir fällt gerade ein, ich habe Sie letztes Mal ein bisschen zu wenig
gefragt, geht es Ihnen zu langsam?
Ist die Geschwindigkeit für Sie in Ordnung?
Gut.
Geht es Ihnen zu schnell?
Okay.
In der Statistik haben Sie ja gelernt, dass man die oberen und unteren
10 % tendenziell eher weglassen sollte und sich auf das Zentrum
konzentrieren sollte.
Gut, vielen Dank.
Ich werde in dieser Geschwindigkeit etwa weitermachen.
Wir haben Klienten und Dienstgeber und hier unten steht auch schon,
was ich eben erwähnte, Sie können das sozusagen als ein zweistufiges
Schichtenmodell auffassen.
Und das ist etwas, was Sie ganz oft in so Programmen finden, die in
einem, also ganz spontan fallen mir so Dinge ein wie ein
Warenwirtschaftssystem, das Sie in einem Büro irgendwo installiert
haben und da gibt es irgendwo einen zentralen Server, der nach außen
hin die Funktionen der Warenwirtschaft bereitstellt, Sie können also
Artikel einbuchen und entnehmen und Rechnungen erstellen und
Lieferscheine erstellen und solche Dinge und auf der kleinen Seite,
netzwerkmäßig verteilt und dezentral, haben Sie Arbeitsplatzrechner,
die ein Warenwirtschaftsklientenprogramm laufen lassen und auf diese
Funktionen zugreifen und Ihnen die Rechnung, die Sie erstellt haben,
darstellen und dann können Sie sie drucken und Sie können diesen
Artikel da einbuchen oder sowas.
Das ist so ein Beispiel für Klient-Dienstgeber-Programme.
Genau, auch könnte man das so sehen, dass der Dienstgeber einfach nur
eine Datenbank ist und der Klient greift direkt auf die Datenbank zu.
Auch das ist eine Form von Klient und Dienstgeber und meistens laufen
diese, wie ich eben schon erwähnte, netzwerkmäßig entkoppelt
voneinander getrennt.
Das muss aber nicht sein.
Es ist im Prinzip softwaretechnisch auch denkbar, dass auf der Kiste,
auf der Sie Ihren Dienstgeber laufen lassen, auch gleichzeitig der
Klient läuft und Sie eben aus anderen Gründen hier eine Entkopplung
haben wollten.
Ein klassischer FTP-Server ist auch so eine Klient-Dienstgeber
-Geschichte, wenn Sie so wollen.
Sie haben ein FTP-Client-Programm, das dient Ihnen dazu, Dateien
herunterzuladen oder auch hochzuladen zu einem FTP-Server und dort
findet genau das statt.
Der FTP-Server bietet Ihnen Dienste an, wie das Herunterladen und das
Hochladen der Datei mal im Wesentlichen, und das Klient-Programm kann
diese Dienste in Anspruch nehmen und diese Dateien übertragen zum
Server und zurück.
Eine andere Art von Architektur sind Peer-to-Peer-Networks.
Das läuft dann so ab, dass Sie eben genau keine Zentraleinheit mehr
haben, mit der Sie kommunizieren, sondern Sie haben Partner in Ihrem
Netzwerk im Allgemeinen, die miteinander kommunizieren.
Es gibt aber keine zentrale Instanz, die diese Kommunikation steuert
in dem Sinne.
Was ist denn Peer-Pressure, was heißt Peer-Pressure an der Stelle?
Kennen Sie den Begriff?
Was heißt das Englische auf Deutsch, jetzt schauen Sie nicht schnell
nach, sondern wissen Sie das?
Peer-Pressure.
Okay, gehen Sie zum Raten über, was kann es heißen?
Ja, das ist der Gruppenzwang, genau.
Peer-Pressure ist der Gruppenzwang.
Okay, wie sieht das als Bild aus?
Nun ja, hier links hatten wir diese Geschichte, es gibt in der Mitte
sozusagen einen Dienstgeber, der Ihnen die Dienste zur Verfügung
stellt und außen die Dienstentgegennehmer, die Klienten, die diesen
Dienst nutzen.
Und bei einem Peer-to-Peer-Network haben Sie das eben genau nicht
mehr, hier in der Mitte fehlt diese zentrale Einheit, sondern die
Partner in diesem Netzwerk reden direkt miteinander.
Und das ist das Charakteristische für so ein Peer-to-Peer-Netz, es
fehlt der zentrale Server.
Das heißt, jeder Klient ist im Allgemeinen auch der Dienstgeber, sind
also dort sozusagen Personalunion.
Sie haben eine Dezentralisierung, was dadurch bedingt ist, dass Sie
halt keine zentrale Stelle haben, an der Sie irgendwas zentralisieren
könnten.
Und das Gesamtverhalten des Systems, und das ist das Interessante bei
solchen Peer-to-Peer-Netzen, ergibt sich eben daraus, wie die Klienten
im Allgemeinen miteinander reagieren.
An der Stelle würde ich gerne eine Seitennotiz machen auf das Institut
von Herrn Professor Böhm.
Die haben mal eine Zeit lang untersucht, das ist jetzt so ein, ich
begebe mich jetzt gerade auf eine abseitige Fahrt, sagt Ihnen der
Begriff Spieltheorie etwas?
Spieltheorie, ja, so ein bisschen?
Die haben mal untersucht, also so rein anreiztechnisch, rein was
würden Sie als Mensch vielleicht auch tun, wie sich das Verhalten von
Teilnehmern im Peer-to-Peer-Netz verhält, wenn man Anreize im Peer-to
-Peer -Netz so oder so setzt.
Da geht es also, wenn Sie mal ein bisschen abstrakter denken als nur
BitTorrent, dann kommen da solche Dinge zustande, wie angenommen Sie
würden in so ein Netz wie BitTorrent einen Bewertungsmechanismus
einführen, also Sie als Dienstnehmer, ich habe mir von Ihnen ein Datei
heruntergeladen, kann jetzt beurteilen mit so einem Art, wie bei Ebay
oder wie bei Amazon oder so, ich kann Ihnen Sternchen vergeben, ich
kann jetzt Sie beurteilen als derjenige, der mir die Datei zur
Verfügung gestellt hat, mit einem bis fünf Sternchen.
Angenommen, es gäbe so ein Mechanismus in BitTorrent, wie würde sich
dann das Nutzerverhalten verändern?
Würden sich dann potenziell die Leute mehr als Dienstgeber verhalten
oder weniger oder was braucht es, damit sie das tun oder nicht?
Wenn Sie sich solche Fragen interessiert, wie funktionieren
Anreizmechanismen in Peer-to-Peer-Netzen, dann schauen Sie mal bei
Professor Böhm vorbei auf der Webseite, da gibt es ein paar Arbeiten
dazu und einige recht interessante Dinge, wie zum Beispiel sowas wie,
es gibt in Peer-to-Peer-Netzen Altruismus.
Das fand ich ja sehr interessant.
Wenn Sie nicht wissen, was Altruismus ist, gucken Sie mal auf
Wikipedia nach.
Okay, das war die Seitennotiz.
Zurück zu Softwaretechnik und Peer-to-Peer-Netzen.
Wenn Sie ein Peer-to-Peer-Netz entwerfen, dann haben Sie diese
Geschichte mit der Autonomie.
Das war, dass jeder Teilnehmer im Netz ein Stück weit mitentscheidet,
wie das Gesamtnetz sich verhält, aber Sie können eben über geeignete
Anreizmechanismen vielleicht auch ein gewisses Gesamtsystemverhalten
provozieren.
Sie haben dort oft in der Regel das Problem der Zuverlässigkeit eines
einzelnen Knoten.
Sie müssen also, wenn Sie so etwas entwerfen, darauf Rücksicht nehmen,
dass Sie mit dem Ausfall einzelner Knoten rechnen müssen und eben nur
ein gewisser Prozentsatz des Netzes immer zur Verfügung steht.
Sie müssen möglicherweise auch mit Boshaftigkeit rechnen.
Das hat auch hier etwas mit der Zuverlässigkeit zu tun.
Möglicherweise haben Sie Knoten, die sich in das Peer-to-Peer-Netz
einhängen, die dem Gesamtsystem irgendwie schaden wollen.
Möglicherweise haben Sie Vandalismus, möglicherweise haben Sie Knoten,
die Daten zur Verfügung oder Dienste zur Verfügung stellen, die nur
scheinbar das sind, was eigentlich zur Verfügung gestellt werden soll.
Das sind alles so Dinge, die Sie bei Peer-to-Peer-Netzen im Hinterkopf
behalten sollen.
Und mit dieser Unzuverlässigkeit kommt natürlich auch die Frage der
Verfügbarkeit einher, wenn Sie also dort Dienste haben, die Sie zur
Verfügung stellen.
Wie sieht es aus, wenn gewisse Knoten oder wie viel Prozent der Knoten
nicht da sind?
Wie sieht es aus mit den Daten, die auf diesen Knoten waren?
Komme ich an die trotzdem noch irgendwie ran?
Muss ich für Redundanz sorgen?
Kann ich diese Daten wieder frisch generieren?
Solche Fragen tauchen da auf.
Genau.
Bitterend ist das Schlüsselwort, es ist schon gefallen.
Ich denke, das ist Ihnen ein Begriff.
Das ist so ein prominentes Beispiel von so einem Partnernetz, von so
einem Peer-to-Peer-Network.
Und das ist genau dort der Fall.
Der Klient, den Sie bei sich installieren, ist Dienstgeber und Nehmer
zugleich.
Sie können Dateien bereitstellen als auch herunterladen und haben
einen gewissen Einfluss darauf, in welcher Form das geschehen soll.
Das hier unten, muss ich ehrlich gestehen, verstehe ich gerade nicht,
was damit mit Peer-to-Peer gemeint sein könnte, weil denen ist es
meiner Meinung nach ein hierarchischer Serverdienst.
Können Sie gerade einen Zusammenhang mit Peer-to-Peer-Netzen an der
Stelle herstellen?
Okay, Beweis durch wegklicken.
Dann ein anderes Architekturmuster.
Das kennen Sie aus der Versionskontrolle, die Sie einsetzen.
Unser SVN ist genau so etwas.
Wir haben eine zentrale Datenablage.
Genauer gesagt haben wir einen Dienst, der uns diese Datenablage mit
bestimmten Eigenschaften zusichert und bereitstellt.
Und wir alle benutzen einen Klienten.
Es ist sowohl ein Client-Server an der Stelle als auch dieses
Datenablage -Modell.
Die Datenablage ist es insofern, als dass es ein Datenablagedienst
ist.
Client -Server ist es insofern, als dass wir einen Subversion-Server
haben, den wir betreiben und wir alle mit unseren SVN-Klienten auf den
Server zugreifen und unsere Daten transferieren.
Der Server stellt uns die zentrale Datenablage zur Verfügung und
insbesondere ist der Klient an der Stelle relativ lose gekoppelt mit
der Datenablage.
Wie lose, das hängt dann vom einzelnen System ab.
Bei Git beispielsweise ist das noch deutlich loser als bei SVN.
Aber das kommt auf den konkreten Dienst, auf die konkrete Anwendung
an, die Sie an der Stelle da entwerfen.
Sie können im Besonderen auch in aller Regel mit mehreren Subsystemen
auf der linken Seite gleichzeitig auf die Datenablage zugreifen.
Also das wäre so der Fall, dass rechts hier ist eine Datenbank oder
sowas.
Dort macht die Datenbank ja insbesondere auch diese Zusicherungen.
Sie können gleichzeitig sicher auf die Datenablage zugreifen und auch
das hier geht sowohl lokal als auch mit Fernzugriff, wobei mit
Fernzugriff der Entwurf meiner Meinung nach deutlich interessanter
ist.
Ein anderes Beispiel für die Datenablage ist, was in Ihrem Übersetzer
passiert.
Wenn Sie mal Ihren Übersetzer anwerfen, dann passieren da einige
Dinge, bis da hinten wieder ein Stück Programm rauskommt.
Zum Beispiel passiert da eine Syntaxanalyse.
Das ist dieser Kasten hier oben.
Die Syntaxanalyse analysiert einfach, haben Sie in Java an allen
Stellen auch den Strichpunkt gemacht, wo er hingehört.
Sie untersucht Ihr Programm, ob Sie syntaktisch gültige Eingabesprache
auch in den Übersetzer eingeben.
Und für Java ist das eben, naja, Sie müssen da halt Java reingeben.
Und wenn Sie in einen Java-Übersetzer C++ reinwerfen, dann wird Sie
die Syntaxanalyse anspringen und sagen, was ist denn das hier?
Und so haben Sie noch andere Komponenten des Übersetzers, auf die
möchte ich jetzt gar nicht im Detail eingehen, dafür gibt es andere
Vorlesungen.
Der Punkt ist, all diese Komponenten erzeugen bestimmte zusätzliche
Informationen aus dem Quelltext, den Sie in den Übersetzer hineingeben
und speichern den zentral ab.
Zum Beispiel den Strukturbaum oder die Symboltabelle.
Was das im Einzelnen ist, ist nicht so wichtig im Moment.
Der Punkt ist, mehrere Komponenten aus dem Übersetzer speichern
generierte Daten aus ihrem Quelltext in einer zentralen Datenablage
ab, das ist hier die Mitte, und andere Komponenten können dann auf
diese Elemente zugreifen.
Zum Beispiel Ihr Eclipse-Editor oder Ihr Eclipse-Debugger können auf
diese Datenelemente hier zugreifen, um Ihnen erweiterte, vertiefte,
genauere Informationen zu Ihrem Quelltext zur Verfügung zu stellen.
Das ist textuell ziemlich genau, was ich gerade gesagt habe.
Mehrere Werkzeuge können auf die Datenablage sicher und gleichzeitig
zugreifen.
Eventuell haben Sie ja ein Transaktionskonzept dahinter.
Bei Subversion ist das übrigens der Fall.
Wenn Sie sich an die Subversion-Folien erinnern, dort war es ja so,
wenn Sie in CVS, dem Vorgänger von Subversion, mehrere Dateien auf
einmal eingebucht haben und aus irgendwelchen Gründen ist die
Einbucherei an irgendeiner Stelle abgebrochen, dann konnte es sein,
dass der Einbuchvorgang zu 70% fertig war, aber die letzten zwei
Dateien, die Sie noch gerne eingebucht hätten, die sind dann nicht da
drin gelandet.
Da haben Sie keine Transaktion gehabt.
Wenn Ihnen also bei CVS so etwas passiert ist, dann mussten Sie zu Fuß
sowohl Ihre Arbeitsplatzkopie als auch das Depot, die zentrale
Datenablage, untersuchen und schauen, wo war es schiefgegangen, und zu
Fuß die Transaktion eben zu Ende führen und fortsetzen.
Transaktion war das falsche Wort, die Aktion fortsetzen.
Denn das CVS-System hat Ihnen keine Transaktionssicherheit geboten.
Subversion kann das.
Nun, dort haben Sie also die Garantie, wenn irgendwas mit der
Netzwerkübertragung schief geht oder irgendwas anderes an der Stelle
abbricht, im richtigen Moment geht die Festplatte der Subversion
-Dateiablage kaputt oder so, dann haben Sie durch die
Transaktionseigenschaft die Sicherheit, dass Sie entweder die
Änderung, die Sie einbuchen wollten, komplett drin haben oder nicht.
Und diese Binärentscheidung, Sie haben sie entweder ganz drin oder
nicht, die wüssten Sie sehr zu schätzen, wenn Sie einmal CVS zu Fuß
aufräumen mussten.
Ja, ein anderes Architekturmodell ist Model-View-Controller.
Das ist gedacht im Allgemeinen für grafische Oberflächen.
Die Idee dahinter ist, dass Sie, wenn Sie eine grafische Oberfläche
bauen, dass Sie die Darstellung von Daten trennen von der Speicherung
der Daten, vom Vorhalten der Daten und trennen von der Logik, die
diese Daten verwaltet.
Das Modell, das M hier drin, ist das Datenmodell, das übernimmt die
Speicherung, dort tun Sie alle Ihre Daten rein, das kann eine
Datenbank sein, das können auch Datenstruktur, Klassen, Objekte in
Ihrem Programm sein.
Das V ist das View, das ist also die eigentliche grafische Oberfläche,
da drin sind die Knöpfe und die Listen und die Textfelder und solche
Dinge.
Und der Controller enthält die Logik, das ist so das Verbindungsklied
zwischen den beiden, der weiß, mit welchem Datum was in der Oberfläche
geschehen soll.
Und diese drei Elemente zusammenheißen eben MVC, Model View
Controller, und sehen dann zum Beispiel wie folgt aus.
Sie haben hier rechts die Datenspeicherung, der Controller ist
derjenige, der die Interaktion logikmäßig unter Kontrolle hat und View
ist das, was der Benutzer am Schluss sieht.
Und die Idee ist eben, diese drei Komponenten so voreinander zu
trennen, dass sie beispielsweise auch hier wieder das alte
Kapselungsprinzip, dass sie im Modell etwas ändern können, ohne dass
sie die grafische Oberfläche groß ändern müssen.
Wenn die grafische Oberfläche zum Beispiel sehr genau Bescheid weiß,
in welchen Datenstrukturen die Daten zu finden sind und keine
definierte Schnittstelle hat, dann müssen sie ja, wenn sie das
Datenmodell ändern, auch die grafische Benutzeroberfläche anfassen.
Wenn sie es aber geschafft haben, so gut zu entwerfen, dass die
Datenspeicherung hier hinter einer Schnittstelle verborgen ist, dann
können sie das Modell wieder ändern, ohne dass sie die grafische
Benutzeroberfläche anfassen müssen.
Wie erfährt eigentlich, wenn der Controller in den Daten etwas ändert,
wie erfährt denn die grafische Benutzeroberfläche von dieser Änderung?
Dafür gibt es ein Entwurfsmuster.
Dieses Entwurfsmuster heißt Beobachter.
Den Beobachter schauen wir uns konkret im nächsten Kapitel an.
Grob passiert hier Folgendes.
Der View, also hier die grafische Benutzeroberfläche, sagt dem Modell
Bescheid.
Hallo Modell, wenn sich bei dir etwas ändert, sag mir Bescheid.
Und das können mehrere Views machen.
Sie können also hier ziemlich viele dieser View-Komponenten haben und
alle diese Komponenten können sich beim Modell anmelden und dem Modell
einen Hinweis hinterlassen, dass sie bitte bei Datenänderungen
benachrichtigt werden sollen.
Und das Modell hält jetzt eine Liste von Views vor, von denen es weiß,
dass sie bei Änderungen benachrichtigt werden sollen.
Und wenn jetzt eine Änderung eintritt, egal woher sie kommt, dann
latscht das Modell diese Liste ab und benachrichtigt alle Views über
diese Änderung.
Das ist also eine Form von...
wie heißt das?
Don't call us, we call you an der Stelle.
Der View meldet sich an, sagt mir Änderungen und das Modell ruft
zurück und sagt, jetzt hat sich eine Änderung ergeben, jetzt hat sich
eine Änderung ergeben.
Und der View entscheidet dann, ob die Änderung, die sich ergeben hat,
für ihn relevant ist und er vielleicht irgendwie den
Fortschrittsbalken jetzt wieder um 2% nach rechts rutschen soll, weil
sich aus den Daten ergeben hat, dass der Fortschritt um 2% gestiegen
ist.
Das war das hier oben, das war die Aktualisierung des Views aus dem
Modell.
Sie können natürlich auch zum Beispiel, wenn Sie den Controller und
das Modell gut entworfen haben, einfach einen anderen View davor
klatschen und die gleiche Oberfläche oder das gleiche
Anwendungsprogramm auf einem anderen Betriebssystem laufen lassen,
dass die Darstellung vielleicht irgendwie anders vornimmt, indem Sie
einfach den View austauschen.
Das ist der dritte Punkt hier unten.
Das sind solche Flexibilitäten, die Ihnen das Model-View-Controller
-Architekturmodell hier liefert.
Was haben wir noch?
Wir haben das Fließband, die Pipeline.
Also auch hier wieder, denken Sie nicht so sehr an die Hardware und
die Prozessoren, dort gibt es auch eine Pipeline.
Das ist vom Software-Technik betrachtet sogar sehr, sehr ähnlich.
Aber die Pipeline an sich ist so gedacht, dass sie mehrere
Verarbeitungsstufen haben, von denen jede ihre... also die Pfeile hier
oben stellen eine Art...
ne, nicht eine Art, die Pfeile hier oben stellen einen Datenfluss dar.
Gemeint ist also, irgendjemand wirft hier oben ein Eingabedatum rein,
die Stufe 1 verarbeitet diese Eingabedaten, macht mit denen irgendwas
und erzeugt ein Ausgabedatum.
Das kann auch ein anderer Typ sein, ein anderes Datum oder sowas.
Und dieses Datum wird in die nächste Stufe gegeben, die macht damit
wieder was, wird in die nächste Stufe gegeben, etc.
Man findet solche Fließbänder recht häufig in
Bildverarbeitungssoftware, wenn sie über Videos oder über Bilder
Filter laufen lassen wollen.
Beispielsweise gibt es eine Software, die in einem mehrstufigen
Pipeline -Prozess hier vorne zwei Bilder nimmt, die von leicht
unterschiedlich positionierten Kameras auf das gleiche Bild, das
Stereo -Bild berechnet, also das Tiefenbild.
Sie erhalten dann eine Information darüber, wie weit weg das
spezifische Objekt, das auf den beiden Bildern dargestellt ist und die
ja hoffentlich das gleiche Motiv zeigen, wie weit weg das Objekt ist.
Und diese Berechnung, wie tief ist an dieser Stelle des Bildes dieses
Objekt aus zwei Stereo-Bildern, das ist so ein mehrstufiger Pipeline
-Prozess.
Können Sie sich vorstellen, wo die beiden Bilder durch mehrere Filter
laufen, durch Kantendetektoren laufen, durch Verschränkungen laufen,
durch Finden gemeinsamer Objekte laufen und daraus eine
Tiefeninformation dann schließlich entsteht.
Wenn Sie gut sind, dann haben Sie hier zwischen einen Puffer gepackt,
das ist das, was hier unten erwähnt ist, der, wenn die Stufe 1
beispielsweise schneller ist als die Stufe 2, die Ergebnisse von Stufe
1 einfach schon mal zwischenspeichert, bis Stufe 2 dazu kommt, diese
abzuarbeiten.
Dann kann Stufe 1 schon mal ein bisschen weitermachen und Stufe 2,
wenn die halt ein bisschen langsamer ist, dann blockiert, also der
Sinn dahinter ist, dann blockiert nicht die gesamte Pipeline, das
gesamte Fließband an der langsamsten Stufe, sondern die Stufen können,
zumindest die schnelleren vorweg, können schon mal ein bisschen
weiterarbeiten und einfach schon mal die Puffer füllen und nicht das
ganze Fließband blockiert.
Warum, naja, im sequentiellen Fall sagen Sie vielleicht, naja, was
bringen mir Puffer, früher oder später ist der Puffer ja voll und es
hängt dann doch früher oder später an der langsamsten Stufe.
Nun ja, wenn Sie zu Parallelverarbeitung übergehen, dann ergibt sich
ja möglicherweise eine interessante Möglichkeit, wenn Sie hier in
orange diese Puffer haben, dann können Sie ja vielleicht die Stufe 2
mehrfach ausführen.
Wenn Stufe 2 die langsamere Stufe ist und Stufe 1 ist die schnellere,
dann haben Sie vielleicht so etwas wie die Stufe 1 liefert Ihnen 10
Bilder pro Sekunde, es landen also 10 Bilder pro Sekunde in dem Puffer
zwischen Stufe 1 und Stufe 2.
Stufe 2 kann aber nur zwei Bilder pro Sekunde verarbeiten, weil das
ein recht umständlicher, ein ausführlicher Rechenvorgang ist.
Naja, dann könnten Sie vielleicht, wenn die Stufe 2 sich dafür eignet,
diese eben einfach in fünf Instanzen ausführen, sodass Sie eben fünf
Instanzen haben, die jeweils zwei Bilder pro Sekunde verarbeiten und
schon haben Sie wieder die Menge, die Stufe 1 produziert, in etwa
gleicher Geschwindigkeit in Stufe 2 abgearbeitet.
Im Allgemeinen, das ist jetzt schon wieder recht nah beim Prozessor,
können Sie die Stufen natürlich auch für die Elemente überschneiden
lassen.
Also gemeint ist hier nun, Sie fangen mit dem ersten Element an, das
wird in der ersten Zeiteinheit von Stufe A1 bearbeitet, in der zweiten
Zeiteinheit von Stufe A2, in der dritten Zeiteinheit von Stufe A3 und
so weiter.
Aber während das erste Element in der zweiten Zeiteinheit von Stufe 2
bearbeitet wird, kann in derselben Zeiteinheit ja schon das zweite
Element bearbeitet werden von der ersten Stufe.
So ergibt sich hier also eine Überschneidung und Sie können das
Fließband mit der ersten Stufe laufen lassen
und während das erste Element noch bearbeitet wird in der vierten,
fünften, sechsten Stufe, haben Sie weitere Elemente schon wieder von
vorne in das Fließband hineingeschoben und können dort sozusagen auf
der großen Fließbandebene elementweise Parallelität erzeugen.
Das ist die andere Variante, wie Sie in Fließbändern Parallelität
erzeugen, wenn Sie nicht hier einzelne Stufen vervielfachen können
oder möchten.
Ein völlig unparalleles Beispiel für Fließbänder ist vielleicht so ein
Aufruf wie der hier.
Wenn Ihnen das ein bisschen spanisch vorkommt, das ist ein möglicher
Aufruf, den Sie vielleicht auf einer Unix-Commanderzeile finden.
Das hier vorne ist für die Windows-Benutzer so ein bisschen wie der
Taskmanager, nur textuell.
Also stellen Sie sich vor, da kommt wie so ein System Out Print Line,
kommen da einfach zeilenweise die Prozesse zurück, die gerade auf dem
Rechner laufen.
Grab ist ein Filter, der filtert zeilenweise die Ergebnisse, was hier
zurückgeliefert wird.
Also wenn das Grab hier fertig ist, dann würden aus der Liste, die das
PS hier zurückliefert, nur noch jene Zeilen übrig bleiben, die
irgendwo in dieser Zeile, in derselben Zeile, das Wort Hans haben.
Dann kommt Sort, das tut das Offensichtliche.
Die also noch verbleibenden Zeilen, die Hans beinhalten, werden
alphabetisch sortiert und Less ist einfach nur ein Darsteller in der
Kommandozeile, der Ihnen ermöglicht, wenn das sehr viele Prozesse
sind, dass Sie seitenweise sich das Ergebnis anschauen können.
Und das ist jetzt genau so ein Fließband an der Stelle.
PS erzeugt erstmal die initiale Ausgabe mit sämtlichen Prozessen, die
Sie da so haben.
Grab kürzt diese Ausgabe um jene Zeilen in diesem Fall, die nicht das
Wort Hans beinhalten.
Sort sortiert die verbleibenden Zeilen, die noch Hans beinhalten,
alphabetisch und dann bekommen Sie sie seitenweise dargestellt, wenn
eben noch viele genug da sind.
Anderes Beispiel für ein Fließband wäre in der Signalverarbeitung.
Hier vorne bekommen Sie also so ein Signal rein, so etwas, das Sie vom
Oszilloskop vielleicht kennen.
Und auf diesem Signal können Sie eine diskrete Fourier-Transformation
ausführen, können das Signal vielleicht manipulieren, die Fourier
-Transformation wieder zurückwandeln und ein geändertes Signal nach
hinten rausgeben.
Das wird oft für Filter benutzt, wenn Sie also ein Signal bekommen und
Sie möchten das auf eine bestimmte Art und Weise filtern, die hohen
oder die tiefen Frequenzen ein bisschen dämpfen, dann können Sie das
auf diese Art und Weise tun und hinten eben ein geändertes Signal
rausbekommen und dort ist das Fließband auch etwas, was man oft
findet.
Die Signalmanipulation ist hier natürlich sehr vereinfacht, auch hier
drin können natürlich wieder mehrere Stufen stecken, bis das Signal
schließlich zurückgewandelt wird und zurückgeliefert wird.
Genau, also ein Fließband ist gut geeignet, wenn Sie Datenelemente
oder Datenströme haben, die von vorne nach hinten irgendeiner Form
berechnet werden sollen.
Bilder, Video ist ein typisches Beispiel, Signalverarbeitung ist ein
typisches Beispiel, Stapelverarbeitung wäre wieder so elementweise,
Sie haben 100.000 Dateien, die Sie verarbeiten müssen auf die gleiche
Art und Weise.
Oder man könnte auch einen Übersetzer als so etwas ansehen, wo eben
der Eingabequelltext viele Zwischenstufen durchwandern muss, bis
schließlich ein fertiger Objektcode oder ein Bytecode oder sowas
rauskommt.
Dort bestehen die Stufen oft aus Optimierungen.
Also jede Optimierungsstufe läuft erneut über irgendeine Art der
Zwischendarstellung, die im Depot, Sie erinnern sich, genau,
das hatten wir schon besprochen, das ist diese Sache, mit der Sie
vielleicht Stufen multiplizieren oder das gesamte Fließband
multiplizieren können, wenn Ihnen Geschwindigkeit wichtig ist.
Was haben wir noch?
Wir haben einen Architekturstil Rahmenarchitektur.
Damit ist gemeint, also mit diesem Architektur, das Wort dürfte Ihnen
bekannt vorkommen, bauen, die Idee ist, die Rahmenarchitektur enthält
bereits einen großen Teil der Anwendung, die Sie bereitstellen möchten
und Sie stecken nun nur noch an den entsprechenden Stellen die
Funktionalität hinzu, die Sie bereitstellen möchten, die Sie jetzt
brauchen.
Ich zeige es Ihnen mal ein bisschen konkreter als in diesem
allgemeinen Blabla, nämlich ich zeige es Ihnen mal an, ja hier geht es
zum Beispiel, wenn Sie ein Zeichenprogramm haben und Sie möchten
dieses Zeichenprogramm erweitern um weitere Elemente, also Sie können
eine Linie machen, ein Rechteck, ein Ellipse und so weiter und Sie
möchten jetzt ein neues Element in diesem Zeichenprogramm zur
Verfügung stellen und das Zeichenprogramm ist als Rahmenarchitektur
entworfen, das heißt es bietet Ihnen auch die Möglichkeit etwas zu
erweitern, dann funktioniert das eben genau so, dass hier oben ist die
existierende Anwendung, die sagt Ihnen möglicherweise in der
Dokumentation, wenn Sie etwas hinzufügen möchten an Funktionalität,
dann machen Sie das wie folgt, Sie nehmen die Schnittstelle oder
abstrakte Klasse Grafik her und implementieren diese oder Erben von
einer anderen Klasse hier unten, wie zum Beispiel dieses Quadrat und
implementieren einfach die Methoden, die in der Schnittstelle
gefordert sind und solange Sie das tun, ist das, was Sie da jetzt neu
implementiert haben, nachher auch auf der grafischen Oberfläche
anwendbar.
Ich habe gerade gesagt Schnittstelle oder abstrakte Klasse, was ist
denn der Unterschied zwischen einer abstrakten Klasse und einer
Schnittstelle und was ist der Unterschied dazwischen?
Ich kann es jetzt leider nicht abstimmen lassen.
Was ist der Unterschied zwischen einer Schnittstelle und einer
abstrakten Klasse?
Abstrakte Klasse ist doch abstrakt und eine Schnittstelle doch auch,
oder?
Da war der Hinweis, in abstrakten Klassen habe ich bereits Teile von
Implementierungen enthalten, möglicherweise ist das Schlüsselwort, und
Schnittstellen können das nicht.
Ja, das ist eigentlich genau das.
Also abstrakte Klassen sind der Zwitter zwischen einer Schnittstelle,
die ausschließlich abstrakte Methoden enthält und einer Klasse, die
ausschließlich implementierte Methoden enthält.
Die abstrakte Klasse liegt genau dazwischen, das ist, wenn Sie eine
Schnittstelle zwar vorgeben wollen, die Implementierung von Methoden
aber noch nicht angeben können, aber andere Methoden so implementiert
sein sollen und nicht anders.
Ja gut, Sie können es ja auch noch überschreiben, aber insbesondere
wenn Sie eine Implementierung gleich mit angeben wollen in einer
Schnittstelle, können Sie keine Implementierung mitgeben.
Abstrakte Klasse, Schnittstelle, Klasse.
Okay, wir haben also hier unsere Schnittstelle oder unsere abstrakte
Klasse implementiert, vermutlich ist das hier eine Schnittstelle.
Sie erkennen eine abstrakte Klasse im allgemeinen im UML-Diagramm
daran, dass sie mindestens eine Methode enthält, die nicht kursiv
gedruckt ist, die also in dieser Oberklasse hier schon implementiert
ist, dann haben Sie im Allgemeinen eine abstrakte Klasse, weil die
Schnittstelle ja keine Implementierungen enthalten kann.
Okay, wie bringen Sie jetzt Ihrem Rahmenwerk bei, dass Sie jetzt
ein...
jetzt haben Sie hier unten zwar so hübsch implementiert, aber jetzt
weiß die Anwendung hier noch nichts von.
Wie funktioniert das?
Nun, in Eclipse funktioniert das wie folgt, bla bla, so sieht es aus.
Sie haben hier oben Eclipse, so wie es existiert.
Und wenn Sie eine Erweiterung schreiben, das ist dieser untere Teil
hier, dann bauen Sie in diese Erweiterung nicht nur Ihre Klassen ein,
sondern Sie haben da auch eine Manifestdatei, heißt die.
Und in der Manifestdatei, das ist diese linke...
...Meta-Informationen zu dem Plugin, zu dieser Erweiterung, die Sie
bereitstellen.
Hier steht zum Beispiel drin, wie Ihre Erweiterung heißt.
Hier steht auch drin, welche Klasse bitte angesprochen werden soll,
wenn der Benutzer diese Erweiterung ausführt.
Und diese Datei ist der Schlüssel für Eclipse, Ihre neue
Funktionalität auch in der Oberfläche anzubieten.
Was also hier passiert ist, das bestehende Eclipse findet die
Manifestdatei, nimmt die Meta-Informationen über Ihre Erweiterung aus
der Manifestdatei her und stellt sie schon mal in der Oberfläche dar.
So findet zum Beispiel ein zusätzlicher Menü-Eintrag seinen Weg in das
Eclipse -Menü.
Das ist mal der erste Schritt.
Sie sehen es in der Oberfläche und jetzt kann ich diesen Menü-Eintrag
anklicken.
Und wenn ein Benutzer jetzt diesen Menü-Eintrag anklickt, dann steht
in der Manifestdatei, wenn jemand diesen Menü-Eintrag anklickt, dann
soll bitte folgende Klasse verwendet werden und instanziiert werden.
Das ist jetzt eine Klasse aus Ihrer Erweiterung.
In der Manifestdatei steht, nimm diese Klasse hier rechts, die Klasse
Aktion, wenn ein Benutzer den Menü-Punkt anklickt, den du mir hier zur
Verfügung gestellt hast.
Und jetzt geht Eclipse her und instanziiert diese Klasse hier unten
über ihren Namen, das funktioniert hier oben, über Reflection heißt
diese Technik, nicht so wichtig.
Der Punkt ist, Eclipse weiß aus der Manifestdatei, welchen
Klassennamen es instanziieren muss, um die neue Funktionalität zur
Verfügung zu stellen, macht dies an dieser Stelle und Sie haben ja
zugesichert, dass Sie mit Ihrer Klasse eine bestimmte Schnittstelle
implementiert haben, das ist hier die Schnittstelle IActionDelegate,
das gibt Ihnen Eclipse vor, dass Sie diese Schnittstelle
implementieren müssen, wenn Sie eine Erweiterung zur Verfügung stellen
wollen.
Eclipse instanziiert Ihre Klasse, weiß nun, dass Sie die
Schnittstellenmethoden, mindestens die, zur Verfügung stellen und kann
nun diese Methoden auf Ihrer neuen Erweiterung aufrufen.
Tada, und so funktionieren Plugins in Eclipse.
Eclipse selbst stellt hierbei die Rahmenarchitektur dar, sie bietet
Ihnen einen Mechanismus, wie Sie zusätzliche Funktionalität in Eclipse
einbauen können und eine Schnittstelle, die Sie implementieren müssen,
um das zu tun, und sagt Ihnen in der Manifestdatei, wie diese
Einbinderei zu funktionieren hat.
All das ist Teil des Entwurfs der Rahmenarchitektur von Eclipse.
Wann benutzen Sie den Architekturstil Rahmenarchitektur?
Naja, wenn Sie eine Grundversion des Programms haben und Sie einfach
nur an den entsprechenden Stellen neue Funktionalität eingebaut, wenn
Sie nur an der entsprechenden Stelle neue Funktionalität einbauen
wollen.
Wenn Sie nur ein kleines Stück Anwendungslogik austauschen müssen,
oder wenn große Teile von Anwendungslogik immer gleich bleiben, egal
was sich im Detail vielleicht ändert.
Diese Entwurfsmuster, die hier aufgezählt sind, die werden wir im
nächsten Kapitel deutlicher besprechen.
Das sind alles Entwurfsmuster, die in diesem Zusammenhang genannt
werden.
Das sind alles Entwurfsmuster, mit denen Sie Varianten erstellen
können oder solche Einschübe erstellen können.
Es gibt noch einen anderen Architekturstil, das ist so ein recht
konkreter, nicht Spezialfall, aber einen, den man seit einigen Jahren
im Internet findet.
Das ist die Service-Oriented Architecture.
Das ist relativ nah bei dem Klient-Dienstgeber-Modell und sieht wie
folgt aus.
Man legt Dienstbeschreibungen in einer abstrakten Form ab.
Ein Klient kann sich diese Dienstbeschreibungen holen und diesen
Dienst entsprechend nutzen.
Ich glaube, am Bild wird es etwas klarer.
Sie haben also einen Dienstanbieter, sagen wir mal Ihren FTP-Server.
Und das Erste, was der macht, ist, Sie veröffentlichen eine
Beschreibung über Ihren Dienst, über den FTP-Server, an ein
Dienstverzeichnis.
Das nennt sich Service Registry in diesem Vokabular.
Und Sie als Nutzer können nun in dem Dienstverzeichnis nach einem
bestimmten Dienst suchen.
Diese Dreiecksbeziehung hier ist der zentrale Unterschied zwischen
Klient -Server, zwischen Klient-Dienstgeber und diesem Modell hier.
Hier haben Sie einen Suchdienst, über den Sie Ihren Dienst erstmal
herausfinden können.
Der Dienstnutzer kann dann die Beschreibung, die der Dienstanbieter
veröffentlicht hat, herunterladen und diesen Dienst entsprechend
nutzen.
Hier finden einige Protokolle Anwendungen, deren Abkürzungen Sie mal
Wikipediaisieren können, wenn Sie Lust haben.
Das ist die Web Service Description Language.
Das ist ein XML-Dialekt, den der Dienstanbieter benutzt, um seinen
Dienst zu veröffentlichen.
UDDI ist einfach dieser Dienstbereitsteller.
Ich weiß ehrlich gesagt gerade nicht auswendig, was UDDI heißt.
SOAP, ich glaube das heißt einfach nur Service-Oriented Architecture
Protocol.
Das ist im Wesentlichen auch XML über HTTP, mehr ist das nicht.
Schlussendlich geht es hier um Dienstnutzung via HTTP über XML
-Dialekte, wobei die konkrete Ausprägung dessen, was im XML steht, vor
allem auch aus dieser WSDL-Beschreibung kommt, die der Dienstanbieter
zur Verfügung stellt.
Denn je nachdem, welchen Dienst Sie nutzen, müssen Sie natürlich
unterschiedliche Inhalte in die Kommunikation zwischen Dienstnehmer
und Geber versenken.
Was möchte man mit dieser Architektur erreichen?
Na ja, Sie haben damit eine lose Kopplung zwischen den Dienstnehmern
und den Dienstgebern, mal wieder.
Sie können hiermit Geschäftsprozesse unterstützen.
Durch diese relativ formale Beschreibung ist das nicht unbedingt nur
innerhalb einer Firma gedacht, wo jeder alles kennt, sondern diese
Abstraktion über WSDL ermöglicht Ihnen eben auch, dass Sie Dienste
nutzen können von anderen Firmen, über die Sie sich vorher vielleicht
gar nicht groß unterhalten haben.
So ein WSDL zeigt Ihnen schon, wie der Dienst zu benutzen ist, wenn
Sie den über UDDI suchen und gefunden haben.
Genau, das war es soweit zu Architekturstilen.
Dann zeige ich Ihnen noch ein bisschen was aus den Entwurfsmustern.
Kommen wir tatsächlich zu diesem magischen... Es gibt übrigens Kapitel
1, 2, 3, 4 oder 3, 2, 3, 4 oder sowas gibt es irgendwie nicht, also
wundern Sie sich nicht über diese Kapitellücke, das gehört so.
Okay, was sind Entwurfsmuster?
Wenn wir gerade eben bei Architekturstilen waren, die so den ganz
groben Entwurf der Software darstellen, Sie haben jetzt in Schichten
und Dienstgebern und sowas gedacht, dann sind wir bei den
Entwurfsmustern jetzt ein Stückchen tiefer.
Wir sind jetzt ein Stückchen näher an der Software, die Sie entwickeln
sollen.
Und Entwurfsmuster beschreiben Ihnen ein Muster für eine Familie von
Lösungen für typische Probleme, die Sie beim Entwerfen haben.
Sie erinnern sich an diese Geschichte beim Model-View-Controller, das
Problem, wie bekommt der View die Sicht mit, wenn sich im Modell etwas
ändert?
Das ist ein grundsätzliches Software-Problem, das Sie ganz oft haben.
Es ist eine Komponente, die soll irgendetwas tun, wenn sich Daten
ändern, sie soll etwas darstellen, sie soll ein Mail rausstecken oder
sonst irgendwas und dann ändern sich Daten und diese Änderungen müssen
Sie jetzt irgendwie an diese Komponenten kommunizieren.
Und Sie lernen das Entwurfsmuster-Beobachter in diesem Kapitel etwas
genauer kennen.
Das ist genau das Werkzeug, um derartige Probleme gut zu lösen.
Sie bekommen insbesondere eine Wiederverwendbarkeit hier mit rein,
also ganz typisch.
Vielleicht haben Sie diesen Effekt auch mal.
Aber Studenten, die bei uns letztes Semester die Vorlesung gehört
haben und dann jetzt im Wintersemester, im vergangenen Wintersemester,
PSE gemacht haben, die haben uns gesagt, also das mit Entwurfsmustern
hier zu machen in der Vorlesung, das ist ja echt zäh.
Ich lüge Ihnen jetzt nicht vor, jetzt kommt echt harter Tobak, aber da
müssen wir jetzt durch.
Aber der Lohn ist, Mensch, das Entwurfsmuster XY in dieser und jener
Software habe ich wieder gesehen und ich wusste, was es ist und ich
wusste, wie man es bedient.
Und ich wusste genau, dass wenn ich den Iterator da vor mir habe, dass
ich diese zwei, drei Methoden da nehmen muss und der tut genau, was
ich will, weil das ist einfach das Entwurfsmuster.
Und diesen Effekt haben Sie hoffentlich in der einen oder anderen
Software auch wieder.
Das Entwurfsmuster-Beobachter, verzeihen Sie, dass ich so drauf
rumreite, das sehen Sie in jeder Anwendung von grafischen
Benutzeroberflächen.
Jede grafische Benutzeroberfläche, egal ob es auf Mac oder unter Linux
ist, hat genau dieses Problem, Sie haben eine Komponente, die stellt
dar und Sie haben eine Komponente, die speichert irgendwo Daten.
Und wie die beiden sich, vor allem die Änderung von Daten,
benachrichtigt wird an die Ansicht, das ist genauso dieses Standard
-Beobachter -Problem und das wird Ihnen hoffentlich, wenn Sie das
Beobachter als Muster mal gesehen haben, bekannt vorkommen, wenn Sie
es draußen in der freien Wildbahn mal sehen.
Ach, das ist so hübsch, soll ich Ihnen vorlesen?
Also, each pattern describes a problem which occurs over and over
again in our environment and then describes the core of the solution
to that problem in such a way that you can use this solution a million
times over without ever doing it the same way twice.
Das ist doch eine schöne Zusammenfassung, oder?
Okay, genau diesen Zweck soll ein Entwurfsmuster erfüllen.
Warum haben wir Entwurfsmuster überhaupt?
Warum einigen wir uns auf solche Gemeinsamkeiten, gemeinsame Lösungen
von ähnlichen Problemen?
Nun ja, wir entwickeln damit ein Vokabular, mit dem wir uns im Team
auch unterhalten können.
Wenn wir uns später beobachten, später treffen und wir unterhalten uns
über Beobachter und Literatoren, dann wissen wir vielleicht, was
gemeint ist.
Genau, das ist etwas, was diese Zusammenfassung so mit sich bringt.
Sie können mit Mustern einfach Entwürfe ganz gut dokumentieren.
Sie können sagen, an dieser und jener Stelle habe ich einen Visitor
eingebaut und dann weiß jemand, der das liest, genau was damit gemeint
ist vielleicht.
Und insbesondere legen Sie uns dar, dass Sie das mit dem Entwerfen
auch verstanden haben.
Genau, Sie mit Entwurfsmustern dokumentieren Sie und fördern Sie den
Stand der Kunst.
Also Entwurfsmuster sind ja nicht nur da, das ist ja nicht ein blinder
Katalog von Vokabular, um Sie irgendwie zu ärgern oder so, sondern
jedes Entwurfsmuster hat ein ganz spezifisches...
Ja, also da haben sich einige Leute Gedanken darüber gemacht, wie man
das spezifische Problem, was das Muster lösen soll, wie man das gut
lösen könnte, wie man das allgemein, allgemein, Sie erinnern sich an
das Wort, wie man das allgemein lösen könnte und das Entwurfsmuster
möglichst oft anwenden kann.
Die Entwurfsmuster haben im Allgemeinen auch eine flexible
Schnittstelle, so dass sie gut an das konkrete Problem angepasst
werden können.
Und weil das alles so ist, können Sie mit Entwurfsmustern auch die
Qualität und die Struktur Ihres Quelltextes verbessern.
Ein Beispiel, das zeige ich Ihnen noch.
Das ist ein sehr plastisches Beispiel, wie es ist, wenn man mit
Schnittstellen zu kämpfen hat.
Also das Problem war, man hat hier ein zweiteiliges Raumfahrzeug und
der linke Teil ist das Mondlandemodul, in dem zwei Astronauten Platz
haben und der rechte Teil ist das Kommandomodul, in dem die drei
Astronauten drin waren während der Reise zum Mond.
Und nun gab es ein klitzekleines Problemchen.
Im Service-Modul ist der Strom ausgefallen und außerdem der
Sauerstofftank explodiert und das führte dazu, dass in dem Service
-Modul, also das war halt irgendwie nicht so richtig überlebensfähig.
Man hatte aber ein Lithiumhydrit im Kommandomodul.
Fragen Sie mich nicht genau, wofür man das braucht, das hat aber etwas
mit der Luftfilterung zu tun, zur Sauerstoffregeneration.
Wenn also der Sauerstofftank explodiert ist, dann braucht man diesen
Kram wohl, um sich wieder atembaren Sauerstoff zu bewaffnen.
Nun hatten die zwar Lithiumhydrit, aber nicht im Kommandomodul,
sondern im Mondlandemodul und jetzt war es halt ein bisschen blöd,
weil sie brauchten es eigentlich hier drüben.
Wie macht man das?
Die brauchten halt mehr, als zur Verfügung stand und hier drüben wäre
auch nochmal ein Vorrat gewesen.
Und das Problem bestand nun eigentlich darin, dass sie im rechten Teil
einen Stoff hatten, den sie links brauchten, aber die Schnittstellen
waren nicht kompatibel, was dazu führte, dass sie improvisiert haben.
Das ist dieses einmalige Problem, kennen Sie diese Einstellungstests?
Sie kriegen so eine Kiste voll mit einem Tesa und einem Lineal und
drei Streichhölzern und dann müssen Sie so MacGyver-mäßig bauen Sie
hier aus mal ein Fahrrad oder so.
Also sowas ähnliches hatten die hier auch.
Die Motivation war hier extra stark, denn das eigene Leben hing so ein
Stück weit mit dran.
Ein nicht laufendes Fahrrad war einfach nicht wirklich eine Option,
aber es war eben dieses Problem, wie kriege ich jetzt ein Teil aus dem
Kommandomodul im Mondlandemodul zum Laufen, wenn diese beiden Teile
nicht für Interoperabilität gebaut waren.
Nun ja, die Lösung sah halt so aus.
Man hat aus dem Kommandomodul diesen Lithium-Hybrid-Behälter genommen
und man sieht hier mit viel Bastelei und Improvisation eine Verbindung
zum Mondlandemodul geschaffen und, das finde ich persönlich das
Besondere daran, mit den Werkzeugen und Materialien, die halt im
Moment gerade zufällig greifbar waren.
Und das ist, glaube ich, bei so einer Mondreise das Besondere an der
Stelle.
Diese Einschränkungen haben wir in der Realität nicht ganz so oft, das
ist ja schon ein bisschen künstlich, aber es stellt relativ schön dar,
welche Kapriolen man schlagen muss, wenn man die Schnittstellen nicht
einfach so entwirft, dass man klack, einstecken und fertig.
Und wenn Sie schon mal auf der Suche waren nach einem neuen Ladegerät
für Ihr Notebook oder Ihr Handy, dann haben Sie wahrscheinlich schon
ähnliche Erfahrungen an der Stelle gemacht, weil es halt 84 gefühlt
Stecker gibt, die Sie untersuchen müssen, ob sie auch zu Ihrem Gerät
passen.
Genau.
Eine etwas komplexere Motivation, ein bisschen stärker Richtung
Softwareentwurf, finden Sie im Buch Headfirst Design Patterns, das
finden Sie in der Bibliothek.
Das empfehle ich Ihnen ganz stark zum Nachlesen, Kapitel 12.
Dort werden mehrere Entwurfsmuster erläutert und miteinander
verbunden.
Wir haben das deshalb hier reingenommen, damit dieses Kapitel nicht
mehr ganz so zäh ist.
Und das Buch kopieren ist einfach nicht so interessant.
Lesen Sie das mal nach und verstehen Sie diese Entensimulation, die
sich da in diesem Kapitel befindet.
Und wenn Sie Lust haben, bauen Sie doch mal ein bisschen was nach.
Die aktuellen Übungsaufgaben bieten ja auch ein bisschen Potenzial für
einen verbesserten Entwurf mit Entwurfsmustern möglicherweise.
Wir schauen deshalb die in dem Endkapitel behandelten Entwurfsmuster,
diese sechs hier aufgezählten, etwas kürzer.
Und die Details können Sie in Headfirst nachlesen.
Zunächst mal, welche Gruppen von Entwurfsmustern gibt es?
Sie haben zum einen Entkopplungsmuster.
Bei Entkopplungsmustern geht es darum, dass Sie etwas von etwas
anderem entkoppeln, also eine Verbindung auflösen oder trennen, die
Sie da nicht haben möchten.
Und in der Regel braucht es dazu eben ein Kopplungsglied, eine
definierte Schnittstelle zwischen den beiden Elementen.
Und für einige Entkopplungsmuster gibt es eben hier schon was
Vorbereitetes.
Das sind jetzt diese sechs Entkopplungsmuster hier.
Die werden wir uns im Detail noch anschauen.
Der Adapter ist, wie der Name schon sagt, damit entkoppeln Sie den
Benutzer, der nicht mehr ganz genau wissen muss, wie etwas im Inneren
funktioniert, anders.
Sie haben eine Implementierung einer Klasse und Sie haben auf der
anderen Seite ein Benutzerprogramm, das diese Klasse benutzen soll.
Aber das Benutzerprogramm benutzt andere Methodennamen und andere
Signaturen, als die anbietende Klasse zur Verfügung stellt.
Also Sie haben eine Klasse, die macht im Grunde schon, was das
Programm will und braucht, aber die Schnittstellen sind insofern
inkompatibel, als dass die Methodennamen nicht passen und die
Signaturen nicht passen.
Wenn Sie nun einfach eine Klasse dazwischen packen können, die, und
jetzt kommt die spezifische Eigenschaft des Adapters, die die
Schnittstelle einer Klasse so ändert, so adaptiert, dass ein
Benutzerprogramm die Schnittstelle vorfindet, die es erwartet, dann
ist das Ihre Adapterklasse.
Sie haben also eine Adapterklasse geschrieben, die eigentlich nichts
anderes macht, als Aufrufe entgegenzunehmen und auf die richtige
Signatur, auf die richtige Methode der eigentlich die Funktionalität
implementierenden Klasse übersetzt.
Sie adaptiert den Zugriff an der Stelle.
Den Beobachter haben Sie von der Funktionalität her schon
kennengelernt, da geht es um Benachrichtigung von Änderungen.
Die Brücke ist ein bisschen zu komplex, ist im freien Raum zu
erklären.
Der Iterator, Sie denken es sich geht, ist eine generische Methode,
wie Sie Elemente einer Menge aufzählen können.
Zum Beispiel könnten Sie in der Übungsblattaufgabe mit einem Iterator
die Bilder aufzählen lassen oder sowas.
Oder die Blätter Ihres Clusterbaumes aufzählen lassen oder sowas.
Das sind typische Iteratoraufgaben.
Der Stellvertreter ist in der Implementierung so ähnlich wie der
Adapter.
Er steht also zwischen der Benutzung und der Implementierung von
etwas.
Er hat aber im Gegensatz zum Adapter nicht die Aufgabe, die
Schnittstelle zu ändern, also den Zugriff irgendwie zu erleichtern
oder da eine Adaption vorzunehmen.
Sondern der Stellvertreter, der Proxy, der hat die Aufgabe, Zugriffe
zu ändern im Sinne von umzuleiten.
Also der Punkt ist, der Stellvertreter fügt Funktionalität beim
Adaptieren hinzu, so könnten Sie sagen.
Und der Vermittler ist auch eine Klasse, die zwischen zwei Klassen
steht und zwischen diesen beiden Klassen vermittelt.
Im Unterschied zum Stellvertreter, unserem Adapter, steckt im
Vermittler noch Logik drin, wie diese Vermittlung zu erfolgen hat.
Sie können sich den so ein bisschen wie den Controller vorstellen, der
zwischen dem View und dem Model sitzt.
Was haben wir noch für Entwurfsmuster?
Wir haben Variantenmuster.
Die stellen Ihnen, wie der Name schon sagt, Varianten von
Implementierungen bereit.
Sie können also einen Blumenstrauß von Varianten aus möglicherweise
den gleichen Bas-Klassen erstellen.
Die Idee dahinter ist natürlich, dass Sie die Gemeinsamkeiten aus den
Klassen irgendwie herausgezogen haben und in den Klassen nur noch die
Spezialitäten drin haben und die Varianten sind eben so allgemeine
Werkzeuge, wie Sie Varianten von ähnlichen Klassen erstellen können,
ohne dass Sie die groß anfassen müssen.
Das sind die Variantenmuster.
Die erkläre ich Ihnen jetzt nicht mehr ganz so im Detail.
Das sehen wir noch, wenn wir das Kapitel detaillierter anschauen.
Wir haben noch die Zustandshandhabungsmuster.
Das sind im Allgemeinen Muster, die, wie der Name schon sagt, in denen
speichern Sie etwas ab über den aktuellen Zustand Ihres Programms.
Ganz typisch, wenn Sie in einem Programm diesen Bearbeiten-Zurück-
oder Bearbeiten-Vorwärts- oder so haben, dann ist das in aller Regel,
steckt genau diese Familie von Entwurfsmustern dahinter, denn dieser
Zurückknopf speichert Ihnen den Zustand vor der letzten Aktion
irgendwo ab und den können Sie dann wiederherstellen.
Und hier lernen Sie mal kennen, wie Sie vielleicht so einen
Zurückknopf in Ihre Anwendung einbauen können.
Genau, das sind die Namen der Entwurfsmuster, die man dafür einsetzen
könnte.
Auch hier schauen wir uns später genauer an, was die im Einzelnen
charakteristisch macht.
Und dann haben wir noch Steuerungsmuster, wie der Name schon sagt.
Das wären Muster, die könnte man zum Beispiel in dem Controller, aus
dem Model-View-Controller, den wir schon gesehen haben, einsetzen.
Die steuern Ihnen den Ablauf Ihres Programms, die Kapseln Logik.
Da gehört zum Beispiel ein Master-Worker dazu.
Master-Worker verwechseln Sie bitte jetzt nicht mit dem
Architekturstil, der auf der großen Ebene dort etwas macht, sondern
innerhalb Ihres Programms haben Sie zwei Klassen und der eine ist der
Master und der andere ist der Worker.