The Adapter Pattern - Delphi OOP Part 10 - Chapter 22
< Continued from page 1
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;// simple adapterinterfaceuses 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;implementationuses 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.
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.
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 can?t 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 won?t implement the AssignTo here.)
unit NewCounterU;// 'Complex' adapterinterfaceuses 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;implementationuses 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.
In the next step we?ll change the user interface to test the Assign method.
Source...