Grundlagen der Programmierung: Entwurfsmuster

Passt schon: Adapter

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
Möchte ein Objekt mit einem anderen kommunizieren, muss es dessen Schnittstelle kennen. Passt die erwartete Schnittstelle jedoch nicht zur tatsächlich vorgefundenen, kann ein "Adapter" Abhilfe schaffen.
06:21

Transkript

In diesem Video schauen wir uns das Entwurfsmuster Adapter an. Das Problem, das wir mit dem Adapter lösen möchten, ist folgendes. Wir haben ein Objekt. Das Objekt bietet eine bestimmte Schnittstelle an. Es ist aber nicht die, die ein Verwender des Objektes erwartet. Außerdem haben wir noch die Einschränkung, dass an keinem der beiden Beteiligten irgendwelche Veränderungen möglich sind. Wir können also weder einfach die Schnittstelle ändern, noch einfach den Client anpassen. Ein Beispiel, das Sie vielleicht alle kennen, ist folgendes. Ein Mobiltelefon lässt sich mit USB-Kabel aufladen. Das USB-Kabel passt aber nicht i die 32V-Steckdose. Weitere Beispiele eher aus dem Bereich Softwareentwicklung könnten sein: Wir haben eine Bibliothek, die wir verwenden und jetzt ersetzen wir die durch eine andere Bibliothek, die zwar dieselben Dienste anbietet aber mit anderen Klassen und Methoden. Eine andere Situation könnte sein, dass wir ein Altsystem haben, für das ein Client entwickelt werden soll. Dieses Altsystem soll dann aber demnächst durch ein neueres ersetzt werden und der Client soll auch mit dem neuen System kommunizieren können. Schauen wir uns dieses grundsätzliche Problem genauer an. Wir haben hier unser Dienstobjekt und dieses Dienstobjekt bietet eine bestimmte Schnittstelle an. Auf der anderen Seite haben wir einen Client und dieser Client erwartet eine Schnittstelle und wie Sie sehen können, sieht die anders aus als die angebotene Schnittstelle. Die Lösung dieses Problems ist nun unser Entwurfsmuster, nämlich der Adapter. Dieser Adapter kennt die Schnittstelle, die das Dienstobjekt anbietet, stellt nach außen hin aber die Schnittstelle zur Verfügung, die der Client erwartet. Dadurch kann dieser Adapter nun die Aufrufe des Client entgegennehmen und an das Dienstobjekt weiterreichen und die Antworten des Dienstobjektes dann an den Client zurückgeben. Für die Umsetzung dieses Musters gibt es grundsätzlich zwei Ansätze. Der eine basiert auf Vererbung, der andere auf Komposition. Schauen wir uns den vererbungsbasierten Ansatz an, den sogenannten Klassenadapter. Wir haben hier die Klasse, die den Dienst anbietet und der Adapter ist nun eine Unterklasse dieses Dienstes. Er erbt also die komplette Funktionalität unserer Dienstklasse. Gleichzeitig implementiert der Adapter die vom Client erwartete Schnittstelle. Auf diese Weise kann er also die Anfragen des Client über diese Schnittstelle entgegennehmen und dann selbst beantworten, da er die Funktionalität von unserer Dienstklasse geerbt hat. Der Client kann nun diese Schnittstelle verwenden, ohne etwas davon wissen zu müssen, dass er eigentlich gar nicht mit dem Dienst redet sondern mit dem Adapter. So sieht also der Klassenadapter aus. Schauen wir uns nun den Objektadapter an, der auf Kompositionen basiert. Wir beginnen wieder mit unserem Dienstobjekt. Diesmal ist der Adapter aber ein separates Objekt, das weiß, wo das Dienstobjekt sich befindet und alle Aufrufe an dieses Dienstobjekt weiterleiten kann. Auch hier implementiert der Adapter wieder die erwartete Schnittstelle, über die dann der Client wieder auf den Adapter zugreifen kann, ohne wissen zu müssen, dass es ein Adapter ist. Der Adapter leitet die Aufrufe dann an das Dienstobjekt weiter. Fassen wir diese beiden Umsetzungsmöglichkeiten nochmal zusammen. Wir haben den klassenbasierten Adapter, der mit Vererbung arbeitet und wir haben den objektbasierten Adapter, der mit Kompositionen funktioniert. Da der Klassenadapter eine Unterklasse des Dienstes ist und gleichzeitig eine andere Schnittstelle anbieten soll, brauchen wir dafür entweder Mehrfachvererbung oder eben eine separate Schnittstelledefinition. Die Vererbungsbeziehung wird zur Entwicklungszeit festgelegt. Das heißt, die Verbindung mit dem Dienst ist statisch und kann zur Laufzeit nicht mehr geändert werden. Dadurch ist der Adapter mit der Dienstklasse, von der er erbt, fest verbunden und kann auch nicht meiner anderen Klasse zum Beispiel mit einer Unterklasse unseres Dienstes funktionieren. Sollten wir von der Dienstklasse gar nicht erben können oder dürfen, weil sie zum Beispiel in Java als final deklariert ist, dann ist der Objektadapter eine gute Alternative. Er leitet die Aufrufe lediglich an den Dienst weiter und Vererbung kommt hier gar nicht vor. Auch wenn es von der Dienstklasse mehrere Unterklassen gibt, mit denen der Adapter ebenfalls funktionieren soll, ist die Komposition vorzuziehen. Die Unterklassen unseres Dienstes haben immer mindestens dieselben Methoden wie der Dienst selbst. Der Adapter kann sich also auch mit den Instanzen diese Klassen problemlos verständigen. Schließlich besteht aufgrund der Kompositionsbeziehung die Möglichkeit, dass der Adapter erst zur Laufzeit mit dem entsprechenden Dienst verknüpft wird. Wie jedes Entwurfsmuster so bietet natürlich auch der Adapter Vor- und Nachteile. Der Vorteil und gleichzeitig auch der eigentliche Zweck des Adapters besteht darin, dass wir existierende Klassen benutzen können, ohne Änderungen daran vornehmen zu müssen, obwohl ihre Schnittstelle eigentlich nicht passt. Außerdem bietet ein Adapter die Möglichkeit, dass verschiedene Clients auf dasselbe Dienstobjekt über unterschiedliche Schnittstelle zugreifen können. Das wären dann zum Beispiel Protokolladapter. Ein Beispiel aus der Praxis wäre hier, dass wir in unserer Unternehmensanwendung ein Dienstobjekt haben, das wir nach außen als Webservice zur Verfügung stellen möchten. Diese zusätzliche Funktionalität direkt in unser Dienstobjekt hineinzupacken, würde bedeuten, dass wir technischen mit fachlichem Code vermischen und unser Objekt so abhängig von Webservices machen. Ein Protokolladapter könnte hier nun die Webservice-Schnittstelle nach außen zur Verfügung stellen und unser Dienstobjekt bleibt bei seiner reinen Fachlichkeit. Möchten wir dann im weiteren Verlauf unseres Projekts den Zugriff auf unseren Dienst zum Beispiel auch per Rest ermöglichen, dann müssen wir am bestehenden System überhaupt nichts ändern, sondern fügen einfach einen zweiten Protokolladapter hinzu. Diesmal eben für Rest. Wie gesagt, gibt es neben Vorteilen natürlich immer auch Nachteile. Ein wesentlicher Punkt beim Adapter ist, dass der Aufwand zwar minimal sein kann, aber möglicherweise auch sehr beträchtlich. Im einfachsten Fall heißen die Methoden einfach nur anders. Es kann aber auch sein, dass der Client Methoden erwartet, die der Dienst eigentlich gar nicht anbietet. Dann muss der Adapter diese zusätzlichen Funktionalitäten selbst zur Verfügung stellen. Und schließlich kann es noch sein, dass der Dienst eigentlich aus vielen verschiedenen Teilen besteht, die eigentlich so ganz anders aussehen, als das was der Client erwartet. Dann kann die Entwicklung eines solchen Adapters sehr aufwendig sein und der Adapter besteht dann in der Regel auch nicht mehr aus einem einzelnen Objekt sondern eher aus einer Gruppe von Objekten, die sich verschiedene Aufgaben teilen. Ein weiterer Punkt ist, dass der Klassenadapter prinzipiell Mehrfachvererbung erfordert und Mehrfachvererbung nicht in allen Programmiersprachen zur Verfügung steht. Das ist allerdings kein Problem, wenn die Client erwartete Schnittstelle tatsächlich auch als separate Schnittstellenbeschreibung vorliegt. In Java also als separates Interface. Dann können wir auf die Mehrfachvererbung natürlich verzichten. Und schließlich gibt es beim Objektadapter eine zusätzliche Indirektion, da jeder Aufruf des Clients an den Adapter geht und der Adapter dann die Aufrufe an den eigentlichen Dienst weiterleitet. Das kann dann prinzipiell zu verlängerten Antwortzeiten führen. Damit haben Sie nun gesehen, was ein Adapter ist, welche Art von Problemen Sie damit lösen können und mit welchen Konsequenzen Sie rechnen müssen, wenn Sie diesen Adapter einsetzen.

Grundlagen der Programmierung: Entwurfsmuster

Erhalten Sie einen fundierten Einstieg in das Thema Entwurfsmuster (Design Patterns) und erfahren Sie, wie Sie Entwurfsmuster in objektorientierten Programmiersprechen umsetzen.

2 Std. 49 min (33 Videos)
Derzeit sind keine Feedbacks vorhanden...
 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!