25% developed

Adapter

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Abstract Factory Computer Science Design Patterns
Adapter
Bridge

The adapter pattern is used when a client class has to call an incompatible provider class. Let's imagine a MailingClient class that needs to call a method on the LegacyEmployee class:

MailingClient
+firstName: String
+lastName: String
 
IEmployee
+mailEmployee(firstName: String, lastName: String)
 
LegacyEmployee
+mailEmployee(fullName: String)

MailingClient already calls classes that implement the IEmployee interface but the LegacyEmployee doesn't implement it. We could add a new method to LegacyEmployee to implement the IEmployee interface but LegacyEmployee is legacy code and can't be changed. We could modify the MailingClient class to call LegacyEmployee but it needs to change every call. The formatting code would be duplicated everywhere. Moreover, MailingClient won't be able to call other provider class that implement the IEmployee interface any more.

So the solution is to code the formatting code in another independent class, an adapter, also called a wrapper class:

IEmployee
+mailEmployee(firstName: String, lastName: String)
MailingClient
+firstName: String
+lastName: String
EmployeeAdapter
+mailEmployee(firstName: String, lastName: String)
LegacyEmployee
+mailEmployee(fullName: String)

EmployeeAdapter implements the IEmployee interface. MailingClient calls EmployeeAdapter. EmployeeAdapter formats the data and calls LegacyEmployee. This type of adapter is called an object adapter. The other type of adapter is the class adapter.


Clipboard

To do:
Describe the class adapter.


Examples

The WebGL-2D is a JavaScript library that implements the adapter pattern. This library is used for the HTML5 canvas element. The canvas element has two interfaces: 2d and WebGL. The first one is very simple to use and the second is much more complex but optimized and faster. The WebGL-2D 'adapts' the WebGL interface to the 2d interface, so that the client calls the 2d interface only.

Cost

Think twice before implementing this pattern. This pattern should not be planned at design time. If you plan to use it for a project from scratch, this means that you don't understand this pattern. It should be used only with legacy code. It is the least bad solution.

Creation

Its implementation is easy but can be expensive. You should not have to refactor the code as the client and the provider should not be able to work together yet.

Maintenance

This is the worst part. Most of the code has redundancies (but less than without the pattern). The modern interface should always provide as much information as the legacy interface needs to work. If one information is missing on the modern interface, it can call the pattern into question.

Removal

This pattern can be easily removed as automatic refactoring operations can easily remove its existence.

Advices

  • Put the adapter term in the name of the adapter class to indicate the use of the pattern to the other developers.

Implementation

Object Adapter

Implementation in Java

Our company has been created by a merger. One list of employees is available in a database you can access via the CompanyAEmployees class:

/**
 * Employees of the Company A.
 */
public class CompanyAEmployees {
  /**
   * Retrieve the employee information from the database.
   *
   * @param sqlQuery The SQL query.
   * @return The employee object.
   */
  public Employee getEmployee(String sqlQuery) {
      Employee employee = null;
     
      // Execute the request.
     
      return employee;
    }
}

One list of employees is available in a LDAP you can access via the CompanyBEmployees class:

/**
 * Employees of the Company B.
 */
public class CompanyBEmployees {
  /**
   * Retrieve the employee information from the LDAP.
   *
   * @param sqlQuery The SQL query.
   * @return The employee object.
   */
  public Employee getEmployee(String distinguishedName) {
      Employee employee = null;
     
      // Call the LDAP.
     
      return employee;
    }
}

To access both to the former employees of the company A and the former employees of the company B, we define an interface that will be used by two adapters, EmployeeBrowser:

/**
 * Retrieve information about the employees.
 */
interface EmployeeBrowser {
  /**
   * Retrieve the employee information.
   *
   * @param direction The employee direction.
   * @param division The employee division.
   * @param department The employee departement.
   * @param service The employee service.
   * @param firstName The employee firstName.
   * @param lastName The employee lastName.
   *
   * @return The employee object.
   */
  Employee getEmployee(String direction, String division, String department, String service, String firstName, String lastName);
}

We create an adapter for the code of the former company A, CompanyAAdapter:

/**
 * Adapter for the company A legacy code.
 */
public class CompanyAAdapter implements EmployeeBrowser {
  /**
   * Retrieve the employee information.
   *
   * @param direction The employee direction.
   * @param division The employee division.
   * @param department The employee department.
   * @param service The employee service.
   * @param firstName The employee firstName.
   * @param lastName The employee lastName.
   *
   * @return The employee object.
   */
  public Employee getEmployee(String direction, String division, String department, String service, String firstName, String lastName) {
    String distinguishedName = "SELECT *"
      + " FROM t_employee as employee"
      + " WHERE employee.company= 'COMPANY A'"
      + " AND employee.direction = " + direction
      + " AND employee.division = " + division
      + " AND employee.department = " + department
      + " AND employee.service = " + service
      + " AND employee.firstName = " + firstName
      + " AND employee.lastName = " + lastName;
   
    CompanyAEmployees companyAEmployees = new CompanyAEmployees();
    return companyAEmployees.getEmployee(distinguishedName);
  }
}

We create an adapter for the code of the former company B, CompanyBAdapter:

/**
 * Adapter for the company B legacy code.
 */
