Adapter Design Pattern

The Adapter Design Pattern is a structural design pattern that allows two incompatible interfaces to work together without modifying their code. It acts as a bridge between them.

Use Case of the Adapter Pattern

  • When you have a legacy system with existing code, but you want to integrate it with a new system without modifying the old code.

  • When you need to convert an interface into one that a client expects.

  • When working with third-party libraries that have different interfaces from what your application expects.

Components of the Adapter Pattern

Think of it as a translator between two different systems. It has 3 main parts:

  1. Target (Expected Interface)
    The interface your client code expects.

  2. Adaptee (Existing Class)

    The existing class that has an incompatible interface.

  3. Adapter (Bridge)

    The class that converts the Adaptee’s interface into the Target’s interface.

Adapter Design Pattern: Adapting JSON to an XML-Based System

Scenario

Imagine you have an existing system that only accepts XML data for processing. However, a new client (or application) wants to work with JSON data instead of XML. Since modifying the existing system is not an option, we need a bridge to make JSON data compatible with the XML-based system.

This is where the Adapter Design Pattern comes into play. We create an adapter that converts JSON data into XML format before passing it to the existing system.

Target(Expected Interface)

package DesignPatterns.structural.adapter.dataprocessor;
// Target
public interface JsonDataProcessor {
    public void processJsonData(String jsonData);
}

Adaptee(Existing Class)

package DesignPatterns.structural.adapter.dataprocessor;

public class XmlDataProcessor {
   public String getXmlData(String xmlData) {
       System.out.println("Processing Xml Data " + xmlData);
       return xmlData;
    }
}

Adapter(Bridge)

package DesignPatterns.structural.adapter.dataprocessor;
import org.json.*;

public class XmlToJsonAdapter implements JsonDataProcessor {
    //existing adaptee
    public XmlDataProcessor xmldataprocessor;

    XmlToJsonAdapter(XmlDataProcessor xmldataprocessor) {
        this.xmldataprocessor = xmldataprocessor;
    }

    @Override
    public void processJsonData(String jsonData) {
        System.out.println("Processing JSON Data: " + jsonData);

    }
    public void convertAndProcess(String xmlData) {
        String xml = xmldataprocessor.getXmlData(xmlData);
        JSONObject json = XML.toJSONObject(xml);  // Convert XML to JSON
        processJsonData(json.toString());
    }
}

Client

package DesignPatterns.structural.adapter.dataprocessor;

public class Client {
    public static void main(String[] args) {
        XmlDataProcessor xmldataprocessor = new XmlDataProcessor(); //old
        XmlToJsonAdapter xmlToJsonAdapter = new XmlToJsonAdapter(xmldataprocessor);
        String xmlData = "<employee><name>Nivedita</name><role>software developer</role></employee>";
        xmlToJsonAdapter.convertAndProcess(xmlData);
    }
}

Output:

Processing Xml Data <employee><name>Nivedita</name><role>software developer</role></employee>

Processing JSON Data: {"employee":{"role":"software developer","name":"Nivedita"}}

Pros (Advantages)

  1. Allows Code Reuse Without Modification

  • You can reuse existing code (like XmlDataProcessor) without changing it, making it easier to integrate old and new systems.
  1. Enhances Flexibility and Maintainability

  • The adapter acts as a bridge, making it easier to switch or extend data formats (e.g., adding support for CSV or YAML in the future).
  1. Improves Compatibility

  • It enables legacy systems to work with new codebases, reducing the need for major system rewrites.
  1. Follows the Open/Closed Principle (OCP)

  • The adapter provides a new implementation without modifying the existing XmlDataProcessor class.
  1. Encapsulates Complex Conversion Logic

  • Instead of spreading XML-to-JSON conversion logic throughout the codebase, the adapter centralizes it, keeping the code clean.

Cons (Disadvantages)

  1. Increases Code Complexity

  • Introducing an adapter adds an extra layer between the client and the adaptee, making the system more complex.
  1. Potential Performance Overhead

  • If conversion is expensive (like converting large XML files to JSON), the adapter might introduce a performance bottleneck.

    1. Not Always Necessary

  • If both classes can be modified, it's often better to refactor the original code rather than introduce an adapter.

  1. Can Lead to Too Many Adapters

  • If you integrate multiple incompatible interfaces, you may end up writing too many adapters, making the system harder to manage.