I have two binary files that contain a similar type of data so I want to create a unified viewer (TViewer) for both files.
Some, methods are common for these two file types, some are not. So I created a base class
TShape, and the from it TCircle and TTriangle.
Pseudo code:
TShape = class(TObject)
function NoOfItems: integer; virtual; abstract;
end;
TCircle = class(TShape)
function NoOfItems: integer; override; <---- The real implementation
end;
TTriangle = class(TShape)
function NoOfItems: integer; override; <---- The real implementation
end;
TViewer = class(TStringGrid)
Container: TShape;
end;
And I use it like this:
Procedure Main;
begin
if FileType= Circle
then (Viewer.Container as TCircle).Load(FileName)
else (Viewer.Container as TTriangle).Load(FileName);
Caption:= Viewer.Container.NoOfItems; <---- it calls TShape which is abstract
end;
When I do this it works:
if Viewer.Container is TTriangle
then Caption:= (Viewer.Container as TTriangle).NoOfItems
else ...
but I want to do it directly like this:
Caption:= Viewer.Container.NoOfItems;
Obviously there is nothing wrong in using is
except that I will have to use it in many many places (close to everywhere). There is a nicer way to achieve this unified viewer?
Update:
Actually, it may be also a performance problem. My file has a really big number of items (up to billions) so doing so many 'is/as' tests may actually have a real impact on speed.
You're doing it wrong.
You need to change your code so that the container is not created until you know what type it needs to be, and then create the proper type:
Procedure Main;
begin
if FileType= Circle then
Viewer.Container := TCircle.Create
else
Viewer.Container := TTriangle.Create;
Viewer.Container.Load(FileName);
Caption := IntToStr(Viewer.Container.NoOfItems); <---- it calls proper code
end;
Here's a working example of using inheritance and polymorphism for you:
program InheritancePolymorphismTest;
uses
System.SysUtils;
type
TAnimal=class
public
procedure Sit; virtual;
procedure Speak; virtual;
end;
TDog=class(TAnimal)
public
procedure Sit; override;
procedure Speak; override;
end;
TCat=class(TAnimal)
public
procedure Speak; override;
end;
TAnimalArray = array of TAnimal;
{ TCat }
procedure TCat.Speak;
begin
inherited;
WriteLn('Bah! No way cats speak when told.');
end;
{ TDog }
procedure TDog.Sit;
begin
inherited;
WriteLn('Sitting down.');
end;
procedure TDog.Speak;
begin
inherited;
Writeln('Woof! Woof!');
end;
procedure TAnimal.Sit;
begin
end;
procedure TAnimal.Speak;
begin
end;
var
Animals: TAnimalArray;
i: Integer;
Pet: TAnimal;
{ TAnimal }
const
NumAnimals = 5;
begin
SetLength(Animals, NumAnimals);
for i := 0 to High(Animals) do
begin
if Odd(i) then
Animals[i] := TDog.Create
else
Animals[i] := TCat.Create;
end;
for Pet in Animals do
begin
Pet.Speak;
Pet.Sit;
end;
Writeln('');
Readln;
end.
Real code and real output. Polymorphism still works!
So I think you have missed some important details while declaring and implementing your class hierarchy.
type
TShape = class(TObject)
function IAm: string; virtual; abstract;
end;
TCircle = class(TShape)
function IAm: string; override;
end;
TTriangle = class(TShape)
function IAm: string; override;
end;
{ TCircle }
function TCircle.IAm: string;
begin
Result := 'I am circle'
end;
{ TTriangle }
function TTriangle.IAm: string;
begin
Result := 'I am triangle'
end;
procedure TForm1.Button6Click(Sender: TObject);
var
Shape: TShape;
begin
Shape := TCircle.Create;
Memo1.Lines.Add(Shape.IAm);
Shape.Free;
Shape := TTriangle.Create;
Memo1.Lines.Add(Shape.IAm);
Shape.Free;
end;
output
I am circle
I am triangle