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 classs 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 dont 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.


