Adapter Design Pattern Demystified

Why Adapter Pattern

Recently I bought a laptop and found it difficult to work without a mouse. So I borrowed a mouse from my friend and to my surprise I couldn't plug it into my laptop. It was a PS/2 mouse, but my laptop supports only USB mouse. Now what do you think, a PS/2 to USB adapter to the rescue?

Mouse Laptop USB
Figure 1:
Mouse Laptop USB

Often times when we do development we encounter a situation where we have an old legacy interface that needs to fit into a new interface. In this case you will code a separate class that will make both the interfaces compatible with each other. This wrapper class provides seamless communication with both the old and new system. What you are implementing is in fact an adapter design pattern that in turn is a structural design pattern from the Gang of Four (GoF) software design patterns book.

Adapter Pattern
Figure 2:
Adapter Pattern

Implementation

Implement
Figure 3: Implement

In our case let's say we have a client class that contains the method Operate Laptop that takes an IUSB input as the parameter (refer to the preceding diagram). The IUSB interface is the new interface that expects objects of classes that implement IUSB. But we have only an object of the class that implements an IPS2. The PS2Mouse class is hence incompatible with the Operate Laptop method, since both exposes two different behaviors. This is like passing a square peg into a round hole. Completely incompatible, right?

Completely incompatible right
Figure 4: Square Peg into a round hole

implementing adapter
Figure 5: Connect Pass PS2 Mouse Instance

The first concept to understand when implementing an adapter pattern is to identify the source interface and target interface. In our case the source interface is IPS2 and the target interface is IUSB, both of them expose the OpenPS2 () and OpenUSB() methods respectively.

  1. interface IUSB  
  2. {  
  3.    void OpenUSB();  
  4. }  
  5. interface IPS2  
  6. {  
  7.   
  8.    void OpenPS2();  
  9. }  
Now we need to determine the source interface implementation class that is the PS2Mouse.
  1. class PS2Mouse : IPS2  
  2. {  
  3.    public void OpenPS2()  
  4.    {  
  5.       Console.WriteLine("Opening..PS2 Mouse...");  
  6.    }  
  7. }  
We need an adapter class that makes both of the non-compatible interfaces compatible. An adapter class implements the new interface and holds a composition relation to the old interface. When you create an adapter object, you need to inject an instance of the IPS2 type, in other words a class that implements an IPS2 interface. This can be an object of PS2Mouse or PS2Keyboard.

PS2Keyboard
Figure 6: Implemented IPS2 interface
  1. class PS2Adapter : IUSB  
  2. {  
  3.   
  4.     private IPS2 _input;  
  5.   
  6.     public PS2Adapter(IPS2 input)  
  7.     {  
  8.         _input = input;  
  9.     }  
  10.   
  11.   
  12.   
  13.     public void OpenUSB()  
  14.     {  
  15.         //before opeingin USB  
  16.   
  17.         _input.OpenPS2();  
  18.         Console.WriteLine("Opening USB device");  
  19.   
  20.     }  
  21. }  
Testing the compatibility
  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.   
  6.         PS2Mouse mouse = new PS2Mouse();  
  7.   
  8.        // OperateLaptop(mouse); // noncompatible  
  9.   
  10.         PS2Adapter converter = new PS2Adapter(mouse);    
  11.   
  12.         OperateUSBLaptop(converter); //compatible now             
  13.   
  14.     }  
  15.   
  16.     static void OperateUSBLaptop(IUSB device)  
  17.     {  
  18.         device.OpenUSB();  
  19.   
  20.          
  21.     }  
  22. }  

OpenUSB
Figure 7: PS2Adapter

Since both interfaces are compatible we will get the following output:

output
Figure 8: Output