Professional Documents
Culture Documents
Main concepts
Substitution The Sender parameter Typecasting Class and instance operators RTTI: run time type information
Chapter contents
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Example 5.1 The Sender parameter in event handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Ex 5.1 step 1 The OnClick event handler, version 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Ex 5.1 step 2 Using the Sender parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Ex 5.1 step 3 Linking in the other SpeedButtons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Ex 5.1 step 4 Testing the Sender parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Ex 5.1 step 5 Working through a superclass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Ex 5.1 step 6 An alternative typecasting operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 The Sender parameter and substitution (26 Jun 2006) Chapter 5, Page 1
Ex 5.1 step 7 The equality operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 A diagrammatic view of substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Example 5.2 Run time type information (RTTI) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Some final comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Chapter summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Problem 5.1 Study Chapter 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Problem 5.2 RTTI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Problem 5.3 Centralised control of several traffic lights . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Introduction
An important consequence of an inheritances hierarchy is the concept of substitution, where an instance of a subclass object can substitute for a variable declared as any of its supertypes. In this brief chapter we introduce the concept of substitution by investigating the Sender parameter that is part of the parameter list of every event handler. Different events have different parameter lists but the first in the list for all events is Sender: TObject, which identifies the component that initiated the event. Identifying the event initiator is a useful idea, but not completely straightforward. The difficulty is that every parameter must have a type, and so Sender must be declared as a specific type. But what type should Sender be? Every component is different. As well see in the example that follows, Delphi uses the inheritance hierarchy to solve this problem.
Chapter 5, Page 2
28 procedure TfrmSender.spdOneClick(Sender: TObject); 29 begin 30 pnlReport.Caption := 'You clicked on ' + Sender.Name; 31 end; // procedure TfrmSender.spdOneClick
If you try this youll see that it does not work. The compiler returns an Undeclared identifier error. Why is this? In the parameter list in the header of this OnClick event handler, and all other event handlers, Senders type is defined as TObject, the root class of the hierarchy, and not as TSpeedButton (line 28). The TObject class does not have a Name property, and so, when the compiler reaches Sender.Name in line 30, it generates an error. Because Sender is a completely general parameter it is declared as type TObject. However, in this case we know that Sender is a TSpeedButton and so we must instruct Delphi to treat Sender as a TSpeedButton in other words we must typecast Sender as a TSpeedButton. There are different ways to do this. Here we use the as class operator as follows (line 31):
28 procedure TfrmSender.spdOneClick(Sender: TObject); 29 begin 30 pnlReport.Caption := 'You clicked on ' + 31 (Sender as TSpeedButton).Name; 32 end; // procedure TfrmSender.spdOneClick
This program compiles and works like the previous version, so we now seem to be using the Sender parameter correctly. We have seen the as typecasting operator in passing in example 2.1 step 2 line 17, and here we will look at it more closely. This typecasting informs Delphi that although Sender is declared in the parameter list as a TObject because it could be any one of a number of different classes, in this particular case Delphi must treat it as a TSpeedButton (line 31 above).
Chapter 5, Page 4
box alongside OnClick (figure 3). This routes each SpeedButtons event to the single General Click event handler. Save this version of the program and run it. Check that the text in the panel gives the correct event handler name for each of the SpeedButtons.
Figure 3 Linking the different components to the same event handler
else if Sender is TForm then pnlReport.Caption := 'You clicked on ' + (Sender as TForm).Name
35 else if Sender is TPanel then 36 pnlReport.Caption := 'You clicked on ' +(Sender as TPanel).Name; 37 end; // end TfrmSender.GeneralClick
Using the events tab of the Object Inspector, link the OnClick events for pnlControl, pnlReport, lblInheritance and frmSender to the GeneralClick event handler. (We use a condition test even in the last else clause to ensure that it will respond only to TPanels. The Sender parameter and substitution (26 Jun 2006) Chapter 5, Page 5
Anything except a TSpeedButton, a TForm or a TPanel is ignored.) Run and test this version of the program and check that it responds correctly to clicks on the Panels and the Form as well as on the SpeedButtons. It does not respond to a click on lblInheritance even though we have linked in GeneralClick as its event handler since GeneralClick does not have an If condition to test for a TLabel.
Save and run this version of the program. It gives the same messages as step 4 whether we click on a SpeedButton, a panel or the form, and now also responds to a click on lblInheritance. Here we gain a further benefit from inheritance. Because TSpeedButton, TPanel, TLabel and TForm are all descendants of TComponent, we can test for TComponent (line 30) and cast as TComponent (line 32) instead of testing and casting for each one separately as in step 4. Where we are using a property (or method) that subclasses inherit from a superclass, we can work with the superclass instead of each subclass individually. This saves programming since we dont have to cater separately for each subclass class and so we can avoid situations like step 4 where we have a separate If statement for each subclass. It also means that if in future we introduce a further subclass we dont have to search through the entire program to cater specifically for it. Chapter 5, Page 6 Object orientation with Delphi (all rights reserved)
What we see in this step is important. Sender, of type TObject, is cast as a descendant, TComponent, which in turn is an ancestor of several other objects used here (TSpeedButton, TPanel and TForm). Since these objects all inherit the Name property from their ancestor TComponent, we cast Sender as their ancestor TComponent. This allows us to work with them as a group rather than with each one individually. Although we are working with a TComponent in lines 30 & 32, any of its descendants (here TSpeedButton, TPanel or TForm) can substitute for it, behaving as though it is a TComponent. This is an example of substitution.
The bracket notation looks a bit like a subroutine call and does not perform any error checking. So, although it is slightly less efficient, many programmers prefer the as operator for typecasting since it is more robust and easier to read.
Chapter 5, Page 7
29 begin 30 if Sender is TComponent then 31 pnlReport.Caption := 'You clicked on ' + 32 (Sender as TComponent).Name; 33 if Sender = spdFive then 34 Color := clAqua 35 else 36 Color := clGreen; 37 end; // end TfrmSender.GeneralClick
Example 5.1 Summary: The crucial point of this example is to introduce the concept of substitution. The Sender parameter, which is part of every event, tells the event handler which component initiated the event. Many different components can initiate events and so the Sender parameter is of type TObject, the most general type of object that there is in Delphi. The is and the = operators allow us to test whether Sender represents either a particular class or a particular object. To use Sender in a program we need to typecast it appropriately and we can use either as (with error checking) or () (without checking) for the typecasting. In this example we saw that a property that is available to a TComponent object is also available to TSpeedButton, TForm and TPanel objects because all three descend from TComponent. Because of the inheritance hierarchy, a subtype inherits all the functionality of the supertype as a starting point, and so a subtype can take on the role of any of its ancestors. However an ancestor, which is typically more restricted than the subtype, cannot take on all the roles of the subtype and trying to substitute an ancestor for a subtype will lead to a compile error.
Chapter 5, Page 8
The class diagram shows in Class B only the additional data fields declared in Class B, with the generalisation arrow from Class B to Class A indicating that Class B also has the data fields declared in Class A. However, an instance of Class B must have memory allocated for both Data Fields A and Data Fields B. (And similarly for the other classes.) Say we have an instance of Class A, two instances of Class B, an instance of Class C and an instance of Class D. We can represent this as follows:
When an instance of Class B, C or D substitutes for an instance of Class A, the additional subclass structure (Data Fields B, C and/or D) are ignored. This is possible because of the overlap in structure between a superclass and all its descendants. However, Class B cannot substitute for Class C or the other way round because of the differences in their structures. An instance of Class D can substitute for instances of either Class B or Class A (but not for Class C). We have referred only to the objects data fields so far. A similar but more sophisticated concept applies to methods and, where we have run-time binding, makes polymorphism possible. Polymorphism is a intriguing concept, and we devote the next chapter to it.
Chapter 5, Page 9
Chapter 5, Page 10
process of identifying the commonality that exists between a set of classes and then moving this commonality into a superclass. (We may create a superclass specially for the generalisation.) Since the common functionality from a group of subclasses has moved into a superclass, it makes sense that any of the subclasses can substitute for the superclass (though not the other way round). This possibility of substitution is an important reason for introducing generalisation into the applications class structure wherever possible. In the VCL components, the Delphi developers have already designed the class hierarchy and produced several levels of generalisation. This is why we could use the substitution as we did in this chapter, with a SpeedButton, panel, label or form substituting for a TComponent. When we begin designing our own classes it will be well worth while to keep the concepts of generalisation and substitution in mind. This is a brief chapter that simply illustrates the concept of substitution to prepare the way for polymorphism. In the next chapter we explore generalisation and substitution further, this time with programmer-written classes, and develop the concept to include polymorphism. Polymorphism is fundamental to several of the patterns well investigate a little later and is one of the main reasons for the growth and development of object-oriented programming.
Chapter summary
Main points: 1. Substitution. 2. The Sender parameter. 3. Typecasting. 4. Class and instance operators. 5. RTTI: run time type information. Objects as derived entities: Substitution.
Chapter 5, Page 11
Problems
Problem 5.1 Study Chapter 5
Identify the appropriate example(s) or section(s) of the chapter to illustrate each comment made in the summary above.
The event handlers for all the objects on the user interface are routed through the following event handler: Chapter 5, Page 12 Object orientation with Delphi (all rights reserved)
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
procedure TfrmSender.GeneralClick(Sender: TObject); var Class1, Class2: TClass; ClassStr: string; begin Class1 := Sender.ClassType; Class2 := Class1.ClassParent; while Class2 <> nil do begin ClassStr := Class1.ClassName + ' derived from ' + Class2.ClassName; lstClasses.Items.Add(ClassStr); Class1 := Class1.ClassParent; Class2 := Class1.ClassParent; end; lstClasses.Items.Add(''); end; // procedure TfrmSender.GeneralClick
a) Explain how this event handler works and what it does, referring to the principles covered in chapter 5. b) Draw the UML class diagram for all the classes in this program on the basis of this programs operation.
Chapter 5, Page 13
This control panel provides independent control of three different traffic lights. Each traffic light can be set to automatic or manual via a CheckBox. When the CheckBox is checked the associated traffic light runs automatically (4 cycles red, 3 cycles green, 1 cycle yellow). When the CheckBox is unchecked, the Step button is enabled. Each click on the Step button moves the associated traffic light, independently of the other two, to the next colour in the sequence. The third control for each light, a SpinEdit, gives the cycle length in tenths of a second. So if a SpinEdit is set to 8, the light it controls spend 3.2 seconds on red, 2.4s on green and 0.8s on yellow. Any change of value in a SpinEdit takes effect from the next colour change. Code this program, incorporating the OO principles covered so far in this module as appropriate. Include simple UML documentation. There are different ways to tackle this problem. Less experienced programmers might find the following steps useful: 1. Write a simple low-level description / algorithm for each of the following cases: a. Enable a traffic light for automatic operation by checking a CheckBox. b. Disable automatic stepping and step manually through the sequence by clicking on a button. c. Check a CheckBox for automatic operation and then change the cycle time. 2. From these descriptions, decide what classes are needed and how they should be related. 3. Based on the classes in the class diagram, draw a sequence diagram for each of the cases above. 4. Implement each case in turn. Its probably easier not to solve the whole problem in one go. Instead, you may decide to start with just the controller and a single traffic light and to concentrate on switching that lights automatic operation on and off correctly. You would then have a choice. You could
Chapter 5, Page 14
get three lights controlled independently. Then you could get the manual control working for one light and extend it to three, and finally program the adjustable cycle for one and then extend it to all three. Alternatively, you could stick with a single light and get the manual stepping to work, followed by the adjustable cycle, and then finally expand that to the independent control of first two lights and then three lights. Whichever approach you take will involve some iteration and each step forward is likely to involve changes to some of the code already written, and possibly some changes to the designs under points 1, 2 or 3 above. 5. Once the program is written, tested and working, use Visio to draw a UML class diagram showing the master control panel, the traffic light display(s), the traffic light class(es) and any other significant classes and the relationships between these classes, and sequence diagrams showing the operation for each of the cases. 6. Prepare a brief description ( to 1 types page) of how the program works and identify the OO principles you have included.
Chapter 5, Page 15