Example 4.5 An immutable object
From example 4.2 it may seem that a constructor is optional. We can either use the default constructor inherited from the superclass and when initialise the values through the Set methods, or we can write a constructor specifically to initialise the values. With a constructor we have less need for Set methods, since values are initialised through the constructor and read through the Getters. However Setters are still useful for changing the values of an object after it has been initialised.Sometimes one or more of an objects data fields must be fixed they must be set when the object is constructed and then they are not to be changed. For example, to increase the security of the system, it may be important that a clients name or number cannot be changed once the TClient object has been created. In this situation we want to create an immutable object, where we initialise its value at creation but cannot set the data fields in any other way. (Immutable means cannot be changed.) In this situation a constructor becomes essential.
As the following steps show, we can create an immutable class by setting the immutable data fields in the object constructor and then providing only a Get access method or a read-only property. The destructor we introduced in example 4.2 was largely for illustrative purposes. For brevity we remove it from the remaining examples.
Ex 4.5 step 1 No Set methods
If one is using access methods, an immutable class has a constructor (to initialise the immutable data fields) and Get methods (to read the data fields) in the class definition but no Set methods for the immutable data fields. Since the data fields are private, it is not possible, in the absence of Set methods, to overwrite these existing data field values from any other unit.unit ClientU;
// Immutable: no Set methods
interface
type
TClient = class (TObject)
private
FAccNo: integer;
FCName: string;
public
constructor Create (ACName, AnAccNo: string) ;
function GetAccNo: string; // Only Getters, no Set methods
function GetCName: string;
end;
implementation
uses SysUtils;
{ TClient }
constructor TClient.Create(ACName, AnAccNo: string) ;
begin
inherited Create;
FAccNo := AnAccNo;
FCName := ACName;
end;
function TClient.GetAccNo: string;
begin
if FAccNo < 100000 then
Result := IntToStr(FAccNo + 100000)
else
Result := IntToStr(FAccNo) ;
end;
function TClient.GetCName: string;
begin
Result := FCName;
end;
end.
Ex 4.5 step 2 Read-only properties
If we are using properties, we create an immutable class by providing a constructor and read-only properties. We create read-only properties by leaving out the write mapping (lines 12&13 below). (We can also create write-only properties by providing no read mapping, but this is less common.) If we give our data fields public access instead of using properties, we cannot make a read-only or write-only restriction.unit ClientU;
// Immutable: read-only properties
interface
type
TClient = class (TObject)
private
FAccNo: integer;
FCName: string;
function GetAccNo: string;
public
constructor Create (ACName, AnAccNo: string) ;
property AccNo: string read GetAccNo; // no write method
property CName: string read FCName; // no write mapping
end;
implementation
uses SysUtils;
{ TClient }
constructor TClient.Create(ACName, AnAccNo: string) ;
begin
inherited Create;
FAccNo := StrToInt(AnAccNo) ;
FCName := ACName;
end;
function TClient.GetAccNo: string;
begin
if FAccNo < 100000 then
Result := IntToStr(FAccNo + 100000)
else
Result := IntToStr(FAccNo) ;
end;
end.

