Bessere Entwürfe durch Schnittstellen
Flexiblere Architekturentwürfe
Alexander Scheb: Das leistungsfähigste Konzept
von Java sind die sogenannten Schnittstellen(Interfaces). Da es sich um ein
enorm komplexes Konzept handelt, wissen die wenigsten Programmierer mit diesem
umzugehen. Für leistungsfähige und langlebige Architekturen sind diese
Schnittstellen jedoch lebensnotwendig. Schließlich gibt man hierüber den
Entwurf eines Softwaresystems vor. Daher widmet der Beitrag dem Einsatz solcher
Schnittstellen für den Praxisalltag.
Einführung
Ein
Java Programm wird tradionell so entwickelt, daß man
zuerst die Klassen festlegt und dessen Beziehungen zueinander festlegt. Erst
danach implementiert man die Methoden nach dem Klassenentwurf. Wenn dieser
Prozeß sorgfältig ausführt wurde, kann man später verschiedene Entwurfsaspekte
auch noch verändern, ohne daß sich dies auf den Entwurf auswirkt. Je weiter man
jedoch jetzt an dem Entwurf programmiert, wird man feststellen, daß das
Klassenmodell einem selbst zu viele Einschränkungen auferlegt, da es rein
statischer Natur ist. Die Lösung hierzu ist die Verwendung sogenannter
Schnittstellen. Denn die Schnittstellen stellen einen breiteres Spektrum der
Einsetzbarkeit auf der Klassen- und Objektentwufebene
dar, als dies je Klassen und Methoden alleine leisten könnten. Je mehr
Erfahrung man im Entwurf von Systemen gesammelt hat, desto mehr wird einem
bewußt, daß die reine Simplizität der Klassenhierachie
eingeschränkt ist. Dies wird besonders deutlich, wenn Sie ein allgemeines Verhalten
nutzen wollen, die von Klassen in verschiedenen Verzeigungen derselben Struktur
verwendet werden. Das allgemeine Verhalten stellt sich hier als Problem dar, da
es in die strenge Klassenhierarchie nicht hineinpaßt.
Die
Probleme werden werden anhand eines Beispiel noch
klarer. Stellen wir uns eine
Klassenhierarchie für Fahrzeuge vor. Als Oberklasse fungiert dort die Klasse
Fahrzeug. Darunter wird grundlegend zwischen motorenbetriebenen und
personenbetriebenen Fahrzeugen unterschieden. Unter der Rubrik
„personenbetrieben“ würde beispielsweise an Fahrrad fallen, während für
„motorenbetrieben“ zwischen Fahrzeugen mit zwei und vier Rädern unterschieden
wird. Unter dem Zweirädern käme dann ein Motorrad in Frage, während ein
normales Auto mit vier Rädern daher kommt.
Nun
gibt es aber nicht nur deutsche Autos und Motorräder sondern auch
amerikanische. Dies gilt es durch bestimmte Eigenschaften zu unterscheiden Über
welche Spezialeigenschaften diese verfügen, ist innerhalb der Klasse zu
definieren. Da diese jedoch in verschieden Bereichen der Klassenhierarchie
liegen, läßt sich für beide keine gemeinsame Superklasse erstellen.
Es
können auch nicht die Eigenschaften eines amerikanische Autos in der Hierarchie
hinaufgesetzt werden, dies wäre ein Bruch in Vererbungslogik. Schließlich haben
nicht alle Autos eine amerikanische Verhaltensweise. Um das Verhalten der
beiden Klassen nicht zu duplizieren, muß eine andere Lösung her.
#Abbildung: Eine
Klassenhierarchie von Fahrzeugen
Andere
objektorientierte Sprachen wie C++ bieten breitere Vererbungsmöglichkeiten,
womit sich solche Probleme lösen lassen. Dort läßt sich durch Mehrfachvererbung
das Verhalten und die Attribute von mehreren Klassen erben. Jedoch besteht das
Problem der Mehrfachvererbung darin, daß eine Programmiersprache zu mächtig
wird. Dabei wird die Lernkurve zu hoch und die Implementierung und dessen
Verwendung einfach zu kompliziert. Den Überblick über Methodenaufrufe und die Klassenhierachieaufbau wird um einiges kompizierter.
Dabei sind dann Zweideutigkeiten nicht zu vermeiden und dieses führt schnell zu
Programmfehlern. Dabei hat man sich entschieden, der Einfachvererbung den
Vorrang zu geben.
Wie
läßt sich jedoch innerhalb einer Klassenhierarchie jetzt ein allgemeines
Verhalten unterbringen, worüber mehrere mehreren Klassen dann verfügen.
Hier
verfügt die Sprache Java über eine
weitere Hierarchieebene, eben der Schnittstellen Hierachie.
Dessen Aufbau weicht von der Hauptklassenhierarchie ab. Diese stellt eine Hierachie zur Definition von Verhalten aus mehreren
Klassen. Dabei kann diesse nicht nur von einer
Superklasse, sondern von weiteren benötigten Klassen aus anderen
Hierarchieebenen erben, bis das gewünschte Klassenverhalten realisiert ist.
Eine Java Schnittstelle ist eine Zusammenfassung aus abstrakten Verhalten.
Diese Verhaltensweisen können aus mehreren Klassen und erlaubt so ein Klassenverhalten zu
schaffen, was die Superklasse nicht unterstützt. Somit sind Java Schnittstellen
komplett abstrakt und beschreiben den Entwurf aber nicht dessen spätere
Implementierung. Die Schnittstellen wird dann innerhalb der Java
Klassenhierarchie implementiert.
Über
eine Schnittstelle läßt sich dann ein Spezialverhalten einer Klasse definieren,
wozu die Klasse in der Lage sein soll. Dies kann beispielsweise sein das eine
Klasse mehrprozeß-fähig ist
oder
abgespeichert werden kann oder von anderen Klassen observiert werden kann.
Von
solche Spezialeigenschaften wird innerhalb der Klassenbibliothek reichlich
Gebrauch gemacht. So läßt sich leicht vorgehen das ein Auto oder Motorrad beispeilsweise an amerikanisches Verhalten haben und wieder
anderen die Eigenschaft deutsch eben aufweisen.
Nutzen der Schnittstellen im Alltag
Sicherlich
werden Sie sich fragen, warum Sie im Alltag solche Schnittstellen verwenden
sollten. Die Praxis hat in den letzten Jahren gezeigt, das es klassische
Barrieren bei der Softwareentwicklung existieren. Bei folgenden Aspekten treten
immer wieder Schwierigkeiten auf:
1. Flexiblität
Im
Praxisalltag ist die Software oft an neue Anforderungen anzupassen und daher
sollte dessen
Aufbau
möglichst flexibel aufgebaut sein.
2. Erweiterbarkeit
+
Oft ist Software mittels eines Add In zu erweitern und muß deshalb eine offene
Architektur besitzen. (muß deshalb offen konzipiert sein)
3. Austauschbarkeit
+
Manchmal ist die Klasse eines Objekts durch eine andere mit gleicher
Methodensignatur auszutauschen
Speziell
beim objektorientierten Entwurf gibt es hierbei Probleme. Denn alle Objekte
kommunizierten mit irgendeinem Objekt, um etwas zu bewerkstelligen. Dabei kann
ein Objekt eine Antwort liefern oder berechnet das Ergebnis komplett selbst,
wobei lediglich immer ein Objekt nachfragte. Daher nehmen Interaktionsdiagramme
auch eine so bedeutende Rolle ein, weil diese zeitliche Reihenfolgen von
Interaktionen zwischen Objekten modellieren. Auf der Ebene der Klassendiagramme
und Szenarien existiert das Problem das ein Objekt eine spezielle Klasse
repräsentiert. Jedoch kann dies kaum für eine Wiederverwendung förderlich sein,
da Objekte einer Klasse mit Objekten einer anderen Klasse direkt verbunden sein
müssen. Dies gilt es aufzuheben. Viel nützlicher wäre es mit einer Gruppe von
Klassen zu arbeiten. Nur so kann ein konsequente Wiederverwendung ermöglicht
werden.
Genau
dasselbe Problem begegnen wir bei der Austauschbarkeit von Komponenten. Wenn
eine andere Klasse eines Objektes hinzufügen wird, treten schon Probleme auf.
Es existiert einfach keine Austauschbarkeit hier. Als Lösung muß eine
Assoziation geschaffen, ein anderes Szenario aufgebaut und entsprechend implementiert
werden. Das Problem hierbei ist nur, daß jede Assoziation und jede
Nachrichtenversendung hart-verbunden mit einem Objekt einer konkreten Klasse
oder Klassenhierachie ist. Durch diesen Umstand
verhindert man die Austauschbarkeit genauso wie die Erweiterbarkeit und
Flexibilität. Denn traditionell sind die Objekte eines Szenarios hart miteinander
verbunden. Aber wenn die Nachrichten
„wenn kenne ich?“ und „mit wem kommuniziere ich?“ direkt mit einer Klasse eins
Objekts verbunden sind, kann keine Austauschbarkeit erfolgen. Hierzu gibt es
zwar eine Lösung, die jedoch zu umständlich ist. Das Hinzufügen der neuen
Klasse bedeutet, daß neue Assoziationen und Szenerien zusätzliche Änderung an
anderen Klassen vorzunehmen sind.
Wir
benötigen in diesem Fall einfach mehr Flexibilität, Erweiterbarkeit und
Austauschbarkeit. Eine Lösung die uns erlaubt eine neue Klasse hinzuzufügen,
ohne dabei Änderung an Assoziationen oder Nachrichtenversendung vornehmen zu
müssen. Eine konsequente Lösung ist nur mittels Schnittstellen zu realisieren.
Denn Schnittstellen befreit uns von der Denkweise von konkreten Objekten und
ihren Beziehungen und Interaktionen mit anderen Objekten. Für unser Problem muß
die Antwort lauten: Ich beachte nicht welche Art von Objekt ich eine Beziehung
habe, solange das Objekt die verlangte Schnittstelle implementiert. Genauso
brauche ich bei der Nachrichtenversendung nicht darauf zu beachten, welcher Typ
ein Objekt ist, solange das Objekt das benötigte Schnittstelle implementiert.
So erhält so viel flexiblere Assoziationen und Nachrichtenversendungen. Denn
diese gehen an Objekte jeder Klasse die das Schnittstelle implementiert, das
gerade benötigt wird, anstatt zu Objekten einer einzelnen Klasse.
Schnittstellen befreit von Kopplungen und macht Teile des Entwurfs austauschbar
und erhöht die Wiederverwendbarkeit und stellt so die höchste Entwurfsstufe
dar.
Schnittstellen
drücken Beziehungen wie „ist ein Typ von“ als „ist eine Art die die
Schnittstelle unterstützt“ aus. Dies erlaubt Kategorisierungsvorteile bei der Vererbung und entfernt die Hauptschwäche
der Vererbung (schwache Kapselung innerhalb der Klassenhierarchie). Auch bei
Komposition sind Schnittstellen
unersetzlich. Denn die Kompositionen werden hierüber ebenfalls flexibler, erweiterbarer
und austauschbar. Denn die Zusammenstellung der Objekte erfolgt über das Schnittstelle,
anstatt über hart-verdrahtete zu nur einem Typ von Objekt.
Dies
ist alles auf der Basis der Trennung der Methodensignaturen von den Methoden
Implementation möglich geworden. Je größer ein System ist und je längere
Lebenszeit es hinter sich hat, desto bedeutender wird das Konzept der
Java-Schnittstellen.
Anwendbarkeit von Schnittstellen
Die
Frage ist jetzt nur, in welchen Fällen man Schnittstellen vorsätzlich nehmen
sollte.
Die
Unterbringung jeglicher Methodensignaturen in ein separates Schnittstellen wäre
übertrieben. Dadurch machen Sie das Objektmodell nur unnötig komplex und ihre Szenarion werden zu abstrakt. Es existieren verschiedene
Kontexte in denen Schnittstellen gut Dienste leisten. Die folgenden vier
Kontexte kommen in der Praxis jedoch am häufigsten vor:
1.)
Anwendung bei Repeaters
2.)
Anwendung bei Proxy
3.)
Einsatz bei ähnlichen Anwendungen
4.)
Anwendung bei zukünftigen Erweiterungen
Die
einfachste Form ist die grundlegenden Methodensignaturen zu suchen, die in
etlichen Klassen wiederholt (repeat) vorkommen und
dafür ein Schnittstelle zu schreiben. Dadurch wird ein höherer
Abstraktionslevel im Klassendiagramm erreicht.
Anhand
des Klassendiagramm werden die Fälle ersichtlich, wo am besten eine andere
Klasse eignet, um gewisse Fragen über die eigentliche Klasse zu beantworten.
Dieser agiert somit als Proxy-Klasse. Hiermit werden die Interaktionsdiagramm
leichter verständlich.
Da
ähnliche Anwendungen auch über fast identische Methoden verfügen, eignen sich
diese, um sie in einem Schnittstelle unterzubringen. So lassen sich Algorithmen
leicht austauschen. Wenn man weiß, an welchen Stellen der Software künftig
Änderungen zu erwachten sind, sollte man diese Stellen ein Schnittstelle
anlegen. So wird die komplette Anwendung leichter anpassbar.
Mit
Schnittstellen liefert jedes Szenario eine größere Wirkung der
Verständlichkeit, Redundanzen zwischen verschiedenen Szenarien werden weniger.
Definieren einer Schnittstelle
Um
Schnittstellen später verwenden zu können sind zwei Grundvoraussetzungen nötig.
Die Spezifikation der Schnittstelle und die Angabe welche Klasse diese
Schnittstelle implementiert.
Eine
Schnittstelle wird ähnlich wie eine Klasse definiert. Hierbei wird jedoch das
Schlüsselwort "Schnittstelle" als Kennung eingesetzt. Jedoch sind nur
Konstanten und abstrakten Methoden in einer Schnittstelle erlaubt. Dabei ist
jede Schnittstelle standardmäßig als abstrakt definiert. Beim Anlegen solcher
Schnittstellen muß ebenfalls der Rückgabetyp und Parametertypen angegeben
werden.
Als
Beispiel modellieren wir einen allgemeinen Sachverhalt. Beispielsweise hat
jeder Kunde in einem Softwaresystem eine konkrete Adresse. In der Regel sind
dort Daten für Straße, PLZ und Ort hinterlegt. Wenn dieser jedoch umzieht,
müssen entsprechende Methoden die Änderung der Adresse durchführen.
Unsere
Schnittstelle für die Abstraktion heißt deshalb „IAdresse“
und verfügt über zwei Methodensignaturen zum Setzen und Holen des aktuellen
Adressen. Dabei kennzeichnen wir die Schnittstelle durch den Präfix „I“ im
Schnittstellenamen. Diese Dienste muß der Implementierer
später realisieren, um als würdige Klasse angesehen zu werden. Innerhalb des
UML-Diagramm wird die Schnittstelle durch einen kursiven Schriftstil
gekennzeichnet. Solch ein Diagramm sieht dann so aus:
#UML-Diagramm1:
Unser Schnittstelle“IAdresse“#
Nun
muß natürlich eine Klasse dieses Schnittstelle noch implementieren. Die
Referenzklasse
die die „IAdresse“ Schnittstelle implementiert, verspricht die „setze“ und „hole“ Methoden zu implementieren. Die „hole“ Methode gibt dabei die Adresse des Objektes zurück, während „setze“ eine neue Adresse für das Objekt setzt. Im UML-Diagramm zeigt die Pfeilrichtung an, von welcher Schnittstelle die Klasse eine Implementierung vornimmt. So beschreibt das „IAdresse“ Schnittstelle eine Standardprotokoll um mit Objekten die dieses Schnittstelle implementieren, zu kommunizieren.
#UML-Diagramm2:
Das Schnittstelle mit dem Implementierer#
Somit
ist ein Objekt jeder Klasse in der Lage ein „IAdresse“
Objekt zu verwalten. So können wir das „IAdresse“
Objekt nach seiner Adresse befragen, ohne dabei Rücksicht nehmen zu müssen,
welcher Klasse dieses Objekt eigentlich angehört. Dies wird in der Fachwelt als
entkoppelte Kommunikation bezeichnet.
#Code: Das Schnittstelle#
interface IAdresse {
static final String ort;
static final String strasse;
static final String plz;
public String holeOrt ();
public String setzeOrt(String ort);
#Code: So sieht die Implementation der Klassen dann aus:#
Class MeineAdresse implements Iadresse
{
String ort = „München“;
String strasse = „Haupstr.124“;
String plz = „80132“;
Public String holeOrt() {
Return ort; }
Public void setzeOrt (String ort) {
This.ort = ort; }
}
Public String holeStrasse() {
Return strasse; }
Public void setzeStrasse (String strasse) {
This.strasse = strasse; }
Public String holePlz() {
Return plz; }
Public void setzePlz (String plz) {
This.plz = plz; }
}
Besonders
bedeutetungsvoll ist, daß man beinahe überall anstelle
einer Klasse auch eine Schnittstelle verwenden kann. Es kann also eine Variable
als Schnittstellentyp deklariert werden.
Runnable einRunnableObjekt = new
MeineEigeneKlasse();
Dies
bedeutet, daß von jedem Objekt, auf welches sich die Variable bezieht,
angenommen wird, daß diese die Schnittstelle implementiert hat. Dies bedeutet,
daß es alle Methoden versteht, die von der Schnittstelle angegeben wird. In
unserem Fall bedeutet dies, daß wird einRunnableObjekt.run()
ausführen können, weil ein einRunnableObjekt ein
Objekt vom Typ Runnable enthält.
Jetzt kommt das wirklich interessante und zeigt die Leistungsfähigkeit einer Schnittstelle gut. Denn Schnittstellen lassen sich genauso wie Klassen verwenden.
#Code: Verwendung der Schnittstelle#
class XYZKlasse {
IAdresse padr; // Die Referenzvariable zur Schnittstelle
// Verwaltung aller Adressen von Typ „Iadresse“
Public void setzeAdresse (IAdresse adresse) {
This.padr = adresse; }
}
Schnittstellen als Stellvertreter
Die
Nützlichkeit von Schnittstellen läßt sich gut bei Entwurfsmustern zeigen. Da
bei jedem Entwurfsmuster muß man zuerst einmal festlegen, welche Rollen die
einzelnen Klasse bei diesem Entwurfsmuster spielen sollen und über welche
Methoden diese dann für ihre Aufgabe verfügen müssen. Der Entwurf läßt sich
mittels Schnittstellen optimal vorgeben. Wir stellen Ihnen hier das
Entwurfsmuster „Proxy“ vor.
Der
Proxy hat die Aufgabe eine andere Klasse zu vertreten und dessen
Funktionalitäten nach außen bereitzustellen. Dabei ist der Proxy als
Stellvertreter im Auftrag eines anderen unterwegs.
Die
Methodensignaturen der eigentlich anzusteuernden Klasse wird in einer Proxy
Klasse untergebracht. Stellen Sie sich hierzu ein Personenauskunftssystem vor,
welche die Personen mit ihren Anschriften verwaltet. Die Personklasse
hat eine „1:1“ Assozationsbeziehung zum
Adress-objekt. Wo immer ein Objekt (Person) eine 1:1 Beziehung hat mit einem
anderen Objekt, dann kann das Objekt (Person), als Proxy für eine anderen
(Adresse) agieren. Zum Verständnis des Proxys sehen wir uns dessen Arbeitsweise
genauer an. Nehmen wir an, Sie haben ein konkretes Person-Objekt identifiziert
und wollen nun dessen Wohnort und die Straße herausfinden. Welche Interaktionen
sind hierzu nötig. Um auf das Adress-Objekt zuzugreifen, muß das Person-Objekt
eine Referenz auf diese haben. Jedoch wäre dies immer wieder notwendig, um
Methoden innerhalb von Adresse aufzufen zu können.
Wie umständlich dies ist, können Sie aus dem Interaktionsdiagramm ersehen.
#Interaktionsdiagramm: Im
Szenario fragt ein Person das Adresse-Objekt, wo es konkret wohnt.
#Code:
// Beispiel ohne
Verwendung eines Proxys
Class Person {
// Die nötigen Attribute und Referenzen
private String nachname;
private String vorname;
private Adresse adresse;
// Der Konstruktur der Klasse mit einem Adress-Objekt versorgen
public Person(Adresse eineAdresse) {
this.adresse = eineAdresse; }
}
// Die Referenz auf ein
konkretes Zugriffsobjekt holen. Um diese
// Methoden aufzurufen ist ein konkretes Adress-Objekt als
// erstes notwendig.
public Adresse holeAdresse(){return adresse; }
// Die Zugriffsmethoden auf die Adress-Klasse
public String holeOrt() { return adresse.holeOrt(); }
public void setzeOrt(String einOrt)
{ adresse.setzeOrt(einOrt); }
}
// Interne Zugriffsmethoden auf die Variablen
Public String holeNachname() {
Return nachname; }
Public void setzeNachname (String nachname) {
This.nachname = nachname; }
}Public String holeVorname() {
Return vorname; }
Public void setzeVorname (String vorname) {
This.vorname = vorname; }
}
Wir
müssen also eine einfachere Lösung finden. Im Gegensatz zur ersten Lösung, ist
die Proxy Klasse in der Lage konkrete Fragen eines anderen Objektes zu
beantworten. Dies ist ja der Sinn des Proxies und
bietet so der Außenwelt ein praktisches
Schnittstelle an. Wie die Interaktion mit Proxy im Detail aussieht, sehen wir
anhand des Interaktionsdiagramms.
#Interaktionsdiagramm:
So
kann man Anfragen an den Proxy stellen, was man gerade benötigt. Die
eigentliche Arbeit wird im Hintergrund erledigt. Denn der Proxy interagiert
weiterhin mit dem Objekt (also Adresse), welcher er nach außen repräsentiert.
Mittels Proxy werden Szenarien einfacher, da dessem
Komplexität sinkt.
Das
Person-Objekt delegiert hierbei die Aufgabe direkt an das Adresse-Objekt. Jetzt
ist man erst in der Lage die Person direkt nach ihrem Wohnsitz zu fragen,
anstatt das Person-Objekt nach seinem Adresse-Objekt zu fragen und dann erst
mit diesem kommunzieren. So werden Szenarien wesentlich
einfacher durchschaubarer. Das folgenden Klassen „Person“ und „Adresse“ im
UML-Diagramm verwenden eine gemeisames Schnittstelle,
um den Proxy zu realisieren.
#Klassendiagramm: Die Klassen
Person und Adresse mit einer einzelnen gemeinsamen Schnittstelle.#
// Beispiel mit
Verwendung eines Proxys
Class Person {
// Die nötigen Attribute und Referenzen
private String nachname;
private String vorname;
private Adresse adresse;
// Der Konstruktur der Klasse mit einem Adress-Objekt versorgen
public Person(Adresse eineAdresse) {
this.adresse = eineAdresse; }
}
// Die Zugriffsmethoden
auf die Adress-Klasse
// Hierbei braucht kein
Adress-Objekt zuerst bezogen werden, um
// mit der Adresse
Klasse zu arbeiten. Alle Arbeiten werden
sofort
// an das Adress-Objekt delegiert.
public String holeOrt() { return this.adresse.holeOrt(); }
public void setzeOrt(String einOrt)
{ this.adresse.setzeOrt(einOrt); }
}
// Interne Zugriffsmethoden auf die Variablen
Public String holeNachname() {
Return nachname; }
Public void setzeNachname (String nachname) {
This.nachname = nachname; }
}Public String holeVorname() {
Return vorname; }
Public void setzeVorname (String vorname) {
This.vorname = vorname; }
}
Wann Schnittstellen verwenden?
An
dieser Stelle werden Sie sicherlich begeistert sein über die Möglichkeiten die
Schnittstellen eröffnen und sich nun die Frage stellen wann und wo Sie
Schnittstellen einsetzen sollten .
Denn
für ein voll-flexiblen Entwurf können Sie Schnittstellen überall verwenden.
1)
Ein Schnittstelle für jede Methodensignatur Zur Aufteilung von Signatur und Implementation
2)
Ein Schnittstelle an jedem Ende einer Assozation, so
daß nicht jede hart-verbunden mit einem Objekt einer Klasse ist
3)
Ein Schnittstelle für jeden Methodenaufruf, so daß Sie eine Alternativ
Algorithmus jederzeit einfügen können.
Die
Basisfrage dabei ist: Wollen Sie es nun sehr flexibel oder unflexibel haben.
Genau dieser Spielraum stellt das eigentliche Problem dar. Wenn Sie sich in den
Kopf gesetzt haben ein besonders flexibles System zu schaffen, werden Sie
definitiv zuviel Zeit und Budgetkosten und Ressourcen in Anspruch nehmen.
Jedoch wo macht es letztendlich Sinn Schnittstelle zu nutzen. Wo sollten Sie an
Entwurfsflexiblität verzichten. Hierzu eine Strategie
wie man am besten vorgeht.
Abschluß
Fügen
Sie Schnittstellen an jenen Punkten ein, an dem Sie Änderungen im Entwurf am
ehesten erwarten. Dabei arbeiten Sie nicht mehr mit Objekten einer konkreten
Klasse. Für
Den
Aufbau von Beziehungen, Versendung von Nachrichten und Aufruf von
Austauschmethoden gehen Sie nur noch über den Schnittstelle-Implementierer.
Wenn Sie eine Schnittstelle hinzufügen, bedenken dabei das dies ein
Austauschpunkt darstellt. Also eine Stelle, wo man jedes Objekt jeder Klasse
einstecken kann, daß die erforderliche Schnittstelle implementiert. Stellen Sie
sich ein Schnittstelle am besten als Steckdose vor. Nur weil diese
Schnittstelle genormt ist, kann man bestimmte Geräte die die Spezifikation
einhalten, auch betreiben.
Manchmal
kommt es vor, daß eine Methodenimplementation geändert werden muß. Dies bedeutet das Sie auf die
Fähigkeit angewiesen sind, einen
Algorithmus herauszunehmen und einen anderen hineinzustecken. In diesem
Fall brauchen wir einen konkreten Austauschpunkt, um das Verhalten abändern zu
können. Dies werden wir natürlich ganz praktisch finden. Fügen Sie am besten
als Modellierer dort Schnittstellen ein,
wo der Aufwand die Assoziationsflexiblitäten, Nachrichtenflexiblitäten und Algorithmusflexiblitäten
rechtfertigt.
Diagramm0.tif:
Die
Klasse Person mit Zugriffsmethoden und Assozation zur
Adresse Klasse
Diagramm1.tif:
Eine
Schnittstelle (Interface) nach der UML Spezifikation
Diagramm2.tif:
Die
Klasse MeineAdresse
verspricht die IAdresse Schnittstelle zu
implementieren
Diagramm3.tif:
Im
Szenario fragt ein Person-Objekt ein Adress-Objekt, wo es wohnt. (Beispiel ohne
Verwendung eines Proxies!)
Diagramm4A.tif:
So
kann man die Anfragen an einen Proxy stellen, anstatt der Original-Klasse
Diagramm4B.tif:
Im
Hintergrund arbeitet der Proxy doch mit der Original-Klasse
Diagramm5.tif:
Die
Klassen Person und Passagier mit einer gemeinsamen Schnittstelle (Interface)