Tuesday, February 8, 2011

Structural Patterns - Adapter Pattern

Definition

Convert the existing interfaces to a new interface to achieve compatibility and reusability of the unrelated classes in one application. Also known as Wrapper pattern. 

  • Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
  • Wrap an existing class with a new interface.
  • Impedance match an old component to a new system

Problem

An “off the shelf” component offers compelling functionality that you would like to reuse, but its “view of the world” is not compatible with the philosophy and architecture of the system currently being developed.

Where to use & benefits



The Adapter pattern is used so that two unrelated interfaces can work together. The joining between them is called an Adapter. This is something like we convert interface of one class into interface expected by the client. We do that using an Adapter.

Let’s try and understand this with the help of an example. Again, I will like to take a general example. We all have electric sockets in our houses of different sizes and shapes. I will take an example of a socket of 15 Ampere. This is a bigger socket and the other one which is smaller is of 5 Ampere. A 15 Amp plug cannot fit into a 5 Amp socket. Here, we will use an Adapter. The adapter can be called a connector here. The connector connects both of these and gives output to the client plug which is of 5 Amp.

The Adapter is something like this. It will be having the plug of suitable for 15 Amp and a socket suitable for a 5 Amp plug. So, that the 5 Amp plug which here is the client can fit in and also the server which here is the 15 Amp socket can give the output.

Let’s try and convert the same example into a software program. How do we do this? Let’s try and understand the problem once more. We have a 5 Amp plug and want a 5 Amp socket so that it can work. We DO NOT have a 5 Amp socket, what we have is a 15 Amp socket in which the 5 Amp plug cannot fit. The problem is how to cater to the client without changing the plug or socket.

The Adapter Pattern can be implemented in two ways, by Inheritance and by Composition.

Here is the example of Adapter by Inheritance:

Let’s say there is a socket interface.

Socket.java

package structural.adapter.inheritance;
/**
* The socket class has a specs for 15 AMP.
*/
public interface Socket {
/**
* This method is used to match the input to be
* given to the Plug
*
* @return Output of the Plug (Client)
*/
public String getOutput();
}// End of interface
And there is a class Plug which wants the input of 5 AMP. This is the client.

Plug.java

package structural.adapter.inheritance;
/**
* The input for the plug is 5 AMP. which is a
* mismatch for a 15 AMP socket.
*
* The Plug is the client. We need to cater to
* the requirements of the Plug.
*/
public class Plug {
private String specification = "5 AMP"; public String getInput() {
return specification;
}
}// End of class

Finally, there will be an adapter class. This will inherit the socket and give output for Plug.

ConnectorAdapter.java

package structural.adapter.inheritance;
/**
* ConnectorAdapter has is the connector between
* the socket and plug so as to make the interface
* of one system to suit the client.
*/
public class ConnectorAdapter implements Socket {
/**
* Method coming from the interface
* Socket which we have to make to
* fit the client plug
*
* @return Desired output of 5 AMP
*/
public String getOutput() {
Plug plug = new Plug();
String output = plug.getInput();
return output;
}
}// End of class

This class implements the getOutput() method of Socket and sets it to fit the client output.

Similarly, let’s consider the Association and Composition of objects by which Adapter can be implemented.
The class Socket gives the 15 AMP output.
Socket.java
package structural.adapter.composition;
/**
* Class socket giving the 15 AMP output.
*/
public class Socket {
/**
* Output of 15AMP returned.
*
* @return Value of output from socket
*/
public String getOutput() {
return "15 AMP";
}
}// End of class
There is an interface Plug.java which has a method getInput(). This is the client and we need to adapt the output for this input which is 5 AMP.
Plug.java
package structural.adapter.composition;
/**
* The input for the plug is 5 AMP. which is a
* mismatch for a 15 AMP socket.
*
* The Plug is the client. We need to cater to
* the requirements of the Plug.
*/
public interface Plug {
public String getInput();
}// End of class
Plug5AMP is the implementation of Plug which requires 5 AMP of input.
Plug5AMP.java
package structural.adapter.composition;

public class Plug5AMP implements Plug {
/**
* Get the input of client i.e. Plug
*
* @return 5 AMP
*/
public String getInput() {
return "5 AMP";
}
}// End of class
The Adapter here takes output from the Socket. If the output is what is needed, it gives it to the Plug else, it overrides the value and returns the adapter output.

ConnectorAdapter.java

package structural.adapter.composition;
/**
* Using composition
*/
public class ConnectorAdapter {
Plug5AMP plug5; public ConnectorAdapter(Plug5AMP plug) {
this.plug5 = plug;
}

public static void main(String[] args) {
// Taking output from the Socket
Socket socket = new Socket();
String outputFromSocket = socket.getOutput();

// Giving away input to the Plug
ConnectorAdapter adapter = new ConnectorAdapter(new Plug5AMP());
String inputToPlug = adapter.getAdapterOutput(outputFromSocket);
System.out.println("New output by adapter is: "+inputToPlug);
}

public String getAdapterOutput(String outputFromScoket) {
/*
* if output is same, return
*/
if (outputFromScoket.equals(plug5.getInput())) {
return outputFromScoket;
}
/*
* Else, override the value by adapterOutput
*/
else {
String adapterOutput = plug5.getInput();
return adapterOutput;
}
}// End of class
This is how the Adapter pattern works. When one interface cannot be changed and has to be suited to the again cannot-be-changed client, an adapter is used so that both the interfaces can work together. 

Example

The famous adapter classes in Java API are WindowAdapter,ComponentAdapter, ContainerAdapter, FocusAdapter, KeyAdapter, MouseAdapter and MouseMotionAdapter.
As you know, WindowListner interface has seven methods. Whenever your class implements such interface, you have to implements all of the seven methods. WindowAdapter class implements WindowListener interface and make seven empty implementation. When you class subclass WindowAdapter class, you may choose the method you want without restrictions. The following give such an example.
public interface Windowlistener {
    public void windowClosed(WindowEvent e);
    public void windowOpened(WindowEvent e);
    public void windowIconified(WindowEvent e);
    public void windowDeiconified(WindowEvent e);
    public void windowActivated(WindowEvent e);
    public void windowDeactivated(WindowEvent e);
    public void windowClosing(WindowEvent e);
}
public class WindowAdapter implements WindowListner{
    public void windowClosed(WindowEvent e){}
    public void windowOpened(WindowEvent e){}
    public void windowIconified(WindowEvent e){}
    public void windowDeiconified(WindowEvent e){}
    public void windowActivated(WindowEvent e){}
    public void windowDeactivated(WindowEvent e){}
    public void windowClosing(WindowEvent e){}
}
Here is a test program
import javax.swing.*;
import java.awt.event.*;
class Test extends JFrame {
   
    public Test () {
        setSize(200,200);
        setVisible(true);
        addWindowListener(new Closer());
    }
    public static void main(String[] args) {
        new Test();
    }
    class Closer extends WindowAdapter {
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    }
}
To reuse classes and make new class compatible with existing ones. For example, A clean system is already designed, you want to add more job in, the Extra interface uses adapter pattern to plug in the existing system.
interface Clean {
    public void makeClean();
}
class Office implements Clean{
    public void makeClean() {
        System.out.println("Clean Office");
    }
}
class Workshop implements Clean{
    public void makeClean() {
        System.out.println("Clean Workshop");
    }
}

interface Extra extends Clean{
    public void takeCare();
}
class Facility implements Extra{
    public void makeClean() {
        System.out.println("Clean Facility");
    }
    public void takeCare() {
        System.out.println("Care has been taken");
    }
}
In order to reuse Workshop and Office classes, 
we create an adapter interface Extra and 
add new job takeCare in the system.

class Test {
   static void Jobs (Extra job) {
       if (job instanceof Clean) 
           ((Clean)job).makeClean();
       if (job instanceof Extra)
           ((Extra)job).takeCare();
   }
   public static void main(String[] args) {
       Extra e = new Facility();
       Jobs(e);
       Clean c1 = new Office();
       Clean c2 = new Workshop();
       c1.makeClean();
       c2.makeClean();
       e.makeClean();
   }
}
 C:\ Command Prompt

C:\> java Test
Clean Facility
Care has been taken
Clean Office
Clean Workshop
Clean Facility
By composition, we can achieve adapter pattern. It is also called wrapper. For example, a Data class has already been designed and well tested. You want to adapt such class to your system. You may declare it as a variable and wrapper or embed it into your class.
//well-tested class
class Data {
   public void add(Info){}
   public void delete(Info) {}
   public void modify(Info){}
   //...
}

//Use Data class in your own class
class AdaptData {
   Data data;
   public void add(Info i) {
       data.add(i);
       //more job
   }
   public void delete(Info i) {
      data.delete(i);
      //more job
   }
   public void modify(Info i) {
      data.modify(i);
      //more job
   }
   //more stuff here
   //...
}

No comments:

Post a Comment