Design Pattern: Adapter Pattern in Java

Adapter pattern is used when an interface does not match the new requirement and the existing interface needs to adapt to another interface.

This article demonstrates Adapter pattern implementations in Java. Check the following examples.

Examples

Here are a few examples of the Adapter pattern in Java.

Example #1: API with File Adapter [Object Adapter]

Let’s consider a system that is used for calling API. The system can call native APIs and 3rd party APIs.

There is another part of the system that can perform operation on files.

Now, for some specific operations, if we want the file operations to behave as an API and be a part of the API system, then we need an adapter. Let’s see how can we implement the adapter.

File Interface

This is the interface that the file operation class implements. This file is preexisting before the adapter implementation.

// File.java

package com.bigboxcode.designpattern.adapter.api;

public interface File {

    String readFile();

    void writeFile(String input);

}

FileOp Class [implements File Interface]

Class that performs different file operations. This file is preexisting before the adapter implementation.

// FileOp.java

package com.bigboxcode.designpattern.adapter.api;

public class FileOp implements File{

    public String readFile() {
        // Code to read from file

        System.out.println("Reading from file");

        return "some dummy response read from file";
    }

    public void writeFile(String input) {
        // Write to file related code here

        System.out.println("Writing to file: " + input);
    }
}

API Interface

Interface for all API classes. This file is preexisting before the adapter implementation.

package com.bigboxcode.designpattern.adapter.api;

public interface Api {

    String fetchData();

    void sendData(String data);

}

Native API Class [implements Api interface]

Native API class (dummy) as an implement of the API interface, so this can be used as an API without any change.

This file is preexisting before the adapter implementation.

// NativeApi.java

package com.bigboxcode.designpattern.adapter.api;

public class NativeApi implements Api {
    @Override
    public String fetchData() {
        // code to fetch data from native API

        System.out.println("Fetching data from Native API");

        return "Data read from Native Api";
    }

    @Override
    public void sendData(String data) {
        // code to send data to native API

        System.out.println("Sending data to Native API: " + data);
    }
}

Third-Party API Class [implements Api interface]

3rd party API class (dummy) as an implement of the API interface, so this can be used as an API without any change.

This file is preexisting before the adapter implementation.

// ThirdPartApi.java

package com.bigboxcode.designpattern.adapter.api;

public class ThirdPartyApi implements Api {
    @Override
    public String fetchData() {
        // code to fetch data from Third Party API

        System.out.println("Fetching data from Third Party API");

        return "Data read from Third Party Api";
    }

    @Override
    public void sendData(String data) {
        // code to send data to Third Party API

        System.out.println("Sending data to Third Party API: " + data);
    }
}

New Requirement

For some API operations, we need to get data from files and also write data to files. Though these are file operations, but these operations need to be done as API operations.

The file operation class needs to adapt to the functionality of API. To achieve that we need an adapter that works as a bridge and enable API to perform file operations using file-related classes.

File Adapter

File adapter that we need to create, for implementing the new adapter required. Here are a few key things to notice here.

  • Adapter class implements the interface to which we need to adapt (API interface).
  • There is a variable that holds a File object.
  • The constructor accepts a File object as a parameter and saves that to the class variable.
  • For the operations, the adapter uses methods from the file object.
// FileAdapter.java

package com.bigboxcode.designpattern.adapter.api;

public class FileAdapter implements Api {

    private final File file;

    public FileAdapter(File file) {
        this.file = file;
    }

    @Override
    public String fetchData() {
        // May perform additional operation or processing 
        // before or after data is fetched
        
        return file.readFile();
    }

    @Override
    public void sendData(String data) {
        // May perform additional operation or processing 
        // before or after data is written to file

        file.writeFile(data);
    }
}

Demo

// Demo.java

package com.bigboxcode.designpattern.adapter.api;

public class Demo {
    public static void main(String[] args) {
        // make a call to third part API for testing
        Api thirdPartyApi = new ThirdPartyApi();
        thirdPartyApi.fetchData();
        thirdPartyApi.sendData("1234");


        // Make a call to the file via FileAdapter
        File file = new FileOp();
        FileAdapter fileAdapter = new FileAdapter(file);
        fileAdapter.fetchData();
        fileAdapter.sendData("ABCDEF");
    }
}

Output

Fetching data from Third Party API
Sending data to Third Party API: 1234


Reading from file
Writing to file: ABCDEF

Example #2: Transport [Object Adpater]

Existing Situation

In the existing code, we already have classes named Plane and Helicopter. Both these classes implement an interface named AirTransport.

New requirement

Then sometime later we are trying to add a feature for a transport system. So we have created an interface named Transport and all the transport-related classes will implement the Transport interface.

Problem

The AirTransport interface and its implementations do not match the new Transport interface. And we don’t want to change the existing interface(AirTransport) and its implementations (Plane and Helicopter)

Solution

  • Create a new class named AirTransportAdapter.
  • Import Transport interface in AirTransportAdapter.
  • Implement all required functions, and make any required changes to achieve the final result.
  • While using the new adapter class, pass the object in the constructor.

Class Diagram

Take a look at the class diagram.

AirTransport Interface

// AirTransport.java

package com.bigboxcode.designpattern.adapter.transport;

public interface AirTransport {
    int getNumberOfWheels();

    int getNumberOfEngines();

    double getWeight();

    // In Nautical miles
    double getDistanceTravelled();

    double getTravelCostTotal();

}

Plane Class [implements AirTransport]

// Plane.java

package com.bigboxcode.designpattern.adapter.transport;

public class Plane  implements AirTransport {
    @Override
    public int getNumberOfWheels() {
        return 3;
    }

    @Override
    public int getNumberOfEngines() {
        return 2;
    }

    // get weight in pound
    @Override
    public double getWeight() {
        return 127_000;
    }

    // In Nautical miles
    @Override
    public double getDistanceTravelled() {
        return 500; // Nautical files
    }

    @Override
    public double getTravelCostTotal() {
        return 3_000;
    }
}

Helicopter Class [implements AirTransport]

// Helicopter.java

package com.bigboxcode.designpattern.adapter.transport;

public class Helicopter implements AirTransport {
    @Override
    public int getNumberOfWheels() {
        return 0;
    }

    @Override
    public int getNumberOfEngines() {
        return 1;
    }

    // Get weight in LB
    @Override
    public double getWeight() {
        return 12_000;
    }

    // In Nautical miles
    @Override
    public double getDistanceTravelled() {
        return 180; // nautical miles
    }

    @Override
    public double getTravelCostTotal() {
        return 20_000;
    }
}

Transport Interface

// Transport.java

package com.bigboxcode.designpattern.adapter.transport;

public interface Transport {
    int getNumberOfWheels();

    double getWeight();

    // In miles
    double getDistanceTravelled();

    double getTravelCostPerMile();
}

Bus Class [implements Transport]

// Bus.java

package com.bigboxcode.designpattern.adapter.transport;

public class Bus implements Transport {
    @Override
    public int getNumberOfWheels() {
        return 4;
    }

    @Override
    public double getWeight() {
        return 10_000;
    }

    @Override
    public double getDistanceTravelled() {
        return 1_000;
    }

    @Override
    public double getTravelCostPerMile() {
        return 5;
    }
}

Bike Class [implements Transport]

// Bike.java

package com.bigboxcode.designpattern.adapter.transport;

public class Bike implements Transport {
    @Override
    public int getNumberOfWheels() {
        return 2;
    }

    @Override
    public double getWeight() {
        return 700;
    }

    @Override
    public double getDistanceTravelled() {
        return 80;
    }

    @Override
    public double getTravelCostPerMile() {
        return 4;
    }
}

AirTransportAdapter Class [implements Transport]

// AirTransportAdapter.java

package com.bigboxcode.designpattern.adapter.transport;

public class AirTransportAdapter implements Transport {
    private final AirTransport airTransport;

    public AirTransportAdapter(AirTransport airTransport) {
        this.airTransport = airTransport;
    }

    @Override
    public int getNumberOfWheels() {
        return this.airTransport.getNumberOfWheels();
    }

    @Override
    public double getWeight() {
        return this.airTransport.getWeight();
    }

    @Override
    public double getDistanceTravelled() {
        // Convert nautical mile to mile and return
        double distanceInNauticalMile = this.airTransport.getDistanceTravelled();

        return distanceInNauticalMile * 1.151;
    }

    @Override
    public double getTravelCostPerMile() {
        // calculate cost per mile from total cost
        double totalCost = this.airTransport.getTravelCostTotal();

        return totalCost / this.getDistanceTravelled();
    }
}

Demo

// Demo.java

package com.bigboxcode.designpattern.adapter.transport;

public class Demo {
    public static void main(String[] args) {

        System.out.println("Get information of Bus travel...");

        Bus bus = new Bus();
        System.out.println("\nNumber of wheels: " + bus.getNumberOfWheels());
        System.out.println("Weight(kg): " + bus.getWeight());
        System.out.println("Distance(miles): " + bus.getDistanceTravelled());
        System.out.println("Cost per mile: " + bus.getTravelCostPerMile());

        System.out.println("\n\n-------------------------------------------\n\n");

        System.out.println("Get information of Plane travel...");

        AirTransportAdapter planeTransport = new AirTransportAdapter(new Plane());
        System.out.println("\nNumber of wheels: " + planeTransport.getNumberOfWheels());
        System.out.println("Weight(kg): " + planeTransport.getWeight());
        System.out.println("Distance(miles): " + planeTransport.getDistanceTravelled());
        System.out.println("Cost per mile: " + planeTransport.getTravelCostPerMile());
    }
}

Output

Get information of Bus travel...

Number of wheels: 4
Weight(kg): 10000.0
Distance(miles): 1000.0
Cost per mile: 5.0


-------------------------------------------


Get information of Plane travel...

Number of wheels: 3
Weight(kg): 127000.0
Distance(miles): 575.5
Cost per mile: 5.212858384013901

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Adapter pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.