Ex 9.2 step 2 Minimising coupling
This new version of the program, and the principle of making a deep copy, both work, but there are still a few problems with it. First lets look at the level of coupling between TSale and TCustomer. What happens if, at some future time, we need to change the TCustomer class, possibly by adding a CellNo field? We then have to remember to come back to the TSale.Create method to make sure we copy this additional field too. It's easy to forget about it, so is there a more secure way to do this?Yes there is. A principle that is sometimes applied in OO programming is that of Nice Classes. A Nice Class is able to copy and clone itself, to save and read itself and to check whether two instances have equal values for each data field. We wont go into all of these aspects here, but we will look at a class being able to make a deep copy of itself. Then, instead of actually copying each of TCustomers fields, TSale could ask TCustomer to copy itself. If TCustomer changes in future, its copy method should change accordingly. Adding additional fields to TCustomer will then not affect TSale at all and so this is a more robust route to take. Change TCustomer as follows to add a CopyFrom method:
unit CustomerU;The CopyFrom method receives a reference to an existing object of the same class and then sets the data field values to those of the existing object. Now if we in the future add a CellNo property to TCustomer, we extend the CopyFrom method to copy this field too:
interface
type
TCustomer = class(TObject)
private
FPhoneNo: string;
FName: string;
public
property Name: string read FName write FName;
property PhoneNo: string read FPhoneNo write FPhoneNo;
procedure CopyFrom (ACustomer: TCustomer) ;
end;
implementation
procedure TCustomer.CopyFrom (ACustomer: TCustomer) ;
begin
Self.Name := ACustomer.Name;
Self.PhoneNo := ACustomer.PhoneNo;
end;
end.
Self.CellNo := ACustomer.CellNo;When writing a new class it is generally a good idea to add this additional CopyFrom method even if it is not needed immediately since it generally does not require much extra work but can make a big difference to future users of the class.
In general, a composed class (TSale in this case) could also provide a CopyFrom method to allow its values to be copied easily. However, in this particular application, TSale has been made immutable. Implementing a CopyFrom method for TSale would provide a way of changing the values of an existing TSale object and so would be breaking the immutability. What is sometimes done in a case like this is to create a CloneFrom method which creates a new object with the values set to those of an existing object. A CloneFrom method effectively packages a constructor and a deep copy into a single function method.
Ex 9.2 step 3 Final version of TSale
Change TSale to take advantage of TCustomers new CopyFrom method:constructor TSale.Create(ACustomer: TCustomer; AnAmount: string) ;Test the program as in step 1. Allowing TCustomer to copy itself works well, leading to a more robust implementation that is less antagonistic to future change than if TSale takes responsibility for copying TCustomers fields.
begin
inherited Create;
FCustomer := TCustomer.Create;
FCustomer.CopyFrom (ACustomer) ;
FAmount := AnAmount;
end;
Ex 9.2 step 4 A minor programming refinement
The CopyFrom method can be simplified slightly. Since unqualified identifiers default to the active object, we can dispense with the Self qualifier in CopyFrom.procedure TCustomer.CopyFrom(ACustomer: TCustomer) ;
begin
Name := ACustomer.Name;
PhoneNo := ACustomer.PhoneNo;
end;

