1. Computing

Accessing an Object and its Data - Delphi OOP Part 4 / Chapter 9

Direct access properties; Processing within access methods

From , former About.com Guide

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:

unit ClientU;
 
 2 interfacetype
 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 11–12). 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 class’s 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;
 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. 
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).

The user interface remains unchanged, and, because of encapsulation, is unaware that the underlying data representation has changed.

©2013 About.com. All rights reserved.