21. Januar 2013

Entwurfsmuster: Adapter

Das Entwurfsmuster "Adapter", dient, wie der Name bereits andeutet, dazu, eine Schnittstelle einer anderen, inkompatiblen Schnittstelle anzupassen. Damit können Klassen zusammenarbeiten, die sonst aufgrund ihrer Schnittstellen nicht dazu in der Lage wären. 

Häufig wird man zu der Verwendung eines Adapters geradezu gezwungen, wenn man mit vorgegebenen Klassen, etwa die eines Frameworks, arbeiten muss, deren Schnittstelle aber nicht der benötigten entspricht. Andererseits kann man den Ansatz auch vorrauschauend nutzen, um etwa Klassen mit erhöhter Wiederverwendbarkeit zu erstellen, die im Folgenden mit beliebigen anderen Klassen zusammenarbeiten können. Auch denkbar wäre die Verwendung eines Adapters wenn verschiedene Implementierungen, die sich jedoch nur schwierig unter einer gemeinsamen Schnittstelle zusammenfassen lassen, genutzt werden sollen.

Bei einem Adapter handelt sich um ein Strukturmuster, das in zwei verschiedenen Varianten existiert: Einer klassenbasierten, die auf Mehrfachvererbung basiert, sowie einer objektorientierten, die auf Delegation setzt. Beide Varianten und ihre Besonderheiten sollen im Folgenden erläutert werden.

Klassenbasierter Adapter:

Da die Umsetzung eines klassenbasierten Adapters auf Mehrfachvererbung basiert, was von vielen Programmiersprachen, wie z.B. Java nicht unterstützt wird, kann er nur unter solchen Sprachen, die Merhfachvererbung anbieten, sinnvoll genutzt werden. 

Das folgende Klassendiagramm zeigt die Realisierung eines solchen Adapters, indem er sowohl von der Ziel-Schnittstelle, als auch von der bereits vorhandenen abgeleitet wird:

Das Klassendiagram eines klassenbasierten Adapters

Neben der Einschränkung, dass ein solcher Adapter nur in Programmiersprachen eingesetzt werden kann, die Mehrfachvererbung unterstützen, existiert noch der weitere Nachteil, dass, anders als bei dem objektorientierten Adapter, der im Folgenden noch vorgestellt werden wird, Unterklassen des anzupassenden Dienstes nicht automatisch von dem Adapter abgedeckt werden. Dieser Nachteil kann allerdings auch als Vorteil gewertet werden, da der Adapter genau einer einzigen Zielklasse angepasst ist und nur deren Verhalten überschreiben kann.

Objektorientierter Adapter:

Für alle Programmiersprachen, die keine Mehrfachvererbung anbieten, kommt nur diese Variante des Adapters in Frage. Dabei besitzt der Adapter, anstatt von der anzupassenden Klasse zu erben, eine Referenz auf diese. Das folgende Klassendiagramm verdeutlicht den Unterschied zu dem klassenbasierten Adapter:

Das Klassendiagramm eines objektorientierten Adapters

Die Referenz auf die anzupassende Klasse wird dabei in der Regel beim Konstruktoraufruf an den Adapter übergeben. Der folgende Beispielcode, soll das verdeutlichen:
 public interface Ziel {  
   
   void operation();  
   
 }  
   
 public class Adapter implements Ziel {  
   
   private Dienst dienst;  
   
   public Adapter(Dienst dienst) {  
     this.dienst = dienst;  
   }  
   
   @Override  
   public void operation() {  
     dienst.action();  
   }  
   
 }  
Neben dem offensichtlichen Vorteil, dass diese Adapter-Variante in jeder beliebigen Programmiersprache seinen Einsatz finden kann, kann auch die Eigenschaft, dass an den Adapter auch Unterklassen des anzupassenden Dienstes übergeben werden können, von Vorteil sein.

Generell bieten alle Adapter den Vorteil, dass der dahinterliegende Dienst und auch sie selbst zur Laufzeit ausgetauscht werden können, was Varianten verschiedener Implementierungen erlaubt. Nachteiligt wirkt sich aus, dass Adapter sehr komplex werden können, falls die zu adaptierende Schnittstelle dementsprechend groß ist, oder die Zielschnittstelle technisch stark von der bestehenden Schnittstelle abweicht.

Keine Kommentare:

Kommentar veröffentlichen