Es wird häufig auch Wrapper oder Hüllenklasse genannt.
Problem:
- Man hat eine Klasse oder ein Interface welches nicht kompatibel mit der Vorhandenen Codebase oder dem angestrebten Aufbau ist
Lösung:
- Das Adapter Pattern
Einen Adapter kann man auf zwei Arten realisieren:
- als Adapter mit Delegation (auch Objektadapter genannt)
- der Vorteil hier ist, dass man den Adapter und die dahinter liegende Funktionalität einfach austauschen kann
- klassisches Beispiel für einen Wrapper
- als Adapter durch Vererbung (auch Klassenadapter genannt)
- hier wird die gewünschnte Schnittstelle durch Selbstdelegation erreicht
- in dem Adapter wird die Funktionalität der Super-Klasse geerbt und zusätzlich wird die gewünschte Funktionalität implementiert
// Das gewünschte Interface
protocol UsedInterface {
public func operation()
}
// Die zu adaptierende Klasse
class Adaptee {
func adaptetOperation() {
print("Something")
}
}
// Die Implementation der Adapterklasse
// als Adapter mit Delegation
class ObjectAdapter: UsedInterface {
// Die zu adaptierende Klasse
private let adaptee = Adaptee()
// Implementation des Interfaces
func operation() {
adaptee.adaptetOperation()
}
}
// Die Implementation der Adapterklasse
// als Adapter durch Vererbung
class ClassAdapter: Adaptee, UsedInterface {
// Implementation des Interfaces
func operation() {
self.adaptetOperation()
}
}
// Das gewünschte Interface
interface UsedInterface {
public void operation();
}
// Die zu adaptierende Klasse
public class Adaptee {
public void adaptetOperation() {
System.out.println("Something");
}
}
// Die Implementation der Adapterklasse
// als Adapter mit Delegation
public class ObjectAdapter implements UsedInterface {
// Die zu adaptierende Klasse
private Adaptee adaptee = new Adaptee();
// Implementation des Interfaces
@Override
public void operation() {
adaptee.adaptetOperation();
}
}
// Die Implementation der Adapterklasse
// als Adapter durch Vererbung
public class ClassAdapter extends Adaptee implements UsedInterface {
// Implementation des Interfaces
@Override
private void operation() {
super.adaptetOperation();
}
}
Problem:
- Die Implementation von Schnittstellen wird durch Vererbung realisiert. Dadurch kann es sein, dass in der Vererbungshirachie Vererbungen von Interfaces oder Implementationen zu finden sind. So kann der Quellcode schnell unübersichtlich werden
Lösung:
- Ziel des Bridge Patterns ist es die Abstraktion (Schnittstelle bzw. Interface) von ihrer Implementation zu trennen
- Verwendet wird das Bridge Pattern wenn die dauerhafte Verbindung zwischen dem Interface bzw. der Abstraktion und Implementierung verhindert werden soll
- Ein weiterer Vorteil ist die Austauschbarkeit der Implementation zur Laufzeit
Beispiele:
- JDBC
- man hat ein Interface und verschiedene Implementationen für verschiedene Datenbanken
- Retrofit
- man kann verschiedene Parser einsetzten
// Ausführung
class MainClass {
func init() {
var controle = RemoteControle(Radio())
controle.turnOn() // Ausgabe: radio turned on
controle.appliance = TV()
controle.turnOn() // Ausgabe: tv turned on
}
}
protocol Appliance {
func run()
}
protocol Switch {
var appliance: Appliance?
func turnOn()
}
class RemoteControle: Switch {
var appliance: Appliance!
init(_ appliance: Appliance) {
self.appliance = appliance
}
func turnOn() {
self.appliance.run()
}
}
class TV: Appliance {
func run() {
print("tv turned on")
}
}
class Radio: Appliance {
func run() {
print("radio turned on")
}
}
// Ausführung
public class MainClass {
public static void main(String args[]) {
RemoteControle controle = new RemoteControle(new Radio());
controle.turnOn(); // Ausgabe: radio turned on
controle.setAppliance(new TV());
controle.turnOn() // Ausgabe: tv turned on
}
}
interface Appliance {
public void run();
}
interface Switch {
public void setAppliance(Appliance appliance);
public void turnOn();
}
public class RemoteControle implements Switch {
private Appliance appliance;
public RemoteControle(Appliance appliance) {
this.appliance = appliance;
}
@Override
public void setAppliance(Appliance appliance){
this.appliance = appliance;
}
@Override
public void turnOn() {
this.appliance.run();
}
}
public class TV implements Appliance {
@Override
public void run() {
System.out.println("tv turned on");
}
}
public class Radio implements Appliance {
@Override
public void run() {
System.out.println("radio turned on");
}
}
Die hier vorgestellten Patterns stammen von der "Gang of Four". Sie wurden ursprünglich im Buch "Entwurfsmuster. Elemente wiederverwendbarer objektorientierter Software" vorgestellt.
Quellen:
- Bridge Wikipedia
- Adapter Wikipedia
- Design Patterns in Swift (Swift Syntax ist veraltet aber die Beispiele sind ganz nett)
Weitere Beispiele: