Adapter

Introduktion

Adapter är ett strukturmönster som låter objekt med inkompatibla gränssnitt samarbeta — som en reseadapter som låter en svensk kontakt fungera i ett amerikanskt eluttag.


Problem

Du bygger en app för börsanalys som laddar ner marknadsdata i XML-format och visar diagram för användaren.

Vid ett tillfälle vill du integrera ett kraftfullt analysbibliotek från tredje part. Men det finns en hake: biblioteket fungerar bara med data i JSON-format.

Du kan inte ändra biblioteket — det är låst kod. Att skriva om din app för att använda JSON istället för XML skulle kräva enorma ändringar i en fungerande kodbas. Du behöver en adapter.

Lösning

En Adapter är ett objekt som konverterar gränssnittet för ett objekt så att ett annat objekt kan förstå det. Adaptern lindar in Service-objektet och hanterar konverteringen bakom kulisserna — det inlindade objektet vet inte om att adaptern finns.

När ska Adapter användas?

  • När du vill använda en befintlig klass men dess gränssnitt inte matchar resten av din kod.
  • När du integrerar med tredjepartskod eller äldre system (legacy code) som du inte kan ändra.
  • När du vill återanvända befintliga klasser utan att modifiera dem.

Struktur

RollAnsvar
KlientArbetar med objekt via klientgränssnittet.
KlientgränssnittBeskriver protokollet som andra klasser måste följa.
ServiceDen användbara klassen med inkompatibelt gränssnitt (t.ex. tredjepartsbibliotek).
AdapterImplementerar klientgränssnittet och lindar in Service-objektet.

Exempel — XML till JSON (Java)

Scenariot: Din app producerar data i XML. Analysbiblioteket kräver JSON. Adaptern konverterar formatet transparent.

// ── Klientgränssnitt (din app förstår XML) ────────────────

public interface XmlDataSource {
    String fetchXml();
}

// ── Befintlig klass som producerar XML ────────────────────

public class MarketDataService implements XmlDataSource {
    @Override
    public String fetchXml() {
        return "<stock><symbol>AAPL</symbol><price>189.50</price><volume>52000000</volume></stock>";
    }
}

// ── Tredjepartsbibliotek som kräver JSON ──────────────────

public class AnalyticsLibrary {
    public void analyze(String json) {
        System.out.println("Analyserar JSON-data: " + json);
        // Komplex analyslogik som bara förstår JSON...
    }
}

// ── Adapter: konverterar XML → JSON ───────────────────────

public class XmlToJsonAdapter {
    private XmlDataSource xmlSource;
    private AnalyticsLibrary analytics;

    public XmlToJsonAdapter(XmlDataSource xmlSource) {
        this.xmlSource = xmlSource;
        this.analytics = new AnalyticsLibrary();
    }

    public void analyzeData() {
        String xml  = xmlSource.fetchXml();
        String json = convertXmlToJson(xml);
        analytics.analyze(json);
    }

    private String convertXmlToJson(String xml) {
        // Förenklad konvertering för demonstration
        String symbol = extractTag(xml, "symbol");
        String price  = extractTag(xml, "price");
        String volume = extractTag(xml, "volume");

        String json = String.format(
            "{\"symbol\":\"%s\",\"price\":%s,\"volume\":%s}",
            symbol, price, volume
        );

        System.out.println("XML in:  " + xml);
        System.out.println("JSON ut: " + json);
        return json;
    }

    private String extractTag(String xml, String tag) {
        int start = xml.indexOf("<" + tag + ">") + tag.length() + 2;
        int end   = xml.indexOf("</" + tag + ">");
        return xml.substring(start, end);
    }
}

// ── Klientkod ─────────────────────────────────────────────

public class Application {
    public static void main(String[] args) {
        XmlDataSource    marketData = new MarketDataService();
        XmlToJsonAdapter adapter    = new XmlToJsonAdapter(marketData);

        // Klienten använder adaptern — vet inte om konverteringen
        adapter.analyzeData();
    }
}

Output:

XML in:  <stock><symbol>AAPL</symbol><price>189.50</price><volume>52000000</volume></stock>
JSON ut: {"symbol":"AAPL","price":189.50,"volume":52000000}
Analyserar JSON-data: {"symbol":"AAPL","price":189.50,"volume":52000000}

Fördelar

  • Single Responsibility Principle — Konverteringslogiken är isolerad i adaptern.
  • Open/Closed Principle — Du kan introducera nya adaptrar utan att ändra klientkoden.
  • Du kan återanvända tredjepartsbibliotek som annars är inkompatibla.

Nackdelar

  • Kodkomplexiteten ökar — du lägger till nya klasser.
  • Ibland är det enklare att bara skriva om Service-klassen om du har tillgång till den.

Av Victor Hernandez från Bytebase.se