1. Computing

The Adapter Pattern - Delphi OOP Part 10 - Chapter 22

By

The Adapter Pattern - Delphi OOP Part 10 - Chapter 22
Materials written by John Barrow. Modifications by Zarko Gajic

Back to Chapter 21

Delegation: Association and Composition

The previous two parts (parts 8 and 9) have introduced us to the concepts of association, composition and delegation. Briefly stated, we write a new class that refers to or incorporates an instance of another class, in the sense of carrying a link to it, and then calls its methods to use some or all of that class’s functionality without having to re-implement it.

By allowing one to incorporate the facilities of an existing class into a new class, delegation has some similarity to inheritance. However, unlike inheritance, each required facility must be exposed explicitly. While this requires more work on the part of the rogrammer, it also allows facilities of the existing class to be kept hidden, something not possible through inheritance.

If a single class delegates operations to more than one existing class, an effect similar to multiple inheritance is possible while avoiding some of the complications.

Delegation is an important OO principle and is used in a variety of patterns such as the Adapter, the Facade and the Strategy patterns. In this part we look at the Adapter and Facade patterns.

Example 10.1 The Adapter Pattern

A composed object is the owner of constituent objects that it can use while hiding them from others. This opens up the possibility of adapting legacy software or existing classes for reuse.

When looking for reuse of existing software in a new application we may find a class or a non-OO program that performs the operations we need but that does not meet some other requirement or that may be subject to future change. Adapting accommodates these factors by encapsulating this existing software as an object and limiting its coupling with the rest of the program.

This example presents the Adapter Pattern as a way of reusing legacy software within an OO programming approach. The next example shows how an object in one hierarchy may be adapted to another inheritance hierarchy when this is required for polymorphism.

Ex 10.1 step 1 A legacy class

Assume that we are programming to a standard that requires separate access methods for modifying and for reading object data, and that these methods must work with a consistent data type. We have an existing TCounter class that does not meet these standards.

First, its access methods (the methods AddAndRead and ClearAndRead below) both modify the value of the field data and return a value, which is not good programming practice.

Second, its access methods accept an integer and return a string. According to the standard these should be of the same type. We want to reuse this class but we don’t want to ignore our standards, so we can create an adapter with the required interface. The existing class is:

 unit CounterU;
 
 // Legacy class
 
 interface
 
 type
    TCounter = class (TObject)
    protected
      Total : Integer;
    public
      function AddAndRead (Number : Integer): string;
      function ClearAndRead: string;
    end;
 
 implementation
 
 uses
    SysUtils;
 
 function TCounter.AddAndRead (Number : Integer): string;
 begin
    Total := Total + Number;
    Result := IntToStr (Total) ;
 end;
 
 function TCounter.ClearAndRead: string;
 begin
    Total := 0;
    Result := IntToStr (Total) ;
 end;
 
 end. 

Ex 10.1 step 2 The Adapter class

We create a composed class that has the method signatures required by the standards (the methods Add, Clear and GetValue) and that adapts these to calls to the legacy class:
 unit NewCounterU;
 
 // 'Simple' adapter, no local processing or data
 
 interface
 
 uses
    CounterU; // contains adapted class
 
 type
    TNewCounter = class(TObject)
    private
      FOldCounter: TCounter; // the adaptee, kept private
    public
      // composition: must propagate create and destroy
      constructor Create;
      destructor Destroy; override;
      // adapter methods providing the required interface
      procedure Add (ANumber: integer) ;
      procedure Clear;
      function GetValue: integer;
    end;
 
 implementation
 
 uses SysUtils;
 
 { TNewCounter }
 
 procedure TNewCounter.Add(ANumber: integer) ;
 begin
    FOldCounter.AddAndRead (ANumber) ;
 end;
 
 procedure TNewCounter.Clear;
 begin
    FOldCounter.ClearAndRead;
 end;
 
 constructor TNewCounter.Create;
 begin
    inherited;
    FOldCounter := TCounter.Create;
 end;
 
 destructor TNewCounter.Destroy;
 begin
    FOldCounter.Free;
    inherited;
 end;
 
 function TNewCounter.GetValue: integer;
 begin
    Result := StrToInt (FOldCounter.AddAndRead(0)) ;
 end;
 
 end. 

©2014 About.com. All rights reserved.