Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Problem solutions
Identify the appropriate example(s) or section(s) of the chapter to illustrate each comment
made in the summary of chapter 4 above.
Several operations are needed in order to use an object: the class must be defined, the object
reference must be created, and an object must be instantiated and assigned to the reference.
The object is now ready for use. Once the program has no further need for the object, it
should be destroyed. These operations are needed for both RAD generated objects and
programmer generated objects. Describe briefly how each of these four steps are
accomplished in Delphi for both RAD and programmer generated objects, referring in each
case to appropriate examples in the module notes. Also identify the typical circumstances
for a small Delphi application under which RAD generated or programmer generated
objects are used.
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 1
RAD Programmer generated
Class definition Code generated by Delphi from Specify the class explicitly,
the form that the programmer giving the class name,
creates by dragging and derivation, data fields and
dropping components, eg ex 1.2, methods, eg 3.2 step 1, lines
step 2, lines 6–16; ex 1.3, step 1, 3–11.
lines 6–16.
Object instantiation – Project file instantiates all the Use the constructor method.
forms in the project, eg ex 1.3, This may be inherited from the
step 3, lines 9–10. superclass (eg ex 3.2) or written
– Each form instantiates all the specifically (eg ex 4.2).
objects (components) it owns
through the Owner property, eg
ex 1.3, step 7, line 44.
Object destruction When the application closes it Programmer writes code that
closes each form in turn. Each calls the object destructor to free
form has the responsibility for the object. The destructor may
freeing each component (object) be inherited from the superclass
that it owns, eg ex 1.3, step 7, or written specifically (eg ex
line 52. 4.2).
Use: Different User interface objects (ex 3.2) Application / domain objects (ex
program layers (in 3.2)
3 layer model)
Solutions Chapter 4, Page 2 Object orientation with Delphi (all rights reserved)
Problem 4.3 An object oriented traffic light
Code for a simple OO traffic light that works in a single direction is shown below. Change
the object communication in this version to make use of properties, as described after the
program listing.
1 unit TrafficLightU;
2 interface
4 type
5 TTrafficLight = class (TObject)
6 public
7 procedure NextState (var State: string; out Period: integer;
8 var StopLight, CautionLight, GoLight: TColor);
9 end;
10 implementation
11 { TTrafficLight }
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 3
The user interface is shown in figures 10 and 11. The program steps automatically through
the colours in sequence: 4 seconds red, 3 seconds green, 1 second yellow. The colour
displays are created from TShape components.
1 unit LightControlU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, ExtCtrls, StdCtrls,
6 TrafficLightU;
7 type
8 TfrmTrafficLight = class(TForm)
9 tmrTrafficLight: TTimer;
10 lblTrafficLight: TLabel;
11 shpRed: TShape;
12 shpYellow: TShape;
13 shpGreen: TShape;
14 procedure tmrTrafficLightTimer(Sender: TObject);
15 procedure FormShow(Sender: TObject);
16 private
17 MyTrafficLight: TTrafficLight;
18 Period: integer;
19 State: string;
20 StopLight, CautionLight, GoLight: TColor;
21 procedure UpDateDisplay;
22 end;
23 var
24 frmTrafficLight: TfrmTrafficLight;
Solutions Chapter 4, Page 4 Object orientation with Delphi (all rights reserved)
25 implementation
26 {$R *.dfm}
36 State := 'Caution';
37 MyTrafficLight.NextState(State, Period,
38 StopLight, CautionLight, GoLight);
39 UpDateDisplay;
40 tmrTrafficLight.Enabled := True;
41 end; // end procedure TfrmTrafficLight.FormShow
42 procedure TfrmTrafficLight.UpDateDisplay;
43 begin
44 tmrTrafficLight.Interval := Period;
45 lblTrafficLight.Caption := State;
46 shpRed.Brush.Color := StopLight;
47 shpYellow.Brush.Color := CautionLight;
48 shpGreen.Brush.Color := GoLight;
49 end; // end procedure TfrmTrafficLight.UpDateDisplay
TfrmTrafficLight has five private data fields to use as parameters in communicating with
TTrafficLight’s NextState method. Convert these five data fields into properties and then
change TTrafficLight’s NextState method to access these properties instead of using these
parameters. (Hint: Change the NextState method so that its call uses only a parameter
identifying the calling object. This means that the method declaration in line 7 of unit
TrafficLightU must change to:
procedure NextState (AClient: TForm); )
1 unit LightControlU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, ExtCtrls, StdCtrls,
6 TrafficLightU;
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 5
7 type
8 TfrmTrafficLight = class(TForm)
9 {Standard RAD declarations}
16 private
17 MyTrafficLight: TTrafficLight;
18 FPeriod: integer;
19 FState: string;
20 FStopLight: TColor;
21 FGoLight: TColor;
22 FCautionLight: TColor;
23 procedure UpDateDisplay;
24 public
25 property Period: integer read FPeriod write FPeriod;
26 property State: string read FState write FState;
27 property StopLight: TColor read FStopLight write FStopLight;
28 property CautionLight: TColor read FCautionLight
29 write FCautionLight;
30 property GoLight: TColor read FGoLight write FGoLight;
31 end; // end TfrmTrafficLight = class(TForm)
32 var
33 frmTrafficLight: TfrmTrafficLight;
34 implementation
35 {$R *.dfm}
49 procedure TfrmTrafficLight.UpDateDisplay;
50 begin
51 tmrTrafficLight.Interval := Period;
52 lblTrafficLight.Caption := State;
53 shpRed.Brush.Color := StopLight;
54 shpYellow.Brush.Color := CautionLight;
55 shpGreen.Brush.Color := GoLight;
56 end; // end procedure TfrmTrafficLight.UpDateDisplay
1 unit TrafficLightU;
Solutions Chapter 4, Page 6 Object orientation with Delphi (all rights reserved)
2 interface
3 uses Forms;
4 type
5 TTrafficLight = class (TObject)
6 public
7 procedure NextState (AClient: TForm);
8 end; // end TTrafficLight = class (TObject)
9 implementation
12 { TTrafficLight }
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 7
technique is termed a Callback, and we will see more of it later in this module.
Notice that a callback increases the coupling between two classes: to use the callback
TTrafficlight must know TfrmTrafficLight’s data fields in order to access them in lines 17
to 38 of TrafficLightU. To make this possible, we had to expose TfrmTrafficLight’s data
fields as public properties (lines 24 to 29 of LightControlU). In the original version given
in the question, the NextState method simply worked with the five incoming parameters.
It did not need to access TfrmTrafficLight’s data fields, which in turn could be left as
private data fields and not made into public properties.
Here we have situation where deciding which is the better implementation depends
on the circumstances. In this case, one might argue that NextState’s long parameter list in
the original version is a reasonable price to pay for the reduced coupling between the
classes.
In example 4.1 we use conditional statements to trap a reference to an object that does not
exist, following the form:
if ObjectRef = nil then
ShowMessage ('Error')
else
ObjectRef.Method;
Recode example 4.1 to use exception handling instead of conditional statements wherever
appropriate.
There are two aspects to this question: how does one choose between exception handling
and if statements, and how does one implement the exception handling?
As a broad statement we can say: when we need an operation on an object and we
can’t be sure whether or not that object exists we should use exception handling since
accessing a non-existent object leads to an exception. When we want to start by making
Solutions Chapter 4, Page 8 Object orientation with Delphi (all rights reserved)
sure that the object does not already exist, we use an If statement.
Looking at example 4.1, we see that in btnCreateClick we want to start with no existing
object and so we keep the existing if statement; trying to do this with exception handling
is very clumsy indeed and not required anyway.
However, in btnShowClick we want an object to be in existence and so we can convert
this to exception handling, as shown below.
procedure TfrmAccessObject.btnShowClick(Sender: TObject);
begin
try
lblName.Caption := 'Name: ' + NewClient.GetCName;
lblAccNo.Caption := 'Acc No: ' + NewClient.GetAccNo;
except
ShowMessage ('Create object first');
end;
end; // end procedure TfrmObjList.btnShowClick
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 9
Choosing to enter a new bird sighting, by clicking on the Bird RadioButton on the main
form (fig 12), brings up the bird entry form (fig 13):
Choosing to enter a new tree sighting from the main form brings up the tree entry form (fig
14):
Clicking on the Capture button on either the bird entry form or the tree entry form captures
the data, closes the form and inserts an entry into the list box on the main form. Clicking on
the Close button brings up a dialogue allowing the user either to capture the information or
to discard it before closing the form (fig 15):
Solutions Chapter 4, Page 10 Object orientation with Delphi (all rights reserved)
On the main form, selecting one of the entries in the list box and then clicking the Show Info
button shows the values of the data fields for that sighting (fig 16):
Clicking Help on any screen brings up the message ‘No Help yet for this screen’. Clicking
Close on any screen closes that screen, with the option of deciding whether or not to capture
the input on the information entry screens.
The data to be captured for the tree sightings is the sighting type (Protea Curvata), the
grid reference (a string) and the number of individuals in that grid (an integer). To these the
bird sightings add a breeding field (boolean) and the date of the sighting (a string).
You may well find it necessary to make several iterations between the program itself and
the UML diagrams while writing this program in order to get an appropriate degree of
encapsulation, generalisation / specialisation, and so on.
Comments and tips: The questions states that the program must incoroporate sound OO
principles. What would some of these principles be?
The user interface: If we look at the various user interface screens we see quite a bit of
commonality between them. For example, all the screens have Close and Help buttons on
a panel on the right side of the screen. Both types of capture screen add a Capture button
to these. These buttons have the same or similar functionality linked to them. In OO
systems, this kind of repetition should always raise the possibility of implementing the
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 11
common features once, higher in the hierarchy, and then reusing these features in the
subclasses through inheritance. In terms of the user interface in Delphi, this is an ideal
opportunity for using several levels of VFI, as in the code below, implementing the button
event handlers only once, as high as possible in the VFI hierarchy.
The domain objects: The bird objects are everything that the tree objects are, plus more. So it
is tempting to derive the TBirdSighting class directly from the TTreeSighting class.
However, this means that if any additional fields or methods are added to the
TTreeSighting class in future, these will automatically also become part of TBirdSighting,
which may or may not be desirable. So it is better to have a parent class TSighting, and to
derive both TTreeSighting and TBirdSighting from that. For the present, TTreeSighting
may be an empty class. But, in the future, if any addition is needed that applies only to
TTreeSighting, it can be added in that class. If any addition applies to both classes, it can
be added to TSighting so that the subclasses both inherit it. The code below incorporates
this.
1 unit SightingU;
2 interface
3 type
4 TSighting = class(TObject)
5 private
6 FGrid: string;
7 FNumber: integer;
8 FSightType: string;
9 public
10 property Grid: string read FGrid; // Immutable properties
11 property Number: integer read FNumber; // no of individuals
12 property SightType: string read FSightType;
13 constructor Create (AGrid: string; ANumber: integer);
14 end; // end TSighting = class(TObject)
15 TTreeSighting = class(TSighting)
16 public
17 constructor Create (AGrid: string; ANumber: integer;
18 ASightType: string); // maintain immutability
19 end; // end TTreeSighting = class(TSighting)
20 TBirdSighting = class(TSighting)
21 private
22 FBreeding: boolean;
23 FSightDate: string;
24 public
25 property Breeding: boolean read FBreeding;
26 property SightDate: string read FSightDate;
27 constructor Create (AGrid: string;
28 ANumber: integer; ASightType: string;
29 ABreeding: boolean; ASightDate: string);
Solutions Chapter 4, Page 12 Object orientation with Delphi (all rights reserved)
30 end; // end TBirdSighting = class(TSighting)
31 implementation
32 { TSighting }
40 { TBirdSighting }
50 { TTreeSighting }
1 unit MainFormU;
3 interface
4 uses
5 Windows, Messages, SysUtils, Variants, Classes, Graphics,
6 Controls, Forms, Dialogs, TemplateU, Buttons, StdCtrls, ExtCtrls,
7 SightingU;
8 type
9 TfrmMain = class(TfrmTemplate)
10 {Standard RAD declarations}
17 private
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 13
18 FNewSighting: TSighting;
19 end; // end TfrmMain = class(TfrmTemplate)
20 var
21 frmMain: TfrmMain;
22 implementation
24 {$R *.dfm}
52 TypeOfSighting := rgpNew.Items[rgpNew.ItemIndex];
53 rgpNew.ItemIndex := -1;
Solutions Chapter 4, Page 14 Object orientation with Delphi (all rights reserved)
65 with frmGetTreeInfo do
66 begin
67 ElicitInfo;
68 if InfoValid = False then
69 exit;
70 FNewSighting := TTreeSighting.Create (Grid,
71 Number, SightType);
72 lstSightings.AddItem(FNewSighting.SightType, FNewSighting);
73 end
74 else
75 ShowMessage ('Unknown type of sighting');
76 end; // end procedure TfrmMain.rgpNewClick
1 unit TemplateU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, Buttons, StdCtrls, ExtCtrls;
6 type
7 TfrmTemplate = class(TForm)
8 {Standard RAD declarations}
13 end; // end TfrmTemplate = class(TForm)
14 var
15 frmTemplate: TfrmTemplate;
16 implementation
17 {$R *.dfm}
Now the second level template containing all the commonality for the two GetInfo
screens:
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 15
1 unit GetInfoTemplateU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, TemplateU, StdCtrls, Buttons, ExtCtrls,
6 SightingU, Spin, ComCtrls;
7 type
8 TfrmGetInfoTemplate = class(TfrmTemplate)
9 {Standard RAD declarations}
16 private
17 function GetGrid: string;
18 function GetNumber: integer;
19 protected
20 FInfoValid: boolean;
21 FSightType: string;
22 function GetSightType: string;
23 public
24 property Grid: string read GetGrid;
25 property InfoValid: boolean read FInfoValid;
26 property Number: integer read GetNumber;
27 property SightType: string read GetSightType;
28 function ElicitInfo: boolean; // request & capture data
29 end; // end TfrmGetInfo = class(TfrmTemplate)
30 var
31 frmGetInfoTemplate: TfrmGetInfoTemplate;
32 implementation
33 {$R *.dfm}
Solutions Chapter 4, Page 16 Object orientation with Delphi (all rights reserved)
52 Result := sedNumber.Value;
53 end; // end function TfrmGetTreeInfo.GetNumber
The class for collecting the bird information comes next. There is some overriding here
and in the tree capture screen that follows. In chapter 6 we will look at dynamic and static
binding in detail, and with that information we could make small improvements to this
program. But for now we can leave this as it is.
1 unit GetBirdInfoU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, GetInfoTemplateU, ComCtrls, StdCtrls,
6 ExtCtrls, Spin;
7 type
8 TfrmGetBirdInfo = class(TfrmGetInfoTemplate)
9 {Standard RAD declarations}
13 protected
14 function GetBreeding: boolean;
15 function GetSightDate: string;
16 function GetSightType: string; // override
17 public
18 property Breeding: boolean read GetBreeding;
19 property SightDate: string read GetSightDate;
20 property SightType: string read GetSightType; // override
21 function ElicitInfo: boolean; // override: request & capture data
22 end; // end TfrmGetBirdInfo = class(TfrmGetInfoTemplate)
23 var
24 frmGetBirdInfo: TfrmGetBirdInfo;
25 implementation
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 17
26 {$R *.dfm}
1 unit GetTreeInfoU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, GetInfoTemplateU, StdCtrls, Spin,
6 ExtCtrls;
7 type
8 TfrmGetTreeInfo = class(TfrmGetInfoTemplate)
9 lblProtea: TLabel;
10 protected
11 function GetSightType: string;
Solutions Chapter 4, Page 18 Object orientation with Delphi (all rights reserved)
12 public
13 property SightType: string read GetSightType;
14 end; // end TfrmGetTreeInfo = class(TfrmGetInfoTemplate)
15 var
16 frmGetTreeInfo: TfrmGetTreeInfo;
17 implementation
18 {$R *.dfm}
19 { TfrmGetTreeInfo }
1 unit ShowInfoU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, TemplateU, StdCtrls, Buttons, ExtCtrls,
6 SightingU;
7 type
8 TfrmShowInfo = class(TfrmTemplate)
9 {Standard RAD declarations}
15 public
16 procedure ShowInfo (ASighting: TSighting); // get ref to Sighting
17 end; // end TfrmShowInfo = class(TfrmTemplate)
18 var
19 frmShowInfo: TfrmShowInfo;
20 implementation
21 {$R *.dfm}
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 19
30 with (ASighting as TBirdSighting) do
31 begin
32 lblDate.Caption := 'Date: ' + SightDate;
33 lblDate.Visible := True;
34 lblBreeding.Caption := 'Breeding: ' +
35 BoolToStr(Breeding, True);
36 lblBreeding.Visible := True;
37 end
38 else if ASighting is TTreeSighting then
39 begin
40 lblDate.Visible := False;
41 lblBreeding.Visible := False;
42 end;
43 ShowModal;
44 end; // end procedure TfrmShowInfo.ShowInfo
In a software development project the UML diagrams support the development process
both by helping to capture the requirements during analysis activities, an aspect we do not
cover in this module, and by helping to specify and document the code, an aspect that is
relevant to this module. It may seem obvious to say so, but these diagrams are not the code
itself, and so the software developer must decide what are the important aspects to model in
the UML diagrams. So one generally shows enough detail for clarity and to communicate
the important aspects but avoids unnecessary clutter and confusion. On this basis, in the
diagrams that follow we suppress:
– Private data, and protected data that provides the underlying data structure for
descendants’ properties,
– ‘Standard’ methods, such as constructors,
– Attributes that are references to composed objects (we could also have shown these
attributes and not the composed objects),
– VCL components’ attributes and operations (since they are standard).
Also to avoid clutter, we use the ‘tree’ layout for generalisation (all common generalisation
arrowheads coincide) and for some composition relations (TfrmGetInfoTemplate to
TGroupBox and TfrmGetInfoTemplate to TEdit).
The domain model is quite simple. It consists of a generic Sighting, modelled as TSighting.
TBirdSighting adds two attributes to the generic TSighting. TTreeSighting adds no extra
attributes, but we still declare it as a separate class, derived from the generic TSighting since
it is a specific kind of Sighting and so not the same thing as the generic TSighting.
Solutions Chapter 4, Page 20 Object orientation with Delphi (all rights reserved)
Domain model for capturing
the sightings
The user interface structure is quite a bit more complex in this case, particularly since we
make extensive use of VFI:
Accessing an object and its data (20 Jun 2006, all rights reserved) Chapter 4, Page 21
High level sequence diagram for capturing sightings
Solutions Chapter 4, Page 22 Object orientation with Delphi (all rights reserved)