OOP Delphi

Title: ENUMERATED TYPES Part II
Question: Second part to my enumerated types article
Answer:
This second part looks at using objects as apposed to enumerated types.
Here is my example: I included the interface ICompass which is implemented by the TCompass class. TTraveller now uses the ICompass as apposed to TCompass.
First Unit our Compass class and interface:
unit cls_Compass;
interface
uses Windows, Messages, SysUtils, Classes;
type
EDirectionError = class(Exception)
end;
(* No Enumerated types *)
TDirection = class
protected
fHeading : Double;
public
constructor Create(pHeading : Double); virtual;
property Heading : Double read fHeading;
end;
(* Created Interface ICompass which will be used by the TTraveller Class
instead of TCompass
*)
ICompass = interface
['{2D7EB6E1-0295-11D5-A9CC-00105AA43BF4}']
function GetHeading : Double;
procedure SetHeading (pDirection : TDirection);
property Heading : Double read GetHeading;
end;
TCompass = class(TInterfacedObject, ICompass)
protected
fCurrentDirection : TDirection;
(* No enumerated type here to set array dimensions so made it a TList *)
FAvailableDirections : TList;
function GetHeading : Double;
procedure RegisterDirection (pDirection : TDirection);
property Heading : Double read GetHeading;
public
constructor Create; virtual;
destructor Destory;
procedure SetHeading (pDirection : TDirection);
property AvailableDirections : TList read FAvailableDirections;
end;
var drNorth, drSouth, drEast, drWest : TDirection;
implementation
constructor TDirection.Create(pHeading : Double);
begin
inherited Create;
if (pHeading 359.9999) then
raise EDirectionError.Create('Heading is outwith bounds');
fHeading := pHeading;
end;
constructor TCompass.Create;
begin
inherited;
(* Must create our Directions first *)
try
drNorth := TDirection.Create(0.0);
drEast := TDirection.Create(90.0);
drSouth := TDirection.Create(180.0);
drWest := TDirection.Create(270.0);
except
on EDirectionError do
Exit;
end;//try
fCurrentDirection := drNorth;
fAvailableDirections := TList.Create;
(* Add our Directions to available directions *)
RegisterDirection(drNorth);
RegisterDirection(drEast);
RegisterDirection(drSouth);
RegisterDirection(drWest);
end;//Create
destructor TCompass.Destory;
begin
with FAvailableDirections do
while Count 0 do
begin
TDirection(Items[0]).Free;
Delete(0);
end;//while
end;
procedure TCompass.RegisterDirection (pDirection : TDirection);
begin
fAvailableDirections.Add(pDirection);
end;//RegisterDirection
function TCompass.GetHeading : Double;
begin
Result := TDirection(fCurrentDirection).Heading;
end;//GetHeading
procedure TCompass.SetHeading(pDirection : TDirection);
begin
if FAvailableDirections.Indexof(pDirection) = -1 then
raise EDirectionError.Create('Direction Not avaiable with this compass')
else
fCurrentDirection := TDirection(FAvailableDirections.Items[FAvailableDirections.Indexof(pDirection)]);
end;//SetHeading
end.
Our Traveller Form :
unit Travel;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, cls_Compass;
type
TDirectionChangeEvent = procedure(Sender : TObject) of object;
TTraveller = class(TForm)
btnChangeCompass: TButton;
lbHeading: TLabel;
procedure FormCreate(Sender: TObject);
procedure btnChangeCompassClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
(* Modified so TTraveller uses the ICompass interface *)
fCompass : ICompass;
fOnDirectionChange : TDirectionChangeEvent;
procedure DoDirectionChange;
protected
procedure SetCompassHeading (pDirection : TDirection); virtual;
procedure DisplayHeading(Sender : TObject);
public
{ Public declarations }
property OnDirectionChange : TDirectionChangeEvent read fOnDirectionChange write fOnDirectionChange;
end;
var
Traveller: TTraveller;
implementation
{$R *.DFM}
procedure TTraveller.SetCompassHeading (pDirection : TDirection);
begin
fCompass.SetHeading(pDirection);
DoDirectionChange;
end;//SetHeading
procedure TTraveller.DisplayHeading(Sender : TObject);
begin
lbHeading.Caption := FloatToStr(fCompass.Heading);
end;
procedure TTRaveller.DoDirectionChange;
begin
if Assigned(fOnDirectionChange) then
fOnDirectionChange(Self);
end;//DoDirectionChange
procedure TTraveller.FormCreate(Sender: TObject);
begin
try
fCompass := TCompass.Create;
except
on EDirectionError do
begin
ShowMessage('Compass not Created properly');
Exit;
end;
end;//try
Traveller.OnDirectionChange := DisplayHeading;
DoDirectionChange;
end;
procedure TTraveller.btnChangeCompassClick(Sender: TObject);
var vDirection : TDirection;
begin
(* Just so we can change the direction Randomly
Random number between 0 and *)
case Random(4) of
0: vDirection := drNorth;
1: vDirection := drEast;
2: vDirection := drWest;
3: vDirection := drSouth;
else
vDirection := drNorth;
end;//case
try
SetCompassHeading(vDirection);
except
on E : EDirectionError do
ShowMessage(E.Message);
end;
end;
procedure TTraveller.FormDestroy(Sender: TObject);
begin
(* Cleanup *)
if Assigned(drNorth) then
drNorth.Free;
if Assigned(drEast) then
drEast.Free;
if Assigned(drSouth) then
drSouth.Free;
if Assigned(drWest) then
drWest.Free;
end;
end.
Advantages:
The main advantage of using objects is that if I want to add more instance variables to TDirection I can it do when it's a class. I can use OO inheritance etc.. TNewDirection = class(TDirection)
Disadvantages:
Extra Coding to get similar functionality, must instanciate all object and destory them afterwards.
If I add another direction such as drNothEast means more coding.
I welcome any of your comments.
(***************************************************************************)
Here the code without the Global Vars:
Compass Unit:
unit cls_Compass;
interface
uses Windows, Messages, SysUtils, Classes;
type
EDirectionError = class(Exception)
end;
(* No Enumerated types *)
TDirection = class
protected
fHeading : Double;
fDirectionName : String;
public
constructor Create(pHeading : Double; pDirectionName : String ); virtual;
property Heading : Double read fHeading;
property DirectionName : String read fDirectionName;
end;
(* Created Interface ICompass which will be used by the TTraveller Class
instead of TCompass
*)
ICompass = interface
['{2D7EB6E1-0295-11D5-A9CC-00105AA43BF4}']
function GetHeading : Double;
function GetDirection(pDirectionName : String) : TDirection;
procedure SetHeading (pDirection : TDirection);
property Heading : Double read GetHeading;
end;
TCompass = class(TInterfacedObject, ICompass)
protected
fCurrentDirection : TDirection;
(* No enumerated type here to set array dimensions so made it a TList *)
FAvailableDirections : TList;
function GetHeading : Double;
property Heading : Double read GetHeading;
public
constructor Create; virtual;
destructor Destory;
procedure SetHeading (pDirection : TDirection);
function GetDirection(pDirectionName : String) : TDirection;
//function GetDirection(pHeading : Double) : TDirection;
property AvailableDirections : TList read FAvailableDirections;
end;
implementation
constructor TDirection.Create(pHeading : Double; pDirectionName : String);
begin
inherited Create;
if (pHeading 359.9999) then
raise EDirectionError.Create('Heading is outwith bounds');
fHeading := pHeading;
fDirectionName := pDirectionName;
end;
constructor TCompass.Create;
begin
inherited;
(* Must create our Directions first *)
try
fAvailableDirections := TList.Create;
(* Add our Directions to available directions *)
fAvailableDirections.Add(TDirection.Create(0.0,'North'));
fAvailableDirections.Add(TDirection.Create(90.0,'East'));
fAvailableDirections.Add(TDirection.Create(180.0,'South'));
fAvailableDirections.Add(TDirection.Create(270.0,'West'));
except
on EDirectionError do
Exit;
end;//try
fCurrentDirection := TDirection(fAvailableDirections.Items[0]);
end;//Create
destructor TCompass.Destory;
begin
with FAvailableDirections do
while Count 0 do
begin
TDirection(Items[0]).Free;
Delete(0);
end;//while
end;
function TCompass.GetDirection(pDirectionName : String) : TDirection;
var CurrentDirectionToCheck : Integer;
begin
CurrentDirectionToCheck := 0;
Result := nil;
with fAvailableDirections do
while CurrentDirectionToCheck begin
if TDirection(fAvailableDirections.Items[CurrentDirectionToCheck]).DirectionName = pDirectionName then
begin
Result := TDirection(fAvailableDirections.Items[CurrentDirectionToCheck]);
Exit;
end;//if
Inc(CurrentDirectionToCheck);
end;//while
if Result = nil then
Raise EDirectionError.Create('This Direction is not available with this compass');
end;//GetDirection
function TCompass.GetHeading : Double;
begin
Result := TDirection(fCurrentDirection).Heading;
end;//GetHeading
procedure TCompass.SetHeading(pDirection : TDirection);
begin
if FAvailableDirections.Indexof(pDirection) = -1 then
raise EDirectionError.Create('Direction Not avaiable with this compass')
else
fCurrentDirection := TDirection(FAvailableDirections.Items[FAvailableDirections.Indexof(pDirection)]);
end;//SetHeading
end.
Traveller unit:
unit Travel;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, cls_Compass;
type
TDirectionChangeEvent = procedure(Sender : TObject) of object;
TTraveller = class(TForm)
btnChangeCompass: TButton;
lbHeading: TLabel;
procedure FormCreate(Sender: TObject);
procedure btnChangeCompassClick(Sender: TObject);
private
(* Modified so TTraveller uses the ICompass interface *)
fCompass : ICompass;
fOnDirectionChange : TDirectionChangeEvent;
procedure DoDirectionChange;
protected
procedure SetCompassHeading (pDirection : TDirection); virtual;
procedure DisplayHeading(Sender : TObject);
public
{ Public declarations }
property OnDirectionChange : TDirectionChangeEvent read fOnDirectionChange write fOnDirectionChange;
end;
var
Traveller: TTraveller;
implementation
{$R *.DFM}
procedure TTraveller.SetCompassHeading (pDirection : TDirection);
begin
fCompass.SetHeading(pDirection);
DoDirectionChange;
end;//SetHeading
procedure TTraveller.DisplayHeading(Sender : TObject);
begin
lbHeading.Caption := FloatToStr(fCompass.Heading);
end;
procedure TTRaveller.DoDirectionChange;
begin
if Assigned(fOnDirectionChange) then
fOnDirectionChange(Self);
end;//DoDirectionChange
procedure TTraveller.FormCreate(Sender: TObject);
begin
try
fCompass := TCompass.Create;
except
on EDirectionError do
begin
ShowMessage('Compass not Created properly');
Exit;
end;
end;//try
Traveller.OnDirectionChange := DisplayHeading;
DoDirectionChange;
end;
procedure TTraveller.btnChangeCompassClick(Sender: TObject);
var vDirection : TDirection;
begin
(* Just so we can change the direction Randomly
Random number between 0 and *)
try
case Random(4) of
0: vDirection := fCompass.GetDirection('North');
1: vDirection := fCompass.GetDirection('East');
2: vDirection := fCompass.GetDirection('South');
3: vDirection := fCompass.GetDirection('West');
end;//case
SetCompassHeading(vDirection);
except
on E : EDirectionError do
ShowMessage(E.Message);
end;//try
end;
end.