Skip to content

Instantly share code, notes, and snippets.

@ueman
Last active January 2, 2017 10:30
Show Gist options
  • Save ueman/bd112011c99fb163cab17182d5705028 to your computer and use it in GitHub Desktop.
Save ueman/bd112011c99fb163cab17182d5705028 to your computer and use it in GitHub Desktop.
Adapter & Bridge Pattern

Adapter Pattern

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

Beispiel in Swift 3:

// 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()
    }
}

Beispiel in Java:

// 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();
    }
}

Bridge Pattern

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

Beispiel in Swift 3:

// 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")
    }
}

Beispiel in Java:

// 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");
    }
}

Sonstiges

Die hier vorgestellten Patterns stammen von der "Gang of Four". Sie wurden ursprünglich im Buch "Entwurfsmuster. Elemente wiederverwendbarer objektorientierter Software" vorgestellt.

Quellen:

Weitere Beispiele:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment