Introduction

The adapter pattern is part of a Structural Design pattern. It works on the concept of Bridging between two incompatible interfaces. This helps the project to support the legacy code with the new codes which are incompatible with the legacy code. In simple words, There are some chances that multiple Interfaces are incompatible with each other So Adapter makes them compatible without any code modification.

The adapter pattern is used for the integration of new codes into the existing project which is incompatible. The adapter pattern achieves this feature by converting the class’s Interface into some other identified Interface which can be read by the client. An Adapter is basically an object which will be responsible to make the two incompatible interfaces compatible with each other.

The Java library also uses the Adapter pattern in various cases. For example Arrays.asList(), Streams.

1. When to use the Adapter design pattern

  • When we have different incompatible interfaces with similar behavior
  • We need to transform the data into appropriate forms.
  • Modification is not allowed in the existing project.
  • Integration within the modules that are incompatible with the existing one.

2. Types of Adapter – Class and Object types

There are two ways in which we can use the Adapter Design pattern. Class Adapter and Object Adapter. Both approaches are for different requirements. Let us check below these:

2.1. Class Adapter

It uses an inheritance approach so this can only wrap the class and not the interfaces. Because of the inheritance, this can only be derived by the parent class. Let’s take a code example to understand this better:

class MyExistingServiceClass {
    public void old() {
        System.out.println("Inside Service method show()");        
    }
}

interface ClientInterface {
    void new();
}

class MyNewClassAdapter extends MyExistingServiceClass implements ClientInterface {
    void new() {
        old();
    }
}

2.2. Object Adapter

It uses composition and able to wrap both classes or interfaces. It can do this since it contains, as a private, encapsulated member, of the adaptee.

interface clientInterface {
  void write();
}
class Serviceclass {
  public void mark() {}
}
class ObjectAdapter implements clientInterface {
  Serviceclass sc = new Serviceclass();
  @Override
  public void write() {
    sc.mark();
  }
}

3. Use-case of Adapter Design Pattern

Let us try to understand this pattern with a real-world example. Suppose one of the medium level school wants to differentiate their students into different sections. The school is having a record of students within a software. And the software is having the list of the students into student array.

Now, the school decides to do the sectioning with the help of third-party software bypassing the string array to the software. Now let us talk about the statement of the problem, here the third party software only accepts the Arraylist as a parameter and not the String Array. If a String array will pass to the software then it is not gonna work.

To overcome this situation where both the software are not ready to modify the code but wants the job done. From the Adapter’s perspective, we can introduce a class that will understand the String Array and will convert that Array into the Arraylist so that the target software can take this as an Arraylist parameter.

Let’s check the code-implementation of the above use-case below.

3.1. Code implementation

Below class Student.java is acting like a POJO class having the getter and setters of the students. Third-party software is using this class to iterate to get the student details.

public class Student {

  private int rollid;
  private String studentname;

  public Student(int rollid, String studentname) {
    super();
    this.rollid = rollid;
    this.studentname = studentname;
  }

  public int getRollid() {
    return rollid;
  }

  public void setRollid(int rollid) {
    this.rollid = rollid;
  }

  public String getStudentname() {
    return studentname;
  }

  public void setStudentname(String studentname) {
    this.studentname = studentname;
  }

  @Override
  public String toString() {
    // TODO Auto-generated method stub
    return super.toString();
  }

}

Below class Thirdpartysystem.java is the third party software which accepts the student information as a list to differentiate them into different sections as per the conditions provided.

package javadesignpatterns.structural.adapter;

import java.util.ArrayList;

public class Thirdpartysystem {
  public void processSection(ArrayList<Student> studentlist) {
    for (Student student : studentlist) {

      System.out.print("\n" + studentlist.toString() + " : ");

      // Sectioning Logic Go Here.
    }
  }
}

The below Interface is implemented by our Adapter class which is having the Itarget method accepting the String array of student info.

package javadesignpatterns.structural.adapter;

public interface ITarget {
  public void processStudenttoSection(String[][] studentinfo);
}

The below class StudentAdapter is our adapter class which is working as a translator of the StringArray into the ArrayList. The method processStudenttoSection()is accepting the string array, iterating it, formulating the student object, and converting this into the Student List. Once the conversion is done then it will delegate the list to the third-party software.

package javadesignpatterns.structural.adapter;

import java.util.ArrayList;

public class StudentAdapter implements ITarget {
  Thirdpartysystem thirdPartyBillingSystem = new Thirdpartysystem();

  @Override
  public void processStudenttoSection(String[][] studentinfo) {
    String rollId = null;
    String studentname = null;
    ArrayList<Student> studentlist = new ArrayList<Student>();
    for (int i = 0; i < studentinfo.length; i++) {

      for (int j = 0; j < studentinfo[i].length; j++) {
        if (j == 0) {
          rollId = studentinfo[i][j];
        } else {
          studentname = studentinfo[i][j];
        }
        studentlist.add(new Student(Integer.parseInt(rollId), studentname));

      }

      System.out.println("Adapter class converted Array of students to ArrayList of Student : \n" + studentlist + "\n"
          + "then delegate to the Thirdpartysystem for processing the students into section");
      thirdPartyBillingSystem.processSection(studentlist);

    }

  }
}

Below StudentInfoStringarray.java is having the student’s information in String array and the main method is passing that array to the target adapter class.

package javadesignpatterns.structural.adapter;
public class StudentInfoStringarray {

  public static String[][] getStudents() {
    String[][] students = new String[4][];

    students[0] = new String[] { "100", "Ahmad" };
    students[1] = new String[] { "101", "Ana" };
    students[2] = new String[] { "102", "Barbara" };
    students[3] = new String[] { "103", "Chloe" };

    return students;
  }

  public static void main(String args[]) {

    ITarget target = new StudentAdapter();
    System.out.println("School system passes students string array to the Adapter class\n");
    target.processStudenttoSection(getStudents());
  }
}

Conclusion:

You learned about the Adapter Design Pattern in Java and how it solves the common API incompatibility issues. I would love to hear your feedbacks in the comments below.

By |Last Updated: May 9th, 2020|Categories: Java™|