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)