Introducing Type Inheritance - Delphi OOP Part 6 - Chapter 13
Materials written by John Barrow. Modifications by Zarko Gajic
Back to Chapter 12
Introducing (in this Chapter):
Instead we can just declare an attribute in the TFurniture class to indicate Furniture, Chair, or Table. But in cases where the differences between subclasses are more extensive, combining everything into a single class leads to a class that is very big and unwieldy, and makes future change and maintenance very difficult. So can we retain the hierarchy of three separate domain classes from the previous example but find an alternative program that does not use polymorphism?
Polymorphism is the combination of substitution and dynamic binding. In step 1 of this example we?ll look at what happens if we use neither substitution nor dynamic binding. In step 2 we?ll use substitution with static binding.
The association links between classes become much more complex without substitution.
In writing this program without substitution, the only benefit we gain from the inheritance hierarchy is that TFurniture defines a signature, ie the GetKind method, with which TChair and TTable comply.
To test this, remove the typecasts in lines 37 and 39. You?ll get the TFurniture?s method even with TChair and TTable instances. Alternatively, if you make the wrong typecasts in lines 37 and 39, the static binding will bind to the wrong methods, giving the wrong message:
Static binding is slightly faster than dynamic binding. But all the If?ElseIf comparisons introduce their own performance penalty, counteracting the faster static binding, and introduce considerable room for error.
In summary, using static binding introduces a lot of extra coding complexity, makes the program more error prone, makes subsequent enhancement more brittle and generally does not introduce a significant speed advantage.
Back to Chapter 12
Introducing (in this Chapter):
- Polymorphism = substitution + dynamic binding
Alternatives to polymorphism
Newcomers to OO sometimes feel reluctant to exploit polymorphism; it seems too strange. How important is it? What are the alternatives? In this case, because this is such a simple example, the easiest alternative is not to subclass at all.Instead we can just declare an attribute in the TFurniture class to indicate Furniture, Chair, or Table. But in cases where the differences between subclasses are more extensive, combining everything into a single class leads to a class that is very big and unwieldy, and makes future change and maintenance very difficult. So can we retain the hierarchy of three separate domain classes from the previous example but find an alternative program that does not use polymorphism?
Polymorphism is the combination of substitution and dynamic binding. In step 1 of this example we?ll look at what happens if we use neither substitution nor dynamic binding. In step 2 we?ll use substitution with static binding.
Ex 6.2 step 1 No substitution
Without substitution we need to declare three reference variables in class TfrmSubstitution, to FreeAndNil all three references in the rgbFurnitureClick and btnFreeClick event handlers, and then to test for the existence of all three objects in the btnKindClick event handler.The association links between classes become much more complex without substitution.
In writing this program without substitution, the only benefit we gain from the inheritance hierarchy is that TFurniture defines a signature, ie the GetKind method, with which TChair and TTable comply.
Ex 6.2 step 2 Substitution without polymorphism
Considering that the previous step was not too successful, can we solve the problem using substitution but not dynamic binding? To do this, remove the dynamic binding in unit FurnitureU (ie revert from the code in example 6.1 step 4 to the code in step 3 by commenting out or removing the virtual and override keywords). Now replace the polymorphic message (example 6.1, step 2, line 37) with a complex If statement to test the class of the current MyFurniture instance and then call the required GetKind method. 32 procedure TfrmSubstitution.btnKindClick(Sender: TObject) ; 33 begin 34 if MyFurniture = nil then 35 lblKind.Caption := 'Object not defined' 36 else if (MyFurniture is TChair) then 37 lblKind.Caption := (MyFurniture as TChair).GetKind 38 else if (MyFurniture is TTable) then 39 lblKind.Caption := (MyFurniture as TTable).GetKind 40 else 41 lblKind.Caption := MyFurniture.GetKind; 42 end;
What was accomplished in a single line with polymorphism (step 2 line 37) now requires a complex set of if statements with static binding. We must test explicitly for each possible instance type and then cast it specifically to invoke the correct method. At compile time, the compiler knows that MyFurniture in line 37 must be treated as a TChair because of the typecasting as operator. So in line 37, the MyFurniture object is statically bound to TChair's GetKind method. Similarly in line 39, the compiler has enough information at compile time to bind MyFurniture to TTable's GetKind method. Without any typecasting in line 41, MyFurniture is statically bound to TFurniture's GetKind method because MyFurniture is declared as TFurniture.To test this, remove the typecasts in lines 37 and 39. You?ll get the TFurniture?s method even with TChair and TTable instances. Alternatively, if you make the wrong typecasts in lines 37 and 39, the static binding will bind to the wrong methods, giving the wrong message:
46 elseif (MyFurniture is TChair) then 37 lblKind.Caption := TTable(MyFurniture).GetKind // wrong typecast 38 else if (MyFurniture is TTable) then 39 lblKind.Caption := TChair(MyFurniture).GetKind // wrong typecast
The order of evaluation in the conditional statements is important. If we test first for the class highest in the hierarchy, in this case TFurniture, all the subclasses will also evaluate true because any subclass can substitute for its superclass.Static binding is slightly faster than dynamic binding. But all the If?ElseIf comparisons introduce their own performance penalty, counteracting the faster static binding, and introduce considerable room for error.
In summary, using static binding introduces a lot of extra coding complexity, makes the program more error prone, makes subsequent enhancement more brittle and generally does not introduce a significant speed advantage.
Source...