Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I invoke a descendant's implementation when I call a method on a base class?

My top level class is TBaseDB, which has a descendant TCommonDB (, and TCommonDB will have multiple descendants, like TProdDB and TDevDB).

Let's create a function in each class definition, called Test1. For now, all it does is a ShowMessage('Some literal') just to show me what code is being executed.

I don't know the class type until runtime. I want to have common code, but different behavior.

What I want is something like this:

var
  MyObj: TBaseDB;
begin        
  //pseudo-code...
  if RadioButton1.Checked then
    MyObj := TBaseDB.Create
  else
    MyObj := TCommonDB.create;
  MyObj.Test1;    
end;

I can't seem to get this to work, and I imagine it is in my class definition. How should Test1 be defined so that:

  1. I can declare my variable as TBaseDB,
  2. the created class can be either TBaseDB or TCommonDB, and
  3. the proper Test procedure will be called depending on the instance being a TBaseDB or TCommonDB?
like image 396
user1009073 Avatar asked Dec 27 '22 08:12

user1009073


1 Answers

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TFruit = class
  public
    procedure ShowMessage; virtual; abstract;
  end;

  TApple = class(TFruit)
  public
    procedure ShowMessage; override;
  end;

  TOrange = class(TFruit)
  public
    procedure ShowMessage; override;
  end;


{ TApple }

procedure TApple.ShowMessage;
begin
  Writeln('I''m an apple!');
end;

{ TOrange }

procedure TOrange.ShowMessage;
begin
  Writeln('I''m an orange!');
end;

var
  fruit: TFruit;

begin

  fruit := TApple.Create;

  fruit.ShowMessage;

  Writeln('Press Enter to continue.');
  Readln;

end.

The keyword abstract allows you to have no implementation at all in the base class. You can also, however, have an implementation there as well:

program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TFruit = class
  public
    procedure ShowMessage; virtual;
  end;

  TApple = class(TFruit)
  public
    procedure ShowMessage; override;
  end;

  TOrange = class(TFruit)
  public
    procedure ShowMessage; override;
  end;


{ TFruit }

procedure TFruit.ShowMessage;
begin
  Writeln('I''m a fruit.');
end;

{ TApple }

procedure TApple.ShowMessage;
begin
  inherited;
  Writeln('I''m an apple!');
end;

{ TOrange }

procedure TOrange.ShowMessage;
begin
  inherited;
  Writeln('I''m an orange!');
end;

var
  fruit: TFruit;

begin

  fruit := TApple.Create;

  fruit.ShowMessage;

  Writeln('Press Enter to continue.');
  Readln;

end.

Exercises:

  1. In each case, what happens if you create an instance of TFruit?
  2. In the second case, what does inherited in TApple.ShowMessage and TOrange.ShowMessage mean? Do they need to be at the top of the procedures? What happens if you omit them?
like image 197
Andreas Rejbrand Avatar answered May 19 '23 21:05

Andreas Rejbrand