Delphi OOchap 01
Delphi OOchap 01
Introduction to OO basics
Main concepts
Introducing: Object orientation basics Unit structure and project structure Delphis RAD generation: visual objects and events Event handlers Creating an object (RAD and coding) Defining a class Declaring an object reference Instantiating an object Freeing an object Interaction between objects: simple message passing, association and composition The UML: class, object and sequence diagrams Inheritance, association and composition relationships
Chapter contents
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Object orientations basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Representing OO systems: the UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Introduction to OO basics (31 May 2006, all rights reserved) Chapter 1, Page 1
Patterns: applying OO principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Learning OO programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Delphi as a learning environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Example 1.1 A unit file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 The unit name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 The interface section . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 The implementation section . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Example 1.2 Extending an empty application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Ex 1.2 step 1 A RAD GUI builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Ex 1.2 step 2 Events and their handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Ex 1.2 step 3 Analysing the RAD-generated code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Ex 1.2 step 4 A graphical representation: the UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Example 1.3 Object interaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Ex 1.3 step 1 A main form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Ex 1.3 step 2 An auxiliary form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Ex 1.3 step 3 The project file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Ex 1.3 step 4 A matching UML class diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Ex 1.3 step 5 A matching UML object diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Ex 1.3 step 6 A matching UML sequence diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Ex 1.3 step 7 Explicit Create and Free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Ex 1.3 step 8 Lifetimes in a sequence diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Example 1.4 Defining a form directly in code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Ex 1.4 step 1 Modifying the previous program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Ex 1.4 step 2 Methods, and event handler linking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Ex 1.4 step 3 Matching UML diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Introduction
Object orientations basics
Object Orientation (OO) can be seen from three different aspects: objects as independent entities, objects as derived entities, and objects as interacting entities.
Chapter 1, Page 2
Objects as independent entities An object is a separate, self-contained, encapsulated entity, and the term encapsulation is important in OO. This means that an object has data that it can keep private from the outside world and that it has specific behaviour. It manipulates its data and implements its behaviour through its methods. Writing the code that defines an objects methods has many similarities with structured programming, and so uses many structured programming principles such as structured selection (If..then..else, Case) and repetition (While..do and For loops). In this module we assume that students are familiar with these concepts and so concentrate on principles that apply specifically to OO programming. Objects as derived entities Reuse is a very important consideration in OO programming. If some required operations or data storage already exist in one class, it is possible for another class to take advantage of these operations and data and so avoid recoding them. OO makes this second aspect, a classs derivation from other classes, possible through subclassing (using inheritance) and through composition, and can significantly reduce the amount of code one needs to write. Objects as interacting entities The third aspect, the interaction between objects, depends on association and subtyping. If objects could not interact, they could not cooperate with each other to perform the various tasks required in a computer program. So the programmer creates associations (links) between objects to provide pathways for objects to send messages to one another. To allow these interactions to lead to different results under different circumstances, the programmer can use subtyping and polymorphism. Subtyping is when objects within the same hierarchy implement their common interface differently and is an important focus later in this module. There is considerable interleaving between these aspects. Object derivation and object interaction both use inheritance, which is directly supported in the programming language, and forms of object linking (association and composition), which are not directly supported by language constructs.
Chapter 1, Page 3
Learning OO programming
Programming is difficult and OO programming is even more so! A common approach is to provide learners with a set of preconstructed classes (eg a graphics toolbox) as a starting point for OO programming. This module adopts a variation on this approach by using Delphi, a RAD GUI (Rapid Application Development, Graphical User Interface) generator with an extensive library of GUI components. This library allows learners to construct their own visual objects to use and manipulate in their programs. This standard library of user interface objects and the visual (GUI) code generation environment provides a natural vehicle for demonstrating underlying principles and for concretising OO concepts and a good entry point into the complexities of OO programming. It helps to clarify issues like the anatomy of a class, the combination of existing classes to create a new class, and reuse through inheritance by taking advantage of features such as the Visual Component Library (VCL) and Visual Form Inheritance (VFI)). The first chapter uses Delphis GUI generator to start looking at the internal structure of an object. The second chapter looks at RAD-generated inheritance and at the hierarchy of standard user interface components. The third chapter helps the novice OO programmer to write programmer generated classes and the fourth chapter looks at ways of accessing an object and its data. Chapter two introduces inheritance for reuse through subclassing by presenting VFI and the VCL library supplied with Delphi. Inheritance also provides the possibility of subtyping
Chapter 1, Page 4
and the power of polymorphism and this is introduced in chapters four and five, using Delphis standard Sender parameter as a starting point. Chapter six draws together the material from the preceding chapters by stating several anti-patterns that highlight potential pitfalls in using inheritance. Along with polymorphism, the concept of cooperating objects is crucial to OO. Object cooperation in various forms is a theme running from chapter seven through to the end of the module. The concepts of polymorphism and cooperation are consolidated by the exploration, from chapter nine onwards, of a variety of OO patterns. The following table shows the patterns well cover in this module along with the OO programming concepts these patterns allow us to explore:
Pattern Immutable
Sender Argument
Adapter
Facade
Law of Demeter
Player-Role
Class definition, distinguishing between attributes and roles Delegation Packaging for future evolution Changing associations dynamically
Strategy
Chapter 1, Page 5
Template method
Packaging for variability (reducing coupling) Polymorphic substitution Implementation at different hierarchical levels
Association class
Object manufacture Separation of concerns Object manufacture Class and object data fields Polymorphic recursion
While contrasting subclassing and subtyping we also consider several inheritance antipatterns. The fundamental principles of patterns are presented textually and graphically (through words and UML class diagrams, object diagrams and sequence diagrams) and the implementation through detailed worked examples, allowing the student to consolidate the concepts by considering two different perspectives. Using these diagrams brings familiarity in reading class, object and sequence diagrams. This helps develop representational literacy and indicates some of their strengths and limitations.
Chapter 1, Page 6
up a Form, which is the name Delphi uses for a window in a graphical interface environment.
Delphi programs can contain many unit files. The basic structure of a unit file comprises three sections, the unit name (as in line 1 above), the interface section (lines 214) and the implementation section (lines 1517).
Delphi 8 offers a choice of VCL Forms (which gives backward compatibility with earlier versions), W indows Forms and ASP .NET. Since the OO principles we are working with are effectively independent of which of these you use, we use VCL Forms to make it easier to move between the different versions.
Chapter 1, Page 7
The words type and a class are closely related, and well go into the differences between these two terms later on in this module.
Chapter 1, Page 8
unadorned form and dont extend it in any way. In a little while, when we add some Components and event handlers, we will see how Delphi uses its RAD facility to add these objects and methods to the type declaration of TForm1. The var (variable) declaration (lines 13 & 14): The previous subsection, the type declaration, provides the additional classes that we define in our program. However, a type is a template only and not an object. In this section, the var declaration, we declare what objects we will create from the available classes. This sets aside memory on the stack for references to these objects. The only object so far is the form that Delphi creates automatically when we start a new project. It is called Form1 , and is of type TForm1 (line 14). To help us keep the distinction between the type and the object, Delphi has the convention of starting type names with a T. Thus, TForm1 is the type and Form1 is the object.
Chapter 1, Page 9
Delphi automatically creates a skeleton of the new TForm1 class we are creating. It uses the standard TForm class as the basis of the new TForm1 class by deriving TForm1 from TForm.
Here we create a simple Delphi program and then look at the RAD generated code in the units listing. Starting with a new application, add two buttons by selecting them from the Component / Tool palette and dropping them onto the form (figure 1). To change the units name, save it as TwoButtonsU.pas (using the File | Save As menu sequence). Now initialise some properties of the GUI objects in the Object Inspector and notice how they change in the Form Designer. One way to present the properties is by means of a table:
Component Form
Button1
Name Caption
Button2
Name Caption
Chapter 1, Page 10
Delphi stores the initial properties in a .dfm file. Representing this properties table in the dfm format gives the following:
object frmStructureDemo: TfrmStructureDemo Caption = ' Unit Structure' object btnYellow: TButton Caption = '&Yellow' object btnBlue: TButton Caption = '&Blue'
Delphi 6 and 7 show the structure of the user interface object in the Object TreeView (figure 2). (The Model View in Delphi 8 / 2005 gives similar functionality.) This gives the containment hierarchy, not the class inheritance hierarchy, and shows that the form object frmStructureDemo contains two buttons, btnBlue and btnYellow. (As we will explore later, Delphi implements this containment through a strong form of aggregation called composition.)
Figure 2 The Object TreeView showing the form composed from the Buttons
| Run). Clicking with the mouse on button Blue makes the form go blue (line 27) while button Yellow makes the form yellow (line 23).
1 unit TwoButtonsU; 2 interface 3 uses 4 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, 5 Dialogs, StdCtrls; 6 type 7 TfrmStructureDemo = class(TForm) 8 btnYellow: TButton; 9 btnPurple: TButton; 10 procedure btnYellowClick(Sender: TObject); 11 procedure btnBlueClick(Sender: TObject); 12 private 13 { Private declarations } 14 public 15 { Public declarations } 16 end; 17 var 18 frmStructureDemo: TfrmStructureDemo; 19 implementation 20 {$R *.DFM} 21 procedure TfrmStructureDemo.btnYellowClick(Sender: TObject); 22 begin 23 frmStructureDemo.Color := clYellow; 24 end; 25 procedure TfrmStructureDemo.btnBlueClick(Sender: TObject); 26 begin 27 frmStructureDemo.Color := clBlue; 28 end; 29 end.
Chapter 1, Page 12
Changing the units name When we saved the unit as TwoButtonsU, Delphi changed the units name from Unit1 to TwoButtonsU (line 1 above). To keep the project tracking synchronised, always change a units name through Save As and dont change the name directly in the program code. Changing the forms name In the Object Inspector we changed the Forms name to frmStructureDemo. On this basis, Delphi changed the name of the new form type to TfrmStructureDemo. (It is still derived from TForm, line 7). Delphi then declared the form object as frmStructureDemo (without the T prefix) of type TfrmStructureDemo (with the T prefix) (line 18). (The variable declaration in line 18 means that we can use the name frmStructureDemo to refer to an object of type TfrmStructureDemo.) Adding buttons We added two buttons to the form and called them btnYellow and btnBlue. On this basis, Delphi automatically inserted lines 8 & 9. These declare that the TfrmStructureDemo class is composed of objects called btnYellow and btnBlue, both of type TButton (Delphis standard class for buttons). These objects are now part of the type TfrmStructureDemo as reflected in the Object TreeView (figure 2). In composing a class, in this case TfrmStructureDemo, from other objects, in this case btnYellow and btnBlue of type TButton, we automatically gain all of the TButton classs capability when we include it in our form. Like TForm, TButton is a standard Delphi class, and composition gives a powerful way of re-using existing components and classes. We will return in more detail to this concept of composition when we use it as a tool for creating our own classes. Adding event-handlers We added an OnClick event-handling method3 for each button by double-clicking on each in the Form Designer and adding the necessary code. Delphi automatically extends the class definition. It incorporates these event handlers as methods of TfrmStructureDemo by declaring these procedures in the type structure (lines 10 & 11 above). These declarations do not define what the procedures do, so Delphi also creates the skeletons for these event handlers in the implementation section (lines 21 to 28), leaving us to fill in the appropriate programming statements (lines 23 & 27). In Delphi Pascal, although methods can be either procedures or functions, event handlers are always procedures. Along with all the methods
Chapter 1, Page 13
of the TForm and TButton classes, these event handlers are now also methods belonging to the TfrmStructureDemo class. A method declaration, as in line 10, is sometimes referred to as a method signature since it gives the method name (btnYellowClick), the type of subroutine (a procedure) and the parameter list (Sender: TObject). Once we define and use our own classes and objects we will start by mimicking Delphis RAD code.
A UML class diagram can also show relationships between classes such as inheritance and association. First well look at inheritance or generalisation as it is called in UML. This is shown with an open arrow head pointing from the subclass to the superclass (figure 4, cf line 7 above). Classes can become quite complex and so one generally shows only those attributes and methods that are of interest in a particular diagram. In figure 4, we show TForm without any of its attributes or methods. Because the subclass is everything that the superclass is and more, inheritance is often called an IsA relationship.
Chapter 1, Page 14
In addition to the inheritance relationship, we can also use the UML to show the various forms of association. A strict form of association is composition. We mentioned earlier that the relationship between the form and the two buttons is composition. So, instead of the two buttons as attributes of the form as in figure 4, we can show the TButton class separately and then show that TfrmStructureDemo uses (ie is composed of) two TButtons as in figure 5. The UML representation for composition is a solid diamond at the composed class connected by a line to the class or classes that the composed class uses. Although TfrmStructureDemo is composed of two TButtons: we show the TButton class only once but include a 1:2 multiplicity on the composed link. Because we show the TButtons separately, we no longer include them within the box representing TfrmStructureDemos attributes.
Ex 1.2 Summary: As a basis for creating our own classes in the future we have investigated how Delphi adds data fields (the buttons) and methods (the event handlers) to a class definition. The UML provides class diagrams to represent the structure of classes and the inheritance and composition between them.
Chapter 1, Page 15
Chapter 1, Page 16
3 uses 4 Windows, Messages, SysUtils, Variants, Classes, Graphics, 5 Controls, Forms, Dialogs, StdCtrls; 6 type 7 TfrmMain = class(TForm) 8 radAuxShow: TRadioButton; 9 radAuxHide: TRadioButton; 10 procedure radAuxShowClick(Sender: TObject); 11 procedure radAuxHideClick(Sender: TObject); 12 private 13 { Private declarations } 14 public 15 { Public declarations } 16 end; 17 var 18 frmMain: TfrmMain; 19 implementation 20 uses AuxForm; 21 {$R *.dfm} 22 procedure TfrmMain.radAuxShowClick(Sender: TObject); 23 begin 24 frmAuxiliary.Show; 25 end; // end procedure TfrmMain.radAuxShowClick 26 procedure TfrmMain.radAuxHideClick(Sender: TObject); 27 begin 28 frmAuxiliary.Hide; 29 end; // end procedure TfrmMain.radAuxHideClick 30 end.
The message calls appear in lines 24 and 28. They consist of the target object followed by a dot followed by a message name. (We define frmAuxiliary in the next step of this example.) Message calls look like subroutine calls (lines 24 & 28 above). The difference between the two is that message calls allow polymorphism. Polymorphism is an important aspect of OO programming that allows different objects to respond differently to the same message. This, for example, allows OO programs to respond appropriately to varying conditions without the need for hard-coding complex sets of conditional statements. Delphi automatically makes provision for public and private fields in the form (lines 12 to 15). In this example there are no additional public or private fields, and so these statements do not perform any function. They remind programmers that it is possible to add private and public declarations to a RAD form, and we will do so from time to time. However quite often we will not have any private or public declarations to add, and then we will delete these lines from our code listing to avoid cluttering up the examples. Introduction to OO basics (31 May 2006, all rights reserved) Chapter 1, Page 17
Figure 9 Components comprising the auxiliary form 1 unit AuxForm; 2 interface 3 uses 4 Windows, Messages, SysUtils, Variants, Classes, Graphics, 5 Controls, Forms, Dialogs, StdCtrls; 6 type 7 TfrmAuxiliary = class(TForm) 8 procedure FormShow(Sender: TObject); 9 end; 10 var 11 frmAuxiliary: TfrmAuxiliary; 12 implementation
Chapter 1, Page 18
13 {$R *.dfm} 14 procedure TfrmAuxiliary.FormShow(Sender: TObject); 15 begin 16 Left := Random (600); 17 Top := Random (400); 18 end; // end procedure TfrmAuxiliary.FormShow 19 initialization 20 Randomize; 21 end.
(The screen positions and references in lines 16 & 17 refer to a low resolution screen. If you are using a higher resolution display you may want to increase these values proportionately.)
Lines 910 cause the auto-creation of the two form objects in this project.
TForm, and so we show them in the middle compartment of TForm since they are attributes. (TForm does not actually define these attributes itself but inherits them in turn from TControl, one of its superclasses. But this detail is not significant here.)
Figure 10 Class diagram for ex 1.3 steps 1 & 2
Because frmMain interacts with frmAuxiliary, we show an association from frmMain to frmAux. The arrows point from frmMain to the other form because frmMain knows about and uses this form (lines 20, 24 & 28 in Unit MainForm). However, frmAuxiliary has no knowledge of frmMain so there is no arrowhead frmAuxiliary to frmMain. Because this is Chapter 1, Page 20 Object orientation with Delphi (all rights reserved)
an association and not an inheritance relationship we use an open and not a closed arrow head. Association, like inheritance, is an important concept in object orientation, and both crop up throughout this module. Although objects are created from classes, object diagrams mostly look very different from their related class diagram. Figure 11, for example, shows only the actual objects that exist in the system and so does not show that these objects are derived from any superclasses: for that information we refer to the class diagram.
Chapter 1, Page 21
Message passing is a basic communication and synchronisation mechanism within OO. A message is an invocation of an objects method. A message is shown as an arrow from one objects activation to another. There is an association relationship between these two objects: frmMain and frmAux are both independent objects with separate lifetimes since neither Creates nor Frees the other. However, as we saw in the object diagram, frmMain uses frmAux, and so association is sometimes referred to as a UsesA relationship.
Chapter 1, Page 22
creating a reference on the stack. The object constructor uses the class definition to create an object in memory (the heap). In the present version of the program Delphi takes responsibility for all these steps. Quite soon well define classes and declare and create objects in the programs we write. As a first step towards this, well intervene in the auto-create process for RAD objects and create the auxiliary form as part of a program instead of allowing Delphi to create it automatically. We can remove any of the forms in a project except the main form from the auto-create list. If we do this, Delphi will no longer auto-create those forms and the programmer must create them explicitly in program code before they can be used. (We can also choose which form will be the main form.) To avoid overwriting the current version of this code, save these two units and the project file to a new directory for this example before doing anything else. Now use the Project | Options menu sequence and select the Forms options tab. The left pane lists the auto-create forms. Select frmAuxiliary in this pane and click the single right arrow to transfer it from the auto-create list to the available forms list (figure 13). Select OK to close the Project Options window.
Chapter 1, Page 23
To program our previous programs user interface for creating (and freeing) the auxiliary form, add a GroupBox to the form and then add Create and Free buttons to the GroupBox (figure 14 and figure 15).
Figure 14 Creating and freeing a user interface object explicitly
Chapter 1, Page 24
21 uses AuxForm; 22 {$R *.dfm} 23 procedure TfrmMain.radAuxShowClick(Sender: TObject); 24 begin 25 try 26 frmAuxiliary.Show; 27 except 28 ShowMessage ('Auxiliary Form does not exist'); 29 radAuxShow.Checked := False; 30 end; 31 end; // end procedure TfrmMain.radAuxShowClick 32 procedure TfrmMain.radAuxHideClick(Sender: TObject); 33 begin 34 try 35 frmAuxiliary.Hide; 36 except 37 ShowMessage ('Auxiliary Form does not exist'); 38 radAuxHide.Checked := False; 39 end; 40 end; // end procedure TfrmMain.radAuxHideClick 41 procedure TfrmMain.btnCreateClick(Sender: TObject); 42 begin 43 if frmAuxiliary = nil then 44 frmAuxiliary := TfrmAuxiliary.Create(Self) 45 else 46 ShowMessage ('Auxiliary Form already exists'); 47 end; // end procedure TfrmMain.btnCreateClick 48 procedure TfrmMain.btnFreeClick(Sender: TObject); 49 begin 50 try 51 frmAuxiliary.Hide; 52 frmAuxiliary.Free; 53 frmAuxiliary := nil; 54 except 55 ShowMessage ('Auxiliary Form does not exist'); 56 end; 57 end; // end procedure TfrmMain.btnFreeClick 58 end.
The changes required may be more extensive than expected. Since at any particular moment frmAuxiliary may or may not exist, at each stage we need to test for frmAuxiliarys existence before trying to carry out any operations on it. (In the previous auto-create version we know that frmAuxiliary always exists and so we do not need these tests.) We have added an OnClick event handler for each of the two new buttons. Of the four event handlers we now have, three use exception handling to handle situations where we might try to perform an operation on a non-existent object.
Chapter 1, Page 25
If we try to show a non-existent frmAuxiliary, line 26 throws an exception that is then caught by lines 28 and 29. Similarly, if we try to hide a non-existent form, line 35 throws an exception that is caught by lines 37 and 38. If we try to free a non-existent form (line 51) line 55 takes care of the required exception handling. Exception handling will not catch an attempt to create another frmAuxiliary should one already exist, so we use a different approach when instantiating frmAuxiliary. Line 43 tests whether the name frmAuxiliary refers to a valid object. If it doesnt, line 44 creates it. If it does, line 46 issues a message to the user and there is no attempt to instantiate a second TfrmAuxiliary object. Well use this example to look a little more closely at an objects life cycle and at the way it can be referenced by other objects. Line 21 is a local uses clause stating that this unit has access to the public sections of unit AuxForm. Unit Auxform declares a public variable (ie an object reference) frmAuxiliary of type TfrmAuxiliary (step 2, lines 10 & 11). Thus reference frmAuxiliary is visible to unit MainForm. If an object reference has the value nil it is not currently referring to any object: any other value is a reference to an object. When the user clicks btnCreate, its OnClick event handler runs. This first checks whether reference frmAuxiliary refers to a valid object (line 43 above). If not, it moves to line 44:
frmAuxiliary := TfrmAuxiliary.Create(Self)
This invokes TfrmAuxiliarys Create method, sending it the value Self. TfrmAuxiliary is declared publicly in unit AuxForm, and unit MainForm has access to AuxForm through its local uses clause (line 21 above). TfrmAuxiliary is derived from TForm (step 2 line 7). TForm has a method called Create, and so TfrmAuxiliary inherits Create from TForm: an example of how inheritance facilitates reuse. Create is a special kind of method called a Constructor. A Constructor creates an object from the template provided in a class definition. For VCL classes, this constructor requires an owner of the object as an input parameter. Here we give Self (ie frmMain) as the owner. The constructor returns a reference to the newly created object. In line 44 this return value is assigned to frmAuxiliary. Returning to the If statement in line 43, if frmAuxiliary refers to a valid object, line 46 issues a message stating that the object already exists and no attempt is made to create another instance of this object. Now that it exists, frmAuxiliary can be shown and hidden as often as required by the two RadioButton event handlers. When the time comes to destroy frmAuxiliary, line 51 first Hides it. If frmAuxiliary exists, this statement will be successful and line 52 will then invoke frmAuxiliarys Free method. Free is a Destructor type method that TfrmAuxiliary has inherited from TForm. After freeing frmAuxiliary, it no longer exists and so we must then set its reference to nil (line 53). After this, btnFrees OnClick event handler terminates. The other possibility is that Chapter 1, Page 26 Object orientation with Delphi (all rights reserved)
frmAuxiliary does not exist when line 51 executes. The attempt to refer to it then raises an exception and execution continues in the Except section of the Try Except End construct (ie at line 55). This issues a suitable message without any attempt to free the (nonexistent) object. Run and test this program. In the Delphi IDE a raised exception can optionally cause a break into the debugger at the point the exception is raised. To continue when this happens, press <F9> to resume running or <F7> or <F8> to single-step or step-over respectively. To enable or disable the break into the debugger on an exception, use Tools | Debugger Options in Delphi 6/7 or Tools | Options in Delphi 8. Instead of using exception handling, we could have coded the object freeing sequence in an If statement as follows:
procedure TfrmMain.btnFreeClick(Sender: TObject); begin if frmAuxiliary = nil then ShowMessage ('Auxiliary Form does not exist') else begin frmAuxiliary.Hide; frmAuxiliary.Free; frmAuxiliary := nil; end; end; // end procedure TfrmMain.btnFreeClick
In this variation we make sure frmAuxiliary actually does exist before we attempt an operation on it (ie to hide it). It is often possible to use either exception handling or If statements to prevent attempts at invalid object operations, and each approach has its advantages and disadvantages. In this module we generally use the exception handling approach. This gives a brief introduction to object Create and Free processes. We will return to these processes in more detail in a subsequent chapter.
Chapter 1, Page 27
Ex 1.3 Summary: There are a number of stages to creating an object. First we create a class definition. A class is not yet an object and does not have its own data. We cannot use it directly since it is only a template. So we declare one or more names to act as object references and then instantiate the objects from the class definition. Although each object is built to the same template, the class definition, an instantiated object is an independent entity with a life of its own. For example, each form has its own position and caption, and we can reposition and resize each one independently without affecting the others. In UML, a class diagram shows the static structure of classes in the program. An object diagram provides a snapshot of the instantiated objects at a particular moment in the program. A sequence diagram shows dynamic behaviour between various objects. We have investigated issues around an objects lifetime, creating it and freeing it as needed instead of creating it when the program starts and freeing it when the program ends. We assumed responsibility for freeing the object by setting the owner to nil in the Create method call. When freeing an object we must also set its reference to nil. We do this either by calling the Free method and then setting the reference to nil, or (as well see in a future example) by using the FreeAndNil procedure. When needed, we can show the start and end of an objects lifeline on a sequence diagram. A message to the objects Name indicates its construction. A cross over its lifeline shows its destruction. There are several different kinds of message and we distinguish these through different notations.
Figure 16 Showing object creation and destruction on a sequence diagram
Chapter 1, Page 28
Chapter 1, Page 29
11 12 13 14 15 16 17 18 19
btnCreate: TButton; btnFree: TButton; procedure radAuxShowClick(Sender: TObject); procedure radAuxHideClick(Sender: TObject); procedure btnCreateClick(Sender: TObject); procedure btnFreeClick(Sender: TObject); private frmAuxiliary: TForm; // Composition end; // end TfrmMain = class(TForm)
20 var 21 frmMain: TfrmMain; 22 implementation 23 {$R *.dfm} 24 procedure TfrmMain.radAuxShowClick(Sender: TObject); 25 begin 26 try 27 frmAuxiliary.Show; 28 except 29 ShowMessage ('Auxiliary Form does not exist'); 30 radAuxShow.Checked := False; 31 radAuxHide.Checked := False; 32 end; 33 end; // end procedure TfrmMain.radAuxShowClick 34 procedure TfrmMain.radAuxHideClick(Sender: TObject); 35 begin 36 try 37 frmAuxiliary.Hide; 38 except 39 ShowMessage ('Auxiliary Form does not exist'); 40 radAuxHide.Checked := False; 41 radAuxShow.Checked := False; 42 end; 43 end; // end procedure TfrmMain.radAuxHideClick 44 procedure TfrmMain.btnCreateClick(Sender: TObject); 45 begin 46 if frmAuxiliary = nil then 47 begin 48 frmAuxiliary := TForm.Create(Self); 49 frmAuxiliary.Caption := 'frmAuxiliary'; 50 frmAuxiliary.Height := 150; 51 frmAuxiliary.Width := 180; 52 frmAuxiliary.Left := Random (600); // not part of OnShow 53 frmAuxiliary.Top := Random (400); 54 end 55 else 56 ShowMessage ('Auxiliary Form already exists'); 57 end; // end procedure TfrmMain.btnCreateClick
Chapter 1, Page 30
58 procedure TfrmMain.btnFreeClick(Sender: TObject); 59 begin 60 try 61 frmAuxiliary.Hide; 62 frmAuxiliary.Free; 63 frmAuxiliary := nil; 64 except 65 ShowMessage ('Auxiliary Form does not exist'); 66 end; 67 end; // end procedure TfrmMain.btnFreeClick 68 initialization 69 Randomize; 70 end.
When you run this program it appears on the screen much as the previous examples. So what is the significance of the changes we have made? We declare frmAuxiliary (of class TForm) as a constituent of class TfrmMain by declaring it as a private data member (or data field) (line 18). We then create it (line 48), manipulate it (lines 4953) and, when we have finished with it, we free it (line 62). This private create-usefree relationship between one object and another is an example of composition. Its similar to association, but where association is a relationship between two independent objects, with composition one object creates, uses and destroys a private subsidiary object. Composition is an important concept in OO and well return to it several times in the chapters that follow.
Chapter 1, Page 31
In RAD development, the link between an event and its handler is controlled through the Object Inspector. We cant use that here so we link the OnClick event to the frmAuxShow event handler in program code (eg line 53). Just as Width is a property to which we can assign either a constant or variable name (eg line 24), OnShow is a property to which we can assign the name of a suitable method. (Just as we must use appropriate types when assigning variables and not, for example, assign a string to an integer, when assigning methods they must have a matching method signature. That is why we define frmAuxShow with a Sender parameter of type TObject even though it is not used in the method (line 19).) Finally we implement the body of the event handler as we did previously (ex 1.3 step 2, lines 1822). It will run when the auxiliary form is shown on the screen because of the assignment in line 25. In summary, we have a private method (line 19 below) that is used as an event handler, ie initiated by an event, and so it follows same format as Delphi-generated event handlers. Since an events name (eg OnShow) is a property, we can link the event handler to the event for the object we create (eg line 53) This achieves the same effect as linking an event handler through the Object Inspector. The additional methods we create for the subsidiary form need not just be event handlers. They can also be helper or access methods that we can call through program code. Well create a helper method FormRefFail to use whenever we attempt an invalid reference to frmAuxiliary. First, we declare the method privately (line 20) since no other unit should have access to it. It is a procedure since we are not returning a value. (We look at access methods in subsequent examples.) Run this program. From an external, black-box perspective it works as before.
1 unit MainForm; 2 interface 3 uses 4 Windows, Messages, SysUtils, Variants, Classes, Graphics, 5 Controls, Forms, Dialogs, StdCtrls; 6 type 7 TfrmMain = class(TForm) 8 radAuxShow: TRadioButton; 9 radAuxHide: TRadioButton; 10 gpbAuxiliary: TGroupBox; 11 btnCreate: TButton; 12 btnFree: TButton; 13 procedure radAuxShowClick(Sender: TObject); 14 procedure radAuxHideClick(Sender: TObject); 15 procedure btnCreateClick(Sender: TObject); 16 procedure btnFreeClick(Sender: TObject);
Chapter 1, Page 32
17 18 19 20 21
private frmAuxiliary: TForm; // Link: composition procedure frmAuxiliaryShow(Sender: TObject); // event handler procedure FormRefFail; // helper method end; // end TfrmMain = class(TForm)
22 var 23 frmMain: TfrmMain; 24 implementation 25 {$R *.dfm} 26 const // unit level constant 27 Mssgs: Array [0..1] of string = ('Auxiliary Form does not exist', 28 'Auxiliary Form already exists'); 29 procedure TfrmMain.radAuxShowClick(Sender: TObject); 30 begin 31 try 32 frmAuxiliary.Show; 33 except 34 FormRefFail; 35 end; 36 end; // end procedure TfrmMain.radAuxShowClick 37 procedure TfrmMain.radAuxHideClick(Sender: TObject); 38 begin 39 try 40 frmAuxiliary.Hide; 41 except 42 FormRefFail; 43 end; 44 end; // end procedure TfrmMain.radAuxHideClick 45 procedure TfrmMain.btnCreateClick(Sender: TObject); 46 begin 47 if frmAuxiliary = nil then 48 begin // setting frmAuxiliarys properties 49 frmAuxiliary := TForm.Create(Self); 50 frmAuxiliary.Caption := 'frmAuxiliary'; 51 frmAuxiliary.Height := 150; 52 frmAuxiliary.Width := 180; 53 frmAuxiliary.OnShow := frmAuxiliaryShow; // ref to event handler 54 end 55 else 56 ShowMessage (Mssgs [1]); 57 end; // end procedure TfrmMain.btnCreateClick 58 procedure TfrmMain.btnFreeClick(Sender: TObject); 59 begin 60 try 61 frmAuxiliary.Hide; 62 frmAuxiliary.Free; 63 frmAuxiliary := nil;
Chapter 1, Page 33
68 procedure TfrmMain.frmAuxiliaryShow(Sender: TObject); 69 begin 70 frmAuxiliary.Left := Random (600); 71 frmAuxiliary.Top := Random (400); 72 end; // end procedure TfrmAuxiliary.FormShow 73 procedure TfrmMain.FormRefFail; 74 begin 75 ShowMessage (Mssgs [0]); 76 radAuxHide.Checked := False; 77 radAuxShow.Checked := False; 78 end; // end procedure TfrmMain.FormRefFail 79 initialization 80 Randomize; 81 end.
Notice that this diagram shows a relationship between classes, and not objects. It shows two relationships between the classes: we have derived a class, TfrmMain, from TForm and Chapter 1, Page 34 Object orientation with Delphi (all rights reserved)
TfrmMain carries, as a private data field, a reference to a (separate) object of type Tform. We dont show this reference in the diagram because it is implied by the solid diamond symbol and the link to Tform. Both the event handler linked by the programmer and the helper method are private, so their names are preceded by a minus symbol. All other operations (and attributes) so far have been public and so they have been preceded by a plus symbol. What about an object diagram? After clicking btnCreate on the main form, the object diagram is (figure 18):
Figure 18 Object diagram after clicking btnCreate
After clicking btnFree on the main form, the object diagram is (figure 19):
Figure 19 Object diagram after clicking btnFree
The sequence diagram for this version of the program remains the same as the previous one (figure 16).
Chapter Summary
In this chapter we have briefly seen: Delphis unit and project structure, RAD generation of visual objects in Delphi, the process of defining a class, declaring a reference to an object of that class, instantiating an object of that class, using the class and finally freeing that class and how to perform this in program code; the Inheritance (IsA), Association (UsesA) and Composition (UsesA) OO relationships; interaction between objects: simple message passing, association and composition, and UML graphical representations: a class diagram shows static inheritance and association structures, an object diagram snapshots objects and their relationships at a particular moment in the execution, and a sequence diagram shows sequential message passing between objects. This chapter has also introduced: Object orientation basics and Event handlers
Chapter 1, Page 35
Main points: 1. Understanding RAD generated code: classes and objects. 2. Generating visual objects through program code as a precursor to non-visual objects. 3. Inheritance, association and composition. 4. UML class, object and sequence diagrams. Objects as independent entities: Defining and using a class. Objects as derived entities: Simple inheritance and composition. Objects as interacting entities: Association and simple message passing. UML diagrams: Class diagram, object diagram and sequence diagram.
Problems
Problem 1.1 Study Chapter 1
Identify the appropriate example(s) or section(s) of the chapter to illustrate each comment made in the summary above.
Chapter 1, Page 36
Chapter 1, Page 37