Inheritance/polymorphism concept

2019-09-19 11:36发布

问题:

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.

回答1:

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.


回答2:

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


标签: delphi oop