🤖📘🍻 Hooray! After 3 years of work, we've finally released a new ebook on design patterns! Check it out »

Adapter Design Pattern in Delphi

Applications in Delphi

A typical example of this is the wrapper Delphi generates when you import a VBX or OCX. Delphi generates a new class which translates the interface of the external control into a Pascal compatible interface. Another typical case is when you want to build a single interface to old and new systems.

Note Delphi does not allow class adaption through multiple inheritance in the way described in Design Patterns. Instead, the adapter needs to refer to a specific instance of the old class.

Implementation Example

The following example is a simple (read only) case of a new customer class, an adapter class and an old customer class. The adapter illustrates handling the year 2000 problem, translating an old customer record containing two digit years into a new date format. The client using this wrapper only knows about the new customer class.

Translation between classes is handled by the use of virtual access methods for the properties. The old customer class and adapter class are hidden in the implementation of the unit.

unit Adapter;

interface

uses SysUtils, Classes;

type

  //The new class

  TNewCustomer = class
  private
    FCustomerID: Longint;
    FFirstName: string;
    FLastName: string;
    FDOB: TDateTime;
  protected
    function GetCustomerID: Longint; virtual;
    function GetFirstName: string; virtual;
    function GetLastName: string; virtual;
    function GetDOB: TDateTime; virtual;
  public
    constructor Create(CustID: Longint); virtual;
    property CustomerID: Longint read GetCustomerID;
    property FirstName: string read GetFirstName;
    property LastName: string read GetLastName;
    property DOB: TDateTime read GetDOB;
  end;

  //An interface method

  //Lets us hide details of TOldCustomer from the client

function GetCustomer(CustomerID: Longint): TNewCustomer;

implementation

const
  Last_OldCustomer_At_Year_2000 = 15722;
  Last_OldCustomer_In_Database = 30000;

//The new class

constructor TNewCustomer.Create(CustID: Longint);
begin
  FCustomerID := CustID;
  FFirstName := 'A';
  FLastName := 'New_Customer';
  FDOB := Now;
end;

function TNewCustomer.GetCustomerID: Longint;
begin
  Result := FCustomerID;
end;
 
function TNewCustomer.GetFirstName: string;
begin
  Result := FFirstName;
end;

function TNewCustomer.GetLastName: string;
begin
  Result := FLastName;
end;

function TNewCustomer.GetDOB: TDateTime;
begin
  Result := FDOB;
end;

type

  //The old class
 
  TOldDOB = record
    Day: 0..31;
    Month: 1..12;
    Year: 0..99;
  end;


  TOldCustomer = class
    FCustomerID: Integer;
    FName: string;
    FDOB: TOldDOB;
  public
    constructor Create(CustID: Integer);
    property CustomerID: Integer read FCustomerID;
    property Name: string read FName;
    property DOB: TOldDOB read FDOB;
  end;
 
constructor TOldCustomer.Create(CustID: Integer);
begin
  FCustomerID := CustomerID;
  FName := 'An Old_Customer';
  with FDOB do begin
    Day := 1;
    Month := 1;
    Year := 1;
  end;
end;
 
type

  //The Adapter class

  TAdaptedCustomer = class(TNewCustomer)
  private
    FOldCustomer: TOldCustomer;
  protected
    function GetCustomerID: Longint; override;
    function GetFirstName: string; override;
    function GetLastName: string; override;
    function GetDOB: TDateTime; override;
  public
    constructor Create(CustID: Longint); override;
    destructor Destroy; override;
  end;


//The Adapter class

constructor TAdaptedCustomer.Create(CustID: Longint);
begin
  inherited Create(CustID);
  FOldCustomer := TOldCustomer.Create(CustID);
end;

destructor TAdaptedCustomer.Destroy;
begin
  FOldCustomer.Free;
  inherited Destroy;
end;

function TAdaptedCustomer.GetCustomerID: Longint;
begin
  Result := FOldCustomer.CustomerID;
end;

function TAdaptedCustomer.GetFirstName: string;
var
  SpacePos: integer;
begin
  SpacePos := Pos(' ', FOldCustomer.Name);
  if SpacePos = 0 then
    Result := ''
  else
    Result := Copy(FOldCustomer.Name,1,SpacePos-1);
end;

function TAdaptedCustomer.GetLastName: string;
var
  SpacePos: integer;
begin
  SpacePos := Pos(' ', FOldCustomer.Name);
  if SpacePos = 0 then
    Result := FOldCustomer.Name
  else
    Result := Copy(FOldCustomer.Name,SpacePos+1,255);
end;

function TAdaptedCustomer.GetDOB: TDateTime;
var
  FullYear: Word;
begin
  if CustomerID > Last_OldCustomer_At_Year_2000 then
    FullYear := 2000 + FOldCustomer.DOB.Year
  else
    FullYear := 1900 + FOldCustomer.DOB.Year;
  Result := EncodeDate(FullYear, FOldCustomer.DOB.Month, FOldCustomer.DOB.Day);
end;

function GetCustomer(CustomerID: Longint): TNewCustomer;
begin
  if CustomerID > Last_OldCustomer_In_Database then
    Result := TNewCustomer.Create(CustomerID)
  else
    Result := TAdaptedCustomer.Create(CustomerID) as TNewCustomer;
end;

end.

Code examples

More info, diagrams and examples of the Adapter design pattern you can find on our new partner resource Refactoring.Guru.