Adapter Design Pattern

The Adapter Design Pattern also known as Wrapper Pattern is one of the Structural Design Patterns. Sometimes data comes from different input sources in different formats. These Incompatible Interfaces cannot be connected directly, hence an Adapter or a Connecter is required which performs the required transformation to obtain the desired results.

The Adapter Design Pattern mainly converts an existing interface into another as per the required formation and allows reusability.

It wraps the response type, hides the transformation complexity, and presents the result in the desired format.

it separates the business logic of the program from the interface or data conversion hence promoting a Single Responsibility Principle.

Multiple New types of adapters can be introduced in the application without breaking existing code and following the Open-Closed Principle.

let's understand a few scenarios where Adapter Design Pattern is required.

A speedo meter is designed to show the current speed in the Metrics System ie kilometers per Hour used by most of the countries in the world, now the same speedo meter is required in countries that use the Imperial System (U.S., Liberia, and Myanmar) to display speed in Miles per Hour. so an adapter is required to transform the data from the Metrics System to the Imperial System.

A Memory Card comes in different standards and shapes (SD, SDHC, SDXC, SDUC, microSD, microSDHC, microSDXC, and microSDUC), and a single USB Port cannot cater to all the requirements. hence an adapter such as a Card Reader is required which enables the 2 different interfaces to work together.

Each country has its own Power PLug, Charging Port, and Socket Standards. an Asian Power Plug will not work in European Sockets, hence a Power Plug Adapter is required which has an Asian Style Socket and European Style Plug.

A function gets calls multiple APIs and gets the response in XML format, now 1 of the API teams have converted the response from XML to JSON format. hence an adapter or transformer is required to get the result.

A Shop selling Apples works as an Adapter between Customers and Vendors. The same Apple can be supplied by multiple Vendors but for Customers, it will remain the same.

Inheritance Adapter and Composition Adapter

The Adapter Pattern requires a default value for the Adapter, it can be implemented in 2 ways, Inheritance, and Composition but both produce the same result.

  1. Inheritance Adapter - The Inheritance Adapter takes the help of Java Inheritance and the implementation will extend the Adapter class. it had direct access to the default method.
  2. Composition Adapter - The Composition Adapter takes the help of Java Composition and the implementation will create an instance of the Adapter class. the default method will be called using this instance only.

Let's See the Adapter Design Pattern using Inheritance

package org.wesome.design.patterns;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class Apple {
    private String appleName;
    private AppleVendor appleVendor;
}
package org.wesome.design.patterns;

public enum AppleVendor {
    VENDOR_A, VENDOR_B, VENDOR_C, VENDOR_DEFAULT
}
package org.wesome.design.patterns;

/*  it's a shop, all purchases will be made here */
public interface AppleShop {
    Apple getFromVendorA(String appleName);

    Apple getFromVendorB(String appleName);

    Apple getFromVendorC(String appleName);

    Apple getFromVendorDefault(String appleName);
}
package org.wesome.design.patterns;

import static org.wesome.design.patterns.AppleVendor.VENDOR_DEFAULT;

public class DefaultVendor {
    public Apple getDefaultVendor(String appleName) {
        return new Apple(appleName, VENDOR_DEFAULT);
    }
}
package org.wesome.design.patterns;

import static org.wesome.design.patterns.AppleVendor.VENDOR_A;
import static org.wesome.design.patterns.AppleVendor.VENDOR_B;
import static org.wesome.design.patterns.AppleVendor.VENDOR_C;

/*  This is the apple adaptor, it has methods for each vendor to convert and process accordingly */
public class AppleShopDefaultVendorImpl extends DefaultVendor implements AppleShop {
    /*  Inheritance Adapter */
    @Override
    public Apple getFromVendorDefault(String appleName) {
        /*  the default value method is accessible via inheritance and can be called directly  */
        return getDefaultVendor(appleName);
    }

    @Override
    public Apple getFromVendorA(String appleName) {
        return findVendor(appleName, VENDOR_A);
    }

    @Override
    public Apple getFromVendorB(String appleName) {
        return findVendor(appleName, VENDOR_B);
    }

    @Override
    public Apple getFromVendorC(String appleName) {
        return findVendor(appleName, VENDOR_C);
    }

    /* perform all the vendor-specific transformations */
    private Apple findVendor(String appleName, AppleVendor appleVendor) {
        return new Apple(appleName, appleVendor);
    }
}
package org.wesome.design.patterns;


import static org.wesome.design.patterns.AppleVendor.VENDOR_A;
import static org.wesome.design.patterns.AppleVendor.VENDOR_B;
import static org.wesome.design.patterns.AppleVendor.VENDOR_C;
import static org.wesome.design.patterns.AppleVendor.VENDOR_DEFAULT;

public class Adapter {
    public static void main(String[] args) {
        String appleName = "Macintosh";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorA = getApple(appleShop, appleName, VENDOR_A);
        Apple vendorB = getApple(appleShop, appleName, VENDOR_B);
        Apple vendorC = getApple(appleShop, appleName, VENDOR_C);
        Apple defaultVendor = getApple(appleShop, appleName, VENDOR_DEFAULT);
        System.out.println(vendorC.getAppleName() + " apple from vendor " + vendorA.getAppleVendor());
        System.out.println(vendorC.getAppleName() + " apple from vendor " + vendorB.getAppleVendor());
        System.out.println(vendorC.getAppleName() + " apple from vendor " + vendorC.getAppleVendor());
        System.out.println(defaultVendor.getAppleName() + "apple from default Vendor " + defaultVendor.getAppleVendor());
    }

    public static Apple getApple(AppleShop appleShop, String appleName, AppleVendor appleVendor) {
        switch (appleVendor) {
            case VENDOR_A:
                return appleShop.getFromVendorA(appleName);
            case VENDOR_B:
                return appleShop.getFromVendorB(appleName);
            case VENDOR_C:
                return appleShop.getFromVendorC(appleName);
            default:
                return appleShop.getFromVendorDefault(appleName);
        }
    }
}
package org.wesome.design.patterns;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.wesome.design.patterns.Adapter.getApple;
import static org.wesome.design.patterns.AppleVendor.VENDOR_A;
import static org.wesome.design.patterns.AppleVendor.VENDOR_B;
import static org.wesome.design.patterns.AppleVendor.VENDOR_C;
import static org.wesome.design.patterns.AppleVendor.VENDOR_DEFAULT;

public class AdapterTest {
    @Test
    void testAdapterVendorA() {
        String appleName = "Macintosh";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorA = getApple(appleShop, appleName, VENDOR_A);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorA);
            Assertions.assertEquals(appleName, vendorA.getAppleName());
            Assertions.assertEquals(VENDOR_A, vendorA.getAppleVendor());
        });
    }

    @Test
    void testAdapterVendorB() {
        String appleName = "Fuji";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorB = getApple(appleShop, appleName, VENDOR_B);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorB);
            Assertions.assertEquals(appleName, vendorB.getAppleName());
            Assertions.assertEquals(VENDOR_B, vendorB.getAppleVendor());
        });
    }

    @Test
    void testAdapterVendorC() {
        String appleName = "Gala";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorC = getApple(appleShop, appleName, VENDOR_C);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorC);
            Assertions.assertEquals(appleName, vendorC.getAppleName());
            Assertions.assertEquals(VENDOR_C, vendorC.getAppleVendor());
        });
    }

    @Test
    void testAdapterVendorDefault() {
        String appleName = "Jonagold";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorDefault = getApple(appleShop, appleName, VENDOR_DEFAULT);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorDefault);
            Assertions.assertEquals(appleName, vendorDefault.getAppleName());
            Assertions.assertEquals(VENDOR_DEFAULT, vendorDefault.getAppleVendor());
        });
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

test {
    useJUnitPlatform()
}

Let's See the Adapter Design Pattern using Composition

package org.wesome.design.patterns;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class Apple {
    private String appleName;
    private AppleVendor appleVendor;
}
package org.wesome.design.patterns;

public enum AppleVendor {
    VENDOR_A, VENDOR_B, VENDOR_C, VENDOR_DEFAULT
}
package org.wesome.design.patterns;

/*  it's a shop, all purchases will be made here */
public interface AppleShop {
    Apple getFromVendorA(String appleName);

    Apple getFromVendorB(String appleName);

    Apple getFromVendorC(String appleName);

    Apple getFromVendorDefault(String appleName);
}
package org.wesome.design.patterns;

import static org.wesome.design.patterns.AppleVendor.VENDOR_DEFAULT;

public class DefaultVendor {
    public Apple getDefaultVendor(String appleName) {
        return new Apple(appleName, VENDOR_DEFAULT);
    }
}
package org.wesome.design.patterns;

import static org.wesome.design.patterns.AppleVendor.VENDOR_A;
import static org.wesome.design.patterns.AppleVendor.VENDOR_B;
import static org.wesome.design.patterns.AppleVendor.VENDOR_C;

/*  This is the apple adaptor, it has methods for each vendor to convert and process accordingly */
public class AppleShopDefaultVendorImpl implements AppleShop {
    /*  Composition Adapter */ 
    DefaultVendor defaultVendor = new DefaultVendor();

    @Override
    public Apple getFromVendorDefault(String appleName) {
        /*  the default value method will be called using instance  */
        return defaultVendor.getDefaultVendor(appleName);
    }

    @Override
    public Apple getFromVendorA(String appleName) {
        return findVendor(appleName, VENDOR_A);
    }

    @Override
    public Apple getFromVendorB(String appleName) {
        return findVendor(appleName, VENDOR_B);
    }

    @Override
    public Apple getFromVendorC(String appleName) {
        return findVendor(appleName, VENDOR_C);
    }

    /* perform all the vendor specific transformation */
    private Apple findVendor(String appleName, AppleVendor appleVendor) {
        return new Apple(appleName, appleVendor);
    }
}
package org.wesome.design.patterns;


import static org.wesome.design.patterns.AppleVendor.VENDOR_A;
import static org.wesome.design.patterns.AppleVendor.VENDOR_B;
import static org.wesome.design.patterns.AppleVendor.VENDOR_C;
import static org.wesome.design.patterns.AppleVendor.VENDOR_DEFAULT;

public class Adapter {
    public static void main(String[] args) {
        String appleName = "Macintosh";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorA = getApple(appleShop, appleName, VENDOR_A);
        Apple vendorB = getApple(appleShop, appleName, VENDOR_B);
        Apple vendorC = getApple(appleShop, appleName, VENDOR_C);
        Apple defaultVendor = getApple(appleShop, appleName, VENDOR_DEFAULT);
        System.out.println(vendorC.getAppleName() + " apple from vendor " + vendorA.getAppleVendor());
        System.out.println(vendorC.getAppleName() + " apple from vendor " + vendorB.getAppleVendor());
        System.out.println(vendorC.getAppleName() + " apple from vendor " + vendorC.getAppleVendor());
        System.out.println(defaultVendor.getAppleName() + "apple from default Vendor " + defaultVendor.getAppleVendor());
    }

    public static Apple getApple(AppleShop appleShop, String appleName, AppleVendor appleVendor) {
        switch (appleVendor) {
            case VENDOR_A:
                return appleShop.getFromVendorA(appleName);
            case VENDOR_B:
                return appleShop.getFromVendorB(appleName);
            case VENDOR_C:
                return appleShop.getFromVendorC(appleName);
            default:
                return appleShop.getFromVendorDefault(appleName);
        }
    }
}
package org.wesome.design.patterns;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.wesome.design.patterns.Adapter.getApple;
import static org.wesome.design.patterns.AppleVendor.VENDOR_A;
import static org.wesome.design.patterns.AppleVendor.VENDOR_B;
import static org.wesome.design.patterns.AppleVendor.VENDOR_C;
import static org.wesome.design.patterns.AppleVendor.VENDOR_DEFAULT;

public class AdapterTest {
    @Test
    void testAdapterVendorA() {
        String appleName = "Macintosh";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorA = getApple(appleShop, appleName, VENDOR_A);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorA);
            Assertions.assertEquals(appleName, vendorA.getAppleName());
            Assertions.assertEquals(VENDOR_A, vendorA.getAppleVendor());
        });
    }

    @Test
    void testAdapterVendorB() {
        String appleName = "Fuji";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorB = getApple(appleShop, appleName, VENDOR_B);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorB);
            Assertions.assertEquals(appleName, vendorB.getAppleName());
            Assertions.assertEquals(VENDOR_B, vendorB.getAppleVendor());
        });
    }

    @Test
    void testAdapterVendorC() {
        String appleName = "Gala";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorC = getApple(appleShop, appleName, VENDOR_C);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorC);
            Assertions.assertEquals(appleName, vendorC.getAppleName());
            Assertions.assertEquals(VENDOR_C, vendorC.getAppleVendor());
        });
    }

    @Test
    void testAdapterVendorDefault() {
        String appleName = "Jonagold";
        AppleShop appleShop = new AppleShopDefaultVendorImpl();
        Apple vendorDefault = getApple(appleShop, appleName, VENDOR_DEFAULT);
        Assertions.assertAll(() -> {
            Assertions.assertNotNull(vendorDefault);
            Assertions.assertEquals(appleName, vendorDefault.getAppleName());
            Assertions.assertEquals(VENDOR_DEFAULT, vendorDefault.getAppleVendor());
        });
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

test {
    useJUnitPlatform()
}

it's possible to create a two-way adapter that can convert the data in both directions.

follow us on