Introducing (in this Chapter):
- Chaining and delegation in RAD classes.
- Patterns for the Law of Demeter and for Delegation.
Example 8.3 Delegation
As we mentioned, the data fields of the intermediate class, TMuseumCount in example 8.2, are public. Isnt it almost a golden rule of object orientation that data fields are private, and that if we want to access these data fields from outside the unit, we must use access methods of some form or other?This is an important rule, and well incorporate it in this example. Well declare the TCounter objects in TMuseumCount as private data fields. TMuseumCount will have public access methods and properties which will interact with the TCounter objects. In doing this, we will move from chaining to delegation (or forwarding). Delegation overcomes the encapsulation leakage that is inevitably part of chaining.
The intermediate object now encapsulates the subsidiary objects instead of exposing them, with similar benefits to those in other forms of encapsulation. The cost is that we must now write access methods, which in this situation are also referred to as delegation methods or forwarding methods since they delegate or forward operations to other objects.
Ex 8.3 step 1 Using delegation
The revised version of MuseumCountU is:unit MuseumCountU;We have made the data fields private (line 8) with the result that we have to create six access methods. Three of these are mapped to read-only properties (VEntered, VInside and VLeft). The other three are mutator methods, though not Set methods (Arrivals, Departures and ClearAll).
// using delegation
interface
uses CounterU;
type
TMuseumCount = class(TObject)
private
VEntered, VInside, VLeft: TCounter;
// property mapping methods (use delegation)
function GetEntered: integer;
function GetInside: integer;
function GetLeft: integer;
public
// read-only, method mapped properties using delegation
property Entered: integer read GetEntered;
property Inside: integer read GetInside;
property Left: integer read GetLeft;
constructor Create;
procedure Arrivals (aNumber: integer) ;
procedure ClearAll;
procedure Departures (aNumber: integer) ;
end;
implementation
{ TMuseumCount }
procedure TMuseumCount.Arrivals(aNumber: integer) ;
begin
VEntered.Add (aNumber) ;
VInside.Add (aNumber) ;
end;
procedure TMuseumCount.Departures(aNumber: integer) ;
begin
VInside.Subtract (aNumber) ;
VLeft.Add (aNumber) ;
end;
function TMuseumCount.GetEntered: integer;
begin
Result := VEntered.Total;
end;
function TMuseumCount.GetInside: integer;
begin
Result := VInside.Total;
end;
function TMuseumCount.GetLeft: integer;
begin
Result := VLeft.Total;
end;
procedure TMuseumCount.ClearAll;
begin
VEntered.Clear;
VInside.Clear;
VLeft.Clear;
end;
constructor TMuseumCount.Create;
begin
// propagate construction
inherited Create;
VEntered := TCounter.Create;
VInside := TCounter.Create;
VLeft := TCounter.Create;
end;
end.
These access methods all interact with the TCounter objects: TMuseumCount delegates all the data storage and manipulation to its subordinate objects, freeing the user interface from any concern over the details of the application logic.
From a different perspective, we can also say that the TMuseumClass is a composed class with three constituent TCounter objects: TMuseumCount is composed of (HasA) VEntered, VInside and VLeft objects.
Ex 8.3 step 2 Using the delegated methods
To use the new version of TMuseumCount we change the two OnClick event handlers in the user interface class as follows:procedure TfrmAttendance.btnEnterClick(Sender: TObject) ;
begin
MuseumCount.Arrivals (sedEnter.Value) ;
lblIn.Caption := IntToStr(MuseumCount.Entered) ;
lblVisitors.Caption := IntToStr(MuseumCount.Inside) ;
sedEnter.Value := 0;
end;
procedure TfrmAttendance.btnLeaveClick(Sender: TObject) ;
begin
with MuseumCount do
begin
Departures (sedLeave.Value) ;
lblOut.Caption := IntToStr(Left) ; // ** dangerous??
lblVisitors.Caption := IntToStr(Inside) ;
end;
sedLeave.Value := 0;
end;

