We now have two procedure methods for manipulating the data and a function for getting the value, and so comply with Delphi programming convention as well as the more general convention that separate methods be used for reading and writing object data. Add and GetValue also both involve an integer. (GetValue uses StrToInt for the conversion.)
The Adapter Pattern relies on composition, and so this class uses an explicit constructor and explicit destructor to propagate object creation and destruction.
Ex 10.1 step 3 Testing the adapter
We use a simple driver program to test the adapter.unit AdapterDemoU;This program works well. It uses TNewCounter, the adapter class, and does not know that these method calls are delegated to an instance of TCounter. Thus encapsulation is maintained and all the coupling is between neighbouring classes. As suggested by the Law of Demeter, TfrmItems knows only about TNewCounter, which in turn knows only about TCounter.
// simple adapter
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Spin;
type
TfrmItems = class(TForm)
sedNewItems: TSpinEdit;
btnAdd: TButton;
btnClear: TButton;
lblTotalItems: TLabel;
procedure btnAddClick(Sender: TObject) ;
procedure btnClearClick(Sender: TObject) ;
end;
var
frmItems: TfrmItems;
implementation
uses NewCounterU;
var
Items: TNewCounter; // the adapter
{$R *.DFM}
procedure TfrmItems.btnAddClick(Sender: TObject) ;
begin
Items.Add (sedNewItems.Value) ;
lblTotalItems.Caption := 'Value is ' + IntToStr (Items.GetValue) ;
end;
procedure TfrmItems.btnClearClick(Sender: TObject) ;
begin
Items.Clear;
lblTotalItems.Caption := 'Value is ' + IntToStr (Items.GetValue) ;
end;
initialization
Items := TNewCounter.Create;
finalization
Items.Free;
end.
Ex 10.1 step 4 A complex Adapter
The class given in step 2 is a simple adapter. It stores no local data and performs no local manipulations. This is the goal of the Adapter Pattern, but it can lead to clumsiness and/or inefficiency. For example, the Get... method above requires a clumsy implementation. Assuming that we cant modify the legacy class (the adaptee, TCounter) we may decide to create a local variable in the adapter class to store the value locally.There are also situations where the adapted class does not provide all of the required functionality, and then we need to implement these in the adapter. For example, in keeping with the previous chapter, this adapter should have an Assign method. So this new version of the adapter is derived from TPersistent. (For brevity, we wont implement the AssignTo here.)
unit NewCounterU;In the next step well change the user interface to test the Assign method.
// 'Complex' adapter
interface
uses Classes, CounterU;
type
TNewCounter = class(TPersistent)
private
FOldCounter: TCounter;
FValue: string;
public
procedure Assign (ANewCounter: TPersistent) ; override;
constructor Create;
destructor Destroy; override;
// adapter methods
procedure Add (ANumber: integer) ;
procedure Clear;
function GetValue: integer;
end;
implementation
uses SysUtils;
{ TNewCounter }
procedure TNewCounter.Add(ANumber: integer) ;
begin
FValue := FOldCounter.AddAndRead (ANumber) ;
end;
procedure TNewCounter.Assign (ANewCounter: TPersistent) ;
begin
if ANewCounter is TNewCounter then
FValue := IntToStr(TNewCounter(ANewCounter).GetValue)
else
inherited Assign (ANewCounter) ;
end;
procedure TNewCounter.Clear;
begin
FValue := 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(FValue) ;
end;
end.


