Sunteți pe pagina 1din 22

Chapter 4 Accessing an object

and its data

Problem solutions

Problem 4.1 Study Chapter 4

Identify the appropriate example(s) or section(s) of the chapter to illustrate each comment
made in the summary of chapter 4 above.

Problem 4.2 Differences between RAD and


programmer code

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 reference Derived automatically by Declared explicitly by the


Delphi from the name the programmer, eg 3.2, step 2, lines
programmer gives in the Object 27–28.
Inspector, eg ex 1.2, step 2, lines
17–18; ex 1.3, step 1, lines 17–18.

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

3 uses Graphics; // for TColor

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 }

12 procedure TTrafficLight.NextState(var State: string;


13 out Period: integer;
14 var StopLight, CautionLight, GoLight: TColor);
15 begin
16 if State = 'Stop' then
17 begin
18 Period := 3000;
19 State := 'Go';
20 StopLight := clBlack;
21 GoLight := clGreen;
22 end
23 else if State = 'Go' then
24 begin
25 Period := 1000;
26 State := 'Caution';
27 GoLight := clBlack;
28 CautionLight := clYellow;
29 end
30 else
31 begin
32 Period := 4000;
33 State := 'Stop';
34 CautionLight := clBlack;
35 StopLight := clRed;
36 end;
37 end; // end procedure TTrafficLight.NextState

38 end. // end unit TrafficLightU

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.

Figure 11 User interface


components for the traffic light
display
Figure 10 The traffic light
display showing yellow

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}

27 procedure TfrmTrafficLight.tmrTrafficLightTimer(Sender: TObject);


28 begin
29 MyTrafficLight.NextState(State, Period,
30 StopLight, CautionLight, GoLight);
31 UpDateDisplay;
32 end; // end procedure TfrmTrafficLight.tmrTrafficLightTimer

33 procedure TfrmTrafficLight.FormShow(Sender: TObject);


34 begin
35 MyTrafficLight := TTrafficLight.Create;

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

50 end. // end unit LightControlU

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}

36 procedure TfrmTrafficLight.tmrTrafficLightTimer(Sender: TObject);


37 begin
38 MyTrafficLight.NextState(Self);
39 UpDateDisplay;
40 end; // end procedure TfrmTrafficLight.tmrTrafficLightTimer

41 procedure TfrmTrafficLight.FormShow(Sender: TObject);


42 begin
43 MyTrafficLight := TTrafficLight.Create;
44 State := 'Caution';
45 MyTrafficLight.NextState(Self);
46 UpDateDisplay;
47 tmrTrafficLight.Enabled := True;
48 end; // end procedure TfrmTrafficLight.FormShow

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

57 end. // end unit LightControlU

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

10 uses LightControlU, // to access the properties


11 Graphics; // for TColor

12 { TTrafficLight }

13 procedure TTrafficLight.NextState (AClient: TForm);


14 begin
15 if (AClient is TfrmTrafficLight) then
16 with TfrmTrafficLight(AClient) do
17 begin
18 if State = 'Stop' then
19 begin
20 Period := 3000;
21 State := 'Go';
22 StopLight := clBlack;
23 GoLight := clGreen;
24 end
25 else if State = 'Go' then
26 begin
27 Period := 1000;
28 State := 'Caution';
29 GoLight := clBlack;
30 CautionLight := clYellow;
31 end
32 else
33 begin
34 Period := 4000;
35 State := 'Stop';
36 CautionLight := clBlack;
37 StopLight := clRed;
38 end;
39 end;
40 end; // end procedure TTrafficLight.NextState

end. // end unit TrafficLightU

Is this version an improvement on the previous one? We have simplified NextState’s


parameter list significantly and now identify only the calling object (lines 7 & 13 in unit
TrafficLightU), allowing the traffic light object to access the user interface data fields. To
use this, the user interface calls the NextState method and provides a reference to iteself
through the (automatically declared) Self variable (lines 38 & 45 of LightControlU). This

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.

Problem 4.4 Trapping references to non-existent


objects

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;

A widely-used alternative is exception handling:


try
ObjectRef.Method;
except
ShowMessage ('Error');
end;

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

Problem 4.5 Inheritance for reuse

A team of biological researchers working near the Mpumalanga – Swaziland border is


investigating the distribution of southern Africa’s rarest protea, the protea curvata. They are
also investigating the distribution of the Barred Owl and the Pearl-spotted Owl in the same
area. There are plans to investigate the status of certain insects and snakes in the same area
in the future, but the details of these research projects will only be finalised once funds
become available.
A small computer program would greatly help this team of researchers, and it is our job
to write the first prototype based on the screens shown below. This program must be easy to
extend and so must incorporate sound object-oriented design techniques.
The opening screen allows the user to select the required operation (fig 12):

Figure 12 The main form for the Sightings program

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):

Figure 13 Capturing the bird information

Choosing to enter a new tree sighting from the main form brings up the tree entry form (fig
14):

Figure 14 Capturing the tree information

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):

Figure 15 Options on clicking


the Close button

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):

Figure 16 Showing the captured information

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).

For this question you must:


1. Write the program specified above,
2. Draw a class diagram to show the structure and relationship of the user interface and
domain model classes, and
3. Draw a sequence diagram showing the important interactions.

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 }

33 constructor TSighting.Create(AGrid: string;


34 ANumber: integer);
35 begin
36 FGrid := AGrid;
37 FNumber := ANumber;
38 FSightType := 'Unknown';
39 end; // end constructor TSighting.Create

40 { TBirdSighting }

41 constructor TBirdSighting.Create(AGrid: string;


42 ANumber: integer; ASightType: string;
43 ABreeding: boolean; ASightDate: string);
44 begin
45 inherited Create (AGrid, ANumber);
46 FSightType := ASightType;
47 FBreeding := ABreeding;
48 FSightDate := ASightDate;
49 end; // end constructor TBirdSighting

50 { TTreeSighting }

51 constructor TTreeSighting.Create(AGrid: string;


52 ANumber: integer; ASightType: string);
53 begin
54 inherited Create (AGrid, ANumber);
55 FSightType := ASightType;
56 end; // end constructor TTreeSighting

57 end. // end SightingU

Now the main form:

1 unit MainFormU;

2 //Displays a list of Sightings which can also each be deleted.

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

23 uses ShowInfoU, GetBirdInfoU, GetTreeInfoU;

24 {$R *.dfm}

25 procedure TfrmMain.btnDelClick(Sender: TObject);


26 begin
27 if lstSightings.ItemIndex >= 0 then
28 begin
29 lstSightings.Items.Objects [lstSightings.ItemIndex].Free;
30 lstSightings.DeleteSelected;
31 end;
32 end; // end procedure TfrmMain.btnDelClick

33 procedure TfrmMain.btnShowInfoClick(Sender: TObject);


34 var
35 Temp: TObject;
36 begin
37 if lstSightings.ItemIndex >= 0 then
38 begin
39 Temp := lstSightings.Items.Objects [lstSightings.ItemIndex];
40 frmShowInfo.ShowInfo (Temp as TSighting);
41 end;
42 end; // end procedure TfrmMain.btnShowInfoClick

43 procedure TfrmMain.rgpNewClick(Sender: TObject);


44 var
45 TypeOfSighting: string;
46 begin
47 if rgpNew.ItemIndex < 0 then
48 begin
49 ShowMessage ('Please select the type of sighting');
50 Exit;
51 end;

52 TypeOfSighting := rgpNew.Items[rgpNew.ItemIndex];
53 rgpNew.ItemIndex := -1;

54 if TypeOfSighting = 'Bird' then


55 with frmGetBirdInfo do
56 begin
57 ElicitInfo;
58 if InfoValid = False then
59 exit;
60 FNewSighting := TBirdSighting.Create (Grid,
61 Number, SightType, Breeding, SightDate);
62 lstSightings.AddItem(FNewSighting.SightType, FNewSighting);
63 end
64 else if TypeOfSighting = 'Tree' then

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

77 end. // end MainFormU

Now the VFI hierarchy. First the base template:

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}

18 procedure TfrmTemplate.btnHelpClick(Sender: TObject);


19 begin
20 ShowMessage ('No Help yet for this screen');
21 end; // end procedure TfrmTemplate.btnHelpClick

22 procedure TfrmTemplate.btnCloseClick(Sender: TObject);


23 begin
24 Close;
25 end; // end procedure TfrmTemplate.btnCloseClick

26 end. // end TemplateU

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}

34 function TfrmGetInfoTemplate.ElicitInfo: boolean;


35 begin
36 // Initialise
37 edtGrid.Text := '';
38 sedNumber.Value := 0;

39 ShowModal; // Elicit Info


40 Result := FInfoValid;
41 end; // end function TfrmGetTreeInfo.ElicitInfo

42 function TfrmGetInfoTemplate.GetSightType: string;


43 begin
44 Result := 'Unknown';
45 end; // end function TfrmGetTreeInfo.GetSightType

46 function TfrmGetInfoTemplate.GetGrid: string;


47 begin
48 Result := edtGrid.Text;
49 end; // end function TfrmGetTreeInfo.GetGrid

50 function TfrmGetInfoTemplate.GetNumber: integer;


51 begin

Solutions Chapter 4, Page 16 Object orientation with Delphi (all rights reserved)
52 Result := sedNumber.Value;
53 end; // end function TfrmGetTreeInfo.GetNumber

54 procedure TfrmGetInfoTemplate.btnCaptureClick(Sender: TObject);


55 begin
56 inherited;
57 FInfoValid := True;
58 Close;
59 end; // end procedure TfrmGetTreeInfo.btnCaptureClick

60 procedure TfrmGetInfoTemplate.btnCloseClick(Sender: TObject);


61 begin
62 FInfoValid := (MessageDlg ('Capture input?', mtConfirmation,
63 [mbYes, mbNo], 0) = mrYes);
64 inherited;
65 end; // end procedure TfrmGetTreeInfo.btnCloseClick

66 end. // end unit GetInfoTemplateU

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}

27 function TfrmGetBirdInfo.ElicitInfo: boolean;


28 begin
29 // Initialise
30 edtGrid.Text := '';
31 sedNumber.Value := 0;
32 chkBreeding.Checked := False;
33 rgpType.ItemIndex := -1;

34 ShowModal; // Elicit Info


35 Result := FInfoValid;
36 end; // end function TfrmGetBirdInfo.ElicitInfo

37 function TfrmGetBirdInfo.GetBreeding: boolean;


38 begin
39 Result := chkBreeding.Checked;
40 end; // end TfrmGetBirdInfo.GetBreeding

41 function TfrmGetBirdInfo.GetSightDate: string;


42 begin
43 Result := DateToStr (dtpDate.DateTime);
44 end; // end function TfrmGetBirdInfo.GetSightDate

45 procedure TfrmGetBirdInfo.rgpTypeClick(Sender: TObject);


46 begin
47 gpbGrid.Enabled := true;
48 gpbNumber.Enabled := true;
49 end; // end procedure TfrmGetBirdInfo.rgpTypeClick

50 function TfrmGetBirdInfo.GetSightType: string;


51 begin
52 Result := rgpType.Items[rgpType.ItemIndex];
53 end; // end function TfrmGetBirdInfo.GetSightType

54 end. // end GetBirdInfoU

Now the form for collecting the tree information:

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 }

20 function TfrmGetTreeInfo.GetSightType: string;


21 begin
22 Result := 'Protea Curvata';
23 end; // end function TfrmGetTreeInfo.GetSightType

24 end. // end unit GetTreeInfoU

Finally the form for showing the selected information:

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}

22 procedure TfrmShowInfo.ShowInfo(ASighting: TSighting);


23 begin
24 // first display substitutable properties
25 lblGrid.Caption := 'Grid: ' + ASighting.Grid;
26 lblNumber.Caption := 'Number: ' + IntToStr(ASighting.Number);
27 lblSightType.Caption := 'Type: ' + ASighting.SightType;

28 // now display child-specific properties


29 if ASighting is TBirdSighting then

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

45 end. // end ShowInfoU

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:

User interface class structure

And finally, the sequence diagram:

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)

S-ar putea să vă placă și