Ex 4.3 step 3 Direct access properties
In step 1 of this example the mapping methods simply provide access to the data fields (example 4.1 step 1, lines 26 or 30).The Set procedures assign the incoming parameter to the private data field, using a single program statement.
The Get functions return the value of the underlying data field, also using a single program statement (example 4.1 step 1 lines 18 or 22). They do no extra manipulation and the access methods are there only to provide public access to the private data. Under these circumstances we can use direct mapping of properties to data fields and dispense with the access methods.
Creating properties with direct access to the underlying data gives a simpler class definition. Change the type declaration and the implementation section in ClientU.pas as follows:
1 unit ClientU;
2 interface
3 type
4 TClient = class (TObject)
5 private
6 FAccNo: string;
7 FCName: string;
8 public
9 constructor Create (ACName, AnAccNo: string) ;
10 destructor Destroy; override;
11 property AccNo: string read FAccNo write FAccNo;
12 property CName: string read FCName write FCName;
13 end;
14 implementation
15 { TClient }
16 { Constructor & destructor as before }
30 { No Get or Set methods needed }
31 end.
Instead ofreferring to access methods, the property declarations now refer directly to the underlying data field in the read and write mapping (lines 1112). A program reference to one of these properties now results in direct access to the underlying data item. Since no access methods are needed none are declared in the type section nor are they implemented in the implementation section.
The code is significantly less, but the screen display is exactly as before.
Example 4.4 Processing within access methods
As mentioned in example 4.3 step 2, we can do additional processing within access methods. Assume that a change request comes through that client numbers are always six digits without any alphabetic characters. This means that the FAccNo data field can now be declared as an integer instead of a string (this will save storage space) and that all existing client numbers with fewer that six digits must now be prefixed with a 1 and as many zeroes as are needed to make up six digits. New client numbers will start at 200000.Clearly we want to minimise the changes required in the program as much as possible, and the user interface at least should preferably be unaffected with changes restricted to the TClient class definition. (This is a situation where separation of concerns, as discussed in chapter 3, helps to decouple the user interface objects and the domain or application objects.)
We change the TClient classs direct access AccNo property in example 4.3 to a mapped access and do the necessary processing within the access methods. Because we are storing the account number as an integer, the constructor converts the incoming string to an integer. For variety, we will no longer issue a message when destroying a TClient object, and so we can leave out our implementation of the destrutor and rely instead on the implicitly inherited Destroy.
unit ClientU;CName remains a direct mapped property (line 14) while we map AccNo through methods. AccNo is still defined as a string property to keep the interface to other units unchanged (line 13) but the underlying data field, FAccNo has become an integer (line 6). Because of this, mapping access method SetAccNo converts the incoming string to an integer (line 33).
interface
type
TClient = class (TObject)
private
FAccNo: integer; // Now an integer
FCName: string;
function GetAccNo: string;
procedure SetAccNo(const AnAccNo: string) ;
public
constructor Create (ACName, AnAccNo: string) ; // no destructor
property AccNo: string read GetAccNo write SetAccNo; //method map
property CName: string read FCName write FCName; //direct map
end;
implementation
uses Dialogs, SysUtils; // for message & Int to Str conversions
{ TClient }
constructor TClient.Create(ACName, AnAccNo: string) ;
begin
inherited Create; // First invoke superclass's constructor
FAccNo := StrToInt(AnAccNo) ; // convert string to integer
FCName := ACName;
end; // end constructor TClient.Create
function TClient.GetAccNo: string;
begin
if FAccNo < 100000 then
Result := IntToStr(FAccNo + 100000)
else
Result := IntToStr(FAccNo) ;
end;
procedure TClient.SetAccNo(const AnAccNo: string) ;
begin
FAccNo := StrToInt(AnAccNo) ;
end;
end.
The user interface remains unchanged, and, because of encapsulation, is unaware that the underlying data representation has changed.

