Introduction to Class Inheritance (Delphi OOP Tutorial)

Part 2 - Chapter 4

Visual Form Inheritance
Materials written by John Barrow. Modifications by Zarko Gajic

Back to Chapter 3

Introducing (in this Chapter):

  • Class inheritance.
  • Subclassing for reuse: extension, specialisation, generalisation.
  • Overriding inherited methods.
  • Encapsulation.
  • Visual Form Inheritance (VFI).

Introduction

In part 1 (chapters 1,2,3) we talked about three perspectives on objects: objects as independent entities, objects as derived entities and objects as interacting entities.
In this chapter we will take a first look at objects as derived entities by using one of Delphi’s RAD facilities called Visual Form Inheritance (VFI). This allows us to define a form and then use it as the basis for further forms through inheritance. We’ll use VFI to reuse an existing form through inheritance and then we’ll extend and specialise these derived forms through our own code. And because objects in isolation are not much use, we’ll look briefly at the interaction between these objects.

Example 2.1 Subclassing through Visual Form Inheritance (VFI)

In this example, we’ll use VFI to create the structure in figure 1. For now we show only the class names and suppress the attributes and the methods in the class diagrams. Later diagrams will show more detail.

Ex 2.1 step 1 The Master Form

Start a new application. We’ll write an event handler for this form (TForm1 in figure 1) to display other forms (TForm3 and TForm4) in a moment, but for now, just change the size of this TForm1 to about 300 x 150.

Note: These dimensions refer to a low resolution screen.

With higher resolution screens 452 x 225 or 600 x 300 may be better. It just needs to be a convenient size: big enough to use easily and small enough not to obscure other forms.

Ex 2.1 step 2 The base class

VFI is the RAD process of creating a template form, the base class, and then inheriting (or subclassing) other forms from it.
First we create the base class.

Using File | New | Form, add a second form to the project (TForm2 in figure 1) and place a button on it. This will form a base class from which we will inherit two subforms. Make the form a convenient size (about 200 x 100) and create an OnClick event handler for button1 in Unit2:

 13  implementation
 14  {$R *.dfm}
 15  procedure TForm2.Button1Click(Sender: TObject) ;
 17  begin
 17   ShowMessage ('Button ' + (Sender as TButton).Caption + ' clicked') ;
 18  end; // end procedure TForm2.Button1Click
 19end. // end Unit2 
The descendant forms (TForm3 and TForm4) will inherit this method, and we’ll use the Sender parameter to identify the Caption of the button that has been clicked.
Line 17 is the first appearance of the Sender parameter and we’ll see it several times again through the course of this module.

Ex 2.1 step 3 A Derived Form

TForm2, defined in step 2, is the base form from which we will derive further forms.

To create TForm3 in Delphi 4 to 7, select the menu sequence File | New | Other and then select the Project1 tab (or Project2, 3, .... depending on the name of your current project). To create TForm3 in Delphi 2006, select the menu sequence File | New | Other | Delphi Projects | Inheritable Items.

Form1 and Form2 are listed. Select Form2 (this is the base form class from step 2), make sure that the Inherit RadioButton lower down is selected and click OK.

Up until now, all our forms have been derived from TForm. Because we are using TForm2 as a basis, Delphi derives our new form, TForm3 in Unit3, from TForm2 (line 7 below). Units are encapsulated from each other, and so for Unit3 to be able to refer to TForm2 in the class declaration (line 7), Delphi inserts Unit2, which defines TForm2, in the global uses clause (line 5) of Unit3.

unit Unit3;
 
 2 interfaceuses
 4   Windows, Messages, SysUtils, Variants, Classes,
 5   Graphics, Controls, Forms, Dialogs, Unit2;
 
 6 type
 7   TForm3 = class(TForm2) // inherited from TForm2, not TForm
 8   private
 9    { Private declarations }
 10   public
 11    { Public declarations }
 12   end;
 13   var
 14    Form3: TForm3;
 
 15   implementation
 16 {$R *.dfm}
 
 17 end. 
TForm3 inherits all the data fields (properties) and behaviour (methods and event handlers) from TForm2, and so a button, defined in TForm2, appears as part the screen display of object Form3 even though it is not listed in TForm3’s type definition (lines 7–12 above).

Using the Object Inspector, change the button’s Caption to btnForm3 and the form’s position (the Top and Left properties)so that it does not obscure either of the other forms. Don’t make any other changes to Form3 at this stage.

Ex 2.1 step 4 Another Derived Form

We can inherit any number of classes from an existing class, so inherit another new form and unit (TForm4 in Unit4) from Form2 following the process outlines in step 3.

Change the Caption of the button on TForm4 to btnForm4. Position the four forms so that they do not overlap, but don’t change any other properties.

Inheritance means that the subclass inherits all the data fields and methods from the superclass.

Because this is a RAD application, the subclasses also have as default the initial data values of the superclass. To see this, change, say, the width of Form2. The widths of Form3 & Form4 change as well. If you’re in any doubt about what we have done so far, study the code for each of these units.

Ex 2.1 step 5 Linking the forms

We now have an inheritance structure with a number of different classes. However, a class structure on its own does not do much – we need to introduce some interaction paths between the classes. We want to be able to display Form3 and Form4 from Form1. We can extend our class diagram to show these paths.

The UML convention uses closed, unfilled arrowheads for inheritance and open arrowheads for navigation links (also called association links). Figure 3 shows open arrowheads on the links between TForm1 and TForm3 & TForm4, pointing from TForm1 to the other two forms.

These reflect that Form1 can access Form3 and Form4, but that no access is available in the reverse direction.

To implement these links, return to Unit1. Using the Component / Tool palette, place a button on TForm1. Set its Name property to btnShowForms and its Caption to Show Forms. Create an OnClick event handler for it that sends messages to Form3 and Form4, asking them to Show themselves (lines 20–21 below).

Press <F9>, ostensibly to run the program. Delphi will show an Information Box saying: “Form ‘Form1’ references ‘Form3’ declared in ‘Unit3’ which is not in your USES list. Do you wish to add it?” Click the Yes button for this question. Run it again. It repeats the question, but this time for Unit4. Click Yes to add Unit4 to the uses clause as well. Delphi adds a local clause ‘uses Unit3, Unit4;’ to Unit1 (ie in its private, implementation section and not in the public, interface section) (line 16 below).

 15 implementation
 
 16 uses Unit3, Unit4;
 
 17 {$R *.dfm}
 
 18 procedure TForm1.btnShowFormsClick(Sender: TObject) ;
 19 begin
 20   Form3.Show;
 21   Form4.Show;
 22 end;
 
Run the program again. Form1 (only) appears. Click on it to invoke its OnClick event handler and Form3 and Form4 appear. Form2 does not appear because it has not been instructed to Show itself. A Click on Form1 can send a Show message to Form3 and to Form4 because there is an association between TForm1 and TForm3 and between TForm1 and TForm4 (figure 3). This linkage is possible because of the uses clause in Unit1, which lists Unit3 and Unit4 (line 16 above).

Click the button on Form3 or Form4, and a message identifying the button by its Caption appears.

Neither TForm3 nor TForm4 have defined any data fields or operations. However they are both derived from TForm2, which defines a TButton data field and its OnClick event handler, and so both TForm3 and TForm4 inherit the button and its event handler from TForm2. Thus, through inheritance, TForm3 and TForm4 reuse TForm2 and in this case do not need any code of their own.

We set different Captions for the Buttons on Form3 and Form4 to show that we are working with different objects. To identify these separate objects, we use the Sender parameter in line 17 of Form2’s OnClick event handler (example 2.1, step 2). Since every instance has its own data, Form3 and Form4 also have their own values for Top, Left, and so on, which is why they can take up different positions on the screen.

Save this project as we’ll use it as a starting point for examples 2.2 and 2.3.

Example 2.1 Summary:
The main OO principles in this example are inheritance and communication. TForm2 inherits the knowledge of ‘how to be a form’ from TForm. It adds its own data field (a button) and a method (the button’s OnClick event handler). TForm3 and TForm4 are both derived from TForm2 and neither adds anything of its own. Yet, because of inheritance, each one has the knowledge of how to be a form (from the VCL class TForm), and a button and its event handler (from TForm2). We also created navigation links (simple association) between one form and another.

From a Delphi perspective, we have seen how with VFI we can define a base form (Form2 in this case) and then derive further forms from it. We gained all the functionality available in the base form without having to redefine forms separately as in chapter 1. The buttons on the derived forms even all invoke the base form’s event handler. So VFI is a useful illustration of OO and inheritance and the way subclassing can promote reuse.

In example 2.1 we didn’t add any code to units 3 or 4. Because of inheritance each of their Buttons uses the same event handler, the one inherited from the base form. However, a subclass can also extend the superclass’s functionality. Subclassing for extension involves giving a subclass further features in addition to those it inherits.

Example 2.2 Subclassing for Extension

In this example we’ll give each subclass its own separate method: they will each manipulate Form1’s position.

We reflect the bidirectional navigability on our UML class diagram through double openheaded arrows on the association links (figure 5).

Here we show the relevant attributes and methods that extend the superclass. To present a slightly different style, the inheritance paths are shown as separate arrows, and not as combined ‘tree graphs’ as in the previous class diagrams.

Ex 2.2 step 1 Extension in a Subclass

Start with the program of example 2.1. Add a TCheckBox to Form3 (figure 6), give it the Caption ‘Top’, and create an OnClick event handler by double-clicking on the CheckBox:
 procedure TForm3.CheckBox1Click(Sender: TObject) ;
 begin
  inherited;
 
  if CheckBox1.Checked then Form1.Top := 10;
 end; 
There’s a slight difference here from our previous event handlers. Since TForm3 is derived from TForm2 (and not TForm), Delphi automatically inserts the "inherited" statement as the first program line in this event handler. Leave it there for now even though it does not do anything here – we’ll discuss the significance of ‘inherited’ in the next example.

TForm3 now inherits a button and event handler from TForm2 and adds a CheckBox and another event handler of its own.

Ex 2.2 step 2 A Different Extension in Another Subclass

An important aspect of subclassing for extension is that each subclass can extend the superclass differently. To see this, add an UpDown component (Win32 tab) to Form4, set its Wrap property to True, and create an OnClick event handler:
 procedure TForm4.UpDown1Click(Sender: TObject; Button: TUDBtnType) ;
 begin
  inherited;
 
  if Button = btNext then
    Form1.Top := Form1.Top - 20
  else
    Form1.Top := Form1.Top + 20;
 
  Form3.CheckBox1.Checked := False;
 end; 
Here again, Delphi automatically inserts the ‘inherited’ statement. We include a message from Form4 to Form3 to clear the CheckBox whenever Form4 repositions Form1.

Ex 2.2 step 3 Bidirectional Communication

When using VFI we often send messages from one form to another, and in the steps above both Form3 and Form4 communicate with Form1, and Form4 communicates with Form3. We therefore need links between forms, and Delphi can often set these links up for us. To see this, run the program (press <F9>). Delphi issues a series of messages informing us that various form use other forms but do not include them in their respective uses lists. Click Yes each time for Delphi to insert the appropriate uses clauses. Notice that Delphi inserts local uses clauses into Unit3.pas and Unit4.pas (ie in the implementation section).

Attempt again to run the program. This time, when you show Form3 and Form4 by clicking on Form1, each of these has the structure it derives from Form2 and also has its own, individual extension that we have just introduced.

Example 2.2 Summary:
This example introduces subclassing for extension. Using inheritance for extension has the characteristics that:

  • The subclass inherits all the functionality of the superclass,
  • The subclass adds new functionality,
  • The subclass makes no change to the (inherited) superclass’s functionality.
Subclassing for extension is what people often have in mind when they extol the virtues of inheritance as an important approach to the problem of reuse. We also used this example as an opportunity to look briefly at bidirectional navigation between objects and at representing navigation on a class diagram.

Example 2.3 Subclassing for Specialisation

Another very important use of inheritance is subclassing for specialisation. Here, the subclass is in some way a special case of the superclass and either overrides or extends the superclass’s methods. We’ll give Form3 its own additional Button1Click event handler to extend the operation of the event handler it inherits.

Ex 2.3 step 1 Specialising TForm3

Open the project from example 2.1. (If you don’t want to overwrite it, save all the units and project file in a different subdirectory for use in this example).

TForm3 and TForm4 are empty classes that inherit all their functionality (TForm2’s OnClick event handler) from TForm2. We are going to specialise this OnClick event handler in the subclasses by adding extra functionality to it in TForm3 and in TForm4.

In design mode, select Form3 and double-click on Button1. Delphi creates an event handler skeleton. Add the additional line of code shown below (line 17).

unit Unit3;
 2 interfaceuses
 4    Windows, Messages, SysUtils, Variants, Classes,
 5    Graphics, Controls, Forms, Dialogs, StdCtrls, Unit2;
 6 type
 7    TForm3 = class(TForm2)
 8      procedure Button1Click(Sender: TObject) ;
 9    end;
 10 var
 11    Form3: TForm3;
 12 implementation
 13    {$R *.dfm}
 14 procedure TForm3.Button1Click(Sender: TObject) ;
 15 begin
 16    inherited;
 17    ShowMessage ('Another msg from ' + (Sender as TButton).Caption) ;
 18 end;
 19 end. 
Run this program. Clicking on the button in Form3 brings up the original message generated by TForm2’s Button1Click event handler (example 2.1, step 2, line 17). Accepting this message then brings up another message generated by TForm3’s Button1Click event handler. By contrast, clicking on the button in Form4 gives just the functionality derived from Form2.

So with TForm3 we have a case where we use inheritance to add to the inherited functionality, a process called specialisation.

Let’s look at what is happening here. When you double-click on the Button1 in Form3 during design mode, Delphi creates a new Button1 event handler as a method of TForm3 (declared in line 8 above).

For any object of class TForm3, the OnClick event handler in TForm3 will override the inherited event handler in TForm2. (By contrast, TForm4 does not declare its own event handler, so Form4 therefore goes up a level and executes the event handler inherited from TForm2.)

Delphi also generates the event handler skeleton (lines 14–16 & 18 above). What’s different here from the event handler skeletons we saw in chapter 1 is that Delphi inserts an ‘inherited’ statement (line 16 above and also in the event handlers in example 2.2). Why is this? Usually, with specialisation, we want to add some functionality to that available from the superclass. However, in the previous paragraph we said that the event handler in the derived class completely replaces the event handler defined in the superclass. The inherited keyword in the subclass method calls the corresponding method in the superclass.

So the subclass’s method overrides completely the method in the superclass. If we still want the (overridden) superclass’s method to run, we insert the inherited keyword, usually as the first statement in the (overriding) subclass’s method.

Having called and executed the superclass’s method, we now move on to line 17, which provides the specialisation operations.

For interest, you may wish to change the order of lines 16 &17 and then run the program again. The message from TForm3’s Button1Click now appears before TForm2’s.

Ex 2.3 step 2 Specialising TForm4

We can give TForm4 a different specialisation to TForm3. We will also completely override the superclass’s method by deleting the inherited statement that Delphi inserts automatically. Select Form4, double-click on its Button1 and create the following event handler, remembering to remove the ‘inherited’ statement.

Because this event handler refers to Unit1, this unit also needs a ‘uses Unit1’ clause. You can insert this yourself (line 17 below) or have Delphi insert it for you as before. (Unit3 does not refer to Unit1, and so we did not insert a ‘uses Unit1’ clause in step 1 above.)

 16 implementation
 17 uses Unit1;
 18 {$R *.dfm}
 19 procedure TForm4.Button1Click(Sender: TObject) ;
 20 begin
 21   Form1.Top := 300;
 22 end; 
Run this program. Clicking on the button in Form4 repositions Form1 with its top at 300 pixels without displaying any message. The event handler in the superclass does not run because TForm4’s Button1 OnClick event handler (lines 15–18 above) does not contain the ‘inherited’ keyword and so does not invoke the superclass’s (TForm2’s) Button1 OnClick event handler.

Example 2.3 Summary:
A subclass can specialis a superclass’s functionality. This specialisation can be additional code to that in the superclass’s method, in which case the subclass method contains the inherited keyword. The specialisation can also completely replace the superclass’s method, in which case the subclass’s method does not contain the inherited keyword.

A word of caution: when we completely replace the superclass’s method in the subclass we affect the subtyping of the subclass. Interfering with the subtyping should be done only after careful thought since it invalidates polymorphism, a central concept in OO.

Next time: inheritance in the VCL!

Format
mla apa chicago
Your Citation
Gajic, Zarko. "Introduction to Class Inheritance (Delphi OOP Tutorial)." ThoughtCo, Feb. 16, 2016, thoughtco.com/introduction-to-class-inheritance-delphi-oop-tutorial-1058269. Gajic, Zarko. (2016, February 16). Introduction to Class Inheritance (Delphi OOP Tutorial). Retrieved from https://www.thoughtco.com/introduction-to-class-inheritance-delphi-oop-tutorial-1058269 Gajic, Zarko. "Introduction to Class Inheritance (Delphi OOP Tutorial)." ThoughtCo. https://www.thoughtco.com/introduction-to-class-inheritance-delphi-oop-tutorial-1058269 (accessed November 24, 2017).