public class CompanyBAdapter implements EmployeeBrowser {
  /**
   * Retrieve the employee information.
   *
   * @param direction The employee direction.
   * @param division The employee division.
   * @param department The employee department.
   * @param service The employee service.
   * @param firstName The employee firstName.
   * @param lastName The employee lastName.
   *
   * @return The employee object.
   */
  public Employee getEmployee(String direction, String division, String department, String service, String firstName, String lastName) {
    String distinguishedName = "ov1 = " + direction
      + ", ov2 = " + division
      + ", ov3 = " + department
      + ", ov4 = " + service
      + ", cn = " + firstName + lastName;
   
    CompanyBEmployees companyBEmployees = new CompanyBEmployees();
    return companyBEmployees.getEmployee(distinguishedName);
  }
}
Implementation in Ruby

Ruby

class Adaptee
  def specific_request
    # do something
  end
end

class Adapter
  def initialize(adaptee)
    @adaptee = adaptee
  end

  def request
    @adaptee.specific_request
  end
end

client = Adapter.new(Adaptee.new)
client.request
Implementation in Python
class Adaptee:
    def specific_request(self):
        return 'Adaptee'
 
class Adapter:
    def __init__(self, adaptee):
        self.adaptee = adaptee
 
    def request(self):
        return self.adaptee.specific_request()
 
client = Adapter(Adaptee())
print client.request()
Implementation in Scala
trait Socket220V {
  def plug220()
}

trait Socket19V {
  def plug19()
}

class Laptop extends Socket19V {
  def plug19() {
    println("Charging....")
  }
}

class LaptopAdapter(laptop: Laptop) extends Socket220V {
  def plug220() {
    println("Transform1...")
    laptop.plug19()
  }
}

object Test {
  def main(args: Array[String]) {
    //you can do it like this:
    new LaptopAdapter(new Laptop).plug220()

    //or like this (doesn't need LaptopAdapter)
    new Laptop with Socket220V {
      def plug220() {
        println("Transform2...")
        this.plug19()
      }
    } plug220()
  }
}
Implementation in Delphi
program Adapter;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  (* Its interface *)
  TTarget = class abstract
    function Request: string; virtual; abstract;
  end;

  (* A client accessed to this. *)
  TAdaptee = class(TTarget)
    function Request: string; override;
  end;

  (* Object Adapter uses composition and can wrap classes or interfaces, or both.*)
  (* Redirect call to Adaptee. It is loose coupling of client and adapter.*)
  (*
    *It can do this since it contains, as a private, encapsulated member,
    *the class or interface object instance it wraps.
  *)
  TObjectAdapter = class
    fAdaptee: TAdaptee;
    function SpecialRequest: string;
    constructor Create(adaptee: TAdaptee);
  end;

  { TObjectAdapter }

constructor TObjectAdapter.Create;
begin
  fAdaptee := TAdaptee.Create;
end;

function TObjectAdapter.SpecialRequest: string;
begin
  Result := fAdaptee.Request;
end;

{ TAdaptee }

function TAdaptee.Request: string;
begin
  Result := 'Adaptee';
end;

var
  clientObject: TObjectAdapter;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    clientObject := TObjectAdapter.Create(TAdaptee.Create);

    WriteLn('Call method Object Adapter: '+clientObject.SpecialRequest);

    WriteLn(#13#10+ 'Press any key to continue...');
    ReadLn;

    clientObject.Free;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;

end.


Class Adapter

Implementation in Python
class Adaptee1:
    def __init__(self, *args, **kw):
        pass
   
    def specific_request(self):
        return 'Adaptee1'

class Adaptee2:
    def __init__(self, *args, **kw):
        pass
   
    def specific_request(self):
        return 'Adaptee2'
 
class Adapter(Adaptee1, Adaptee2):
    def __init__(self, *args, **kw):
        Adaptee1.__init__(self, *args, **kw)
        Adaptee2.__init__(self, *args, **kw)
 
    def request(self):
        return Adaptee1.specific_request(self), Adaptee2.specific_request(self)
 
client = Adapter()
print client.request()
Implementation in Delphi
program Adapter;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  (* Its interface *)
  TTarget = class abstract
    function Request: string; virtual; abstract;
  end;

  (* A client accessed to this. *)
  TAdaptee = class(TTarget)
    function Request: string; override;
  end;

  (* Class Adapter uses inheritance and can only wrap a class.*)
  (* This plain old inheritance. *)
  (* It cannot wrap an interface since by definition*)
  (* it must derive from some base class as Adaptee in example*)
  (*
    * Can't reuse Class Adapter without rewrite code
    * You need implements other adapter with other method in other class.
  *)
   TClassAdapter = class(TAdaptee)
   function SpecialRequest: string;
   end;

{ TClassAdapter }

function TClassAdapter.SpecialRequest: string;
begin
  //use inherited Request as SpecialRequest
  Result:= inherited Request;
end;

{ TAdaptee }

function TAdaptee.Request: string;
begin
  Result := 'Adaptee';
end;

var
  clientClass:TClassAdapter;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    clientClass:= TClassAdapter.Create;

    WriteLn('Call method Class Adapter: '+clientClass.SpecialRequest);

    WriteLn(#13#10+ 'Press any key to continue...');
    ReadLn;

    clientClass.Free;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;

end.


Clipboard

To do:
Add more illustrations.


Abstract Factory Computer Science Design Patterns
Adapter
Bridge


You have questions about this page?
Ask it here:


Create a new page on this book: