DelphiBasics
  Home  |  Inheritance
 Documents
 Tutorials

 Writing your first program
 Writing your second program
 Amending this program

 Delphi data types
   Numbers
   Text (strings and chars)
   Sets and enumerations
   Arrays
   Records

 Programming logic
   Looping
   SubRoutines
   Exception handling

 Dates and times

 Files

 Pointers

 Printing text and graphics

 Object Orientation basics
   Memory leaks!
   Inheritance
   Abstraction
   Interfaces
   An example class

 References

 Standard components

 Articles

 A brief history of Delphi

 Usability : file handling

 Usability : reference books

 Author links

  Inheritance
What is inheritance?
Inheritance in people is through their genes. These genes may give you your Mother's nose, or your Father's ear for music, along with a lot else. You inherit features of your parents, and features of their parents, and indeed all human beings. You are at the bottom of an enormous hierarchy of living things. But your inheritance is a framework - you add your own features, and modify some of those inherited. You may be able draw when your parents cannot, but you are not such a good cook maybe. Inheritance in Object oriented languages like Delphi has much the same features, as you will see.
 

Inheritance in Delphi
Object oriented (OO) languages, as their name implies, revolve around another aspect of the real World, where a chunk of code and data is treated as an object. An object in the real World, such as a football, has characteristics, such as size (the data), and actions, such as being kicked (a method). Such objects in an OO language are defined in a class. A class of objects, such as a class of balls, of which a football is a sub-class. The sub-class has specific features (such as a set of sewn panels) but also inherits the parent class features. It too has a size, and can be kicked. In code terms, a Football class would inherit the parent Ball class size variable (data), and kick method (code) :
 
 type
  // Define a Ball class
   TBall = class
   protected
     ballSize  : Byte;
     ballSpeed : Byte;
   published
     procedure   Kick(power : Byte);
     function    GetSpeed : Byte;
     constructor Create(size : Byte);
   end;
 
  // Define a specialised ball
   TFootball = class(TBall)
   private
     ballPanels : Byte;
   published
    // Different constructor - must pass panels now
     constructor Create(size : Byte; panels : Byte);
   end;

And here are the method implementations for these classes:
 
 // Ball method implementations
 procedure TBall.Kick(power : Byte);
 begin
   ballSpeed := (power * ballSize) DIV 4;
 end;
 
 function TBall.GetSpeed : Byte;
 begin
   Result := ballSpeed;
 end;
 
 constructor TBall.Create(size : Byte);
 begin
   ballSize  := size;
   ballSpeed := 0;
 end;
 
 // Football method implementations
 constructor TFootball.Create(size : Byte; panels : Byte);
 begin
   Inherited Create(size);  // Call the parent constructor first
   ballPanels := panels;    // Save the passed number of panels
 end;

Ife we run the following code, creating and using object of these classes, we can see how the inheritance operates:
 
 var
   beachBall  : TBall;
   soccerBall : TFootball;
 begin
  // Create our two balls
   beachBall  := TBall.Create(5);
   soccerBall := TFootball.Create(5, 12);
 
  // How fast are they moving at the moment?
   ShowMessageFmt('Beach ball  is moving at speed : %d',[beachBall.GetSpeed]);
   ShowMessageFmt('Soccer ball is moving at speed : %d',[soccerBall.GetSpeed]);
 
  // Now kick each ball with a power of 10
   beachBall.Kick(10);
   soccerBall.Kick(10);
 
  // How fast are they moving now?
   ShowMessageFmt('Beach ball  is moving at speed : %d',[beachBall.GetSpeed]);
   ShowMessageFmt('Soccer ball is moving at speed : %d',[soccerBall.GetSpeed]);
 end;

 The ShowMessageFmt routine shows the two ball speeds before and after kicking:
 
 Beach ball  is moving at speed : 0
 Soccer ball is moving at speed : 0
 Beach ball  is moving at speed : 12
 Soccer ball is moving at speed : 12

In the above code snippets, we have defined a parent and child class. The TFootball child is base on the parent TBall class. The Football inherits the size and speed variables, and the kick method. It only differs in that when creating a football, you are adding a football feature; the number of panels.
 

Strengths and weaknesses of inheritance
The kick and getSpeed methods are part of the Ball class; the Football class can use them without any code addition. Not even a reference to them. This is the power of the inheritance. But it is also a weakness - you have to look at the parent class (and maybe its parents) to find out what you can do with a class.
 
There are many more subtle benefits of inheritance. When you extend a class, and inherit the data and methods, you are benefitting from software reusability. Only one version of a method needs to exist.
 
With only one version of the method to maintain, you are increasing code reliability. This is true, of course, with the overall object concept, where one class can provide for multiple object instances, all sharing the same methods.
 
With only one version of each method, code size is reduced.
 
The parent class provides inheritable methods, which can be used by child classes without them knowing, or needing to know how they are implemented. This is called information hiding and is a very powerful benefit.
 
Finally, the reduced code size, and method hiding cleans up the code, and it can make for improved code readability. But bear in mind the disadvantage mentioned earlier - you may have to search for the method used by a class.
 

Types of inheritance
You may have noticed that the Football kick method did not take into account the effect of the number of panels on the speed. It may be that more panels make for a faster ball. Lets make that change to the kick method in the Football class:
 
 type
   TFootball = class(TBall)
   private
     ballPanels : Byte;
   published
    // Now we override the TBall kick method to take into account
    // the number of panels of the ball
     procedure   Kick(power : Byte); override;
    // Different constructor - must pass panels now
     constructor Create(size : Byte; panels : Byte);
   end;

And its new kick method:
 
 procedure TFootball.Kick(power : Byte);
 begin
  // Now take the number of panels into account
   ballSpeed := (power * ballSize * ballPanels) DIV 24;
 end;

Here we have overridden the inherited kick class. Calling a Ball or Football class kick method still looks the same, but the outcome is now different :
 
 The ShowMessageFmt routine shows the two ball speeds before and after kicking:
 
 Beach ball  is moving at speed : 0
 Soccer ball is moving at speed : 0
 Beach ball  is moving at speed : 12
 Soccer ball is moving at speed : 25

By taking into account the number of Football panels, it travels faster than a plain ball. This type of class inheritance is called a specialisation - the child class is a specialised version of the parent class.
 
Another form of class inheritance is where the child class enriches the parent class. It literally extends the parent. For example, a new class inherited from a TButton may add graphics to the button.
 
Finally, there are two rather different forms of inheritance. Abstract classes provide skeleton methods that child classes must implement (unless they want to be abstract classes). Interfaces provide a variation on abstract classes. A class that implements an interface must flesh out the skeleton methods of the interface.
 

The full code for the above example
This code may be run by copying and pasting into Delphi, following the instructions at the start:
 
 // Full Unit code.
 // -----------------------------------------------------------
 // You must store this code in a unit called Unit1 with a form
 // called Form1 that has an OnCreate event called FormCreate.
 
 unit Unit1;
 
 interface
 
 uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs;
 
 type
  // Define a Ball class
   TBall = class
   protected
     ballSize  : Byte;
     ballSpeed : Byte;
   published
     procedure   Kick(power : Byte); virtual;
     function    GetSpeed : Byte;
     constructor Create(size : Byte);
   end;
 
  // Define a specialised ball
   TFootball = class(TBall)
   private
     ballPanels : Byte;
   published
    // Now we override the TBall kick method to take into account
    // the number of panels of the ball
     procedure   Kick(power : Byte); virtual;
    // Different constructor - must pass panels now
     constructor Create(size : Byte; panels : Byte);
   end;
 
  // definition of the form this unit uses
   TForm1 = class(TForm)
     procedure FormCreate(Sender: TObject);
   private
     { Private declarations }
   public
     { Public declarations }
   end;
 
 var
   Form1: TForm1;
 
 implementation
 
 {$R *.dfm}
 
 // Ball method implementations
 procedure TBall.Kick(power : Byte);
 begin
   ballSpeed := (power * ballSize) DIV 4;
 end;
 
 function TBall.GetSpeed : Byte;
 begin
   Result := ballSpeed;
 end;
 
 constructor TBall.Create(size : Byte);
 begin
   ballSize  := size;
   ballSpeed := 0;
 end;
 
 // Football method implementations
 procedure TFootball.Kick(power : Byte);
 begin
  // Now take the number of panels into account
   ballSpeed := (power * ballSize * ballPanels) DIV 24;
 end;
 
 constructor TFootball.Create(size : Byte; panels : Byte);
 begin
   Inherited Create(size);  // Call the parent constructor first
   ballPanels := panels;    // Save the passed number of panels
 end;
 
 // The form OnCreate implementation
 procedure TForm1.FormCreate(Sender: TObject);
 var
   beachBall  : TBall;
   soccerBall : TFootball;
 begin
  // Create our two balls
   beachBall  := TBall.Create(5);
   soccerBall := TFootball.Create(5, 12);
 
  // How fast are they moving at the moment?
   ShowMessageFmt('Beach ball  is moving at speed : %d',[beachBall.GetSpeed]);
   ShowMessageFmt('Soccer ball is moving at speed : %d',[soccerBall.GetSpeed]);
 
  // Now kick each ball with a power of 10
   beachBall.Kick(10);
   soccerBall.Kick(10);
 
  // How fast are they moving now?
   ShowMessageFmt('Beach ball  is moving at speed : %d',[beachBall.GetSpeed]);
   ShowMessageFmt('Soccer ball is moving at speed : %d',[soccerBall.GetSpeed]);
 end;
 
 end.

 
 

Delphi Basics © Neil Moffatt All rights reserved.  |  Home Page