从验证后退出的类继承方法的最佳方法是什么?

时间:2013-10-30 13:34:33

标签: delphi oop inheritance freepascal lazarus

什么是Delphi / Lazarus / FreePascal中继承父方法验证的最佳方法? 假设以下类和方法:

type

  TPlant = class
  public
    FIsGreen: Boolean;
    procedure DoPhotosynthesis; virtual;
  end;

  TChildPlant = class(TPlant)
  public
    procedure DoPhotosynthesis; override;
  end;

Implementation

{TPlant}
procedure TPlant.DoPhotosynthesis;
begin
  if not FIsGreen then Exit; //TPlants cannot do Photosynthesis if they are not green;

  //basic photosynthesis implementation not to be included in child plants
end;

以下实现将完全隐藏继承的验证和/或重复代码。

{TChildPlant}
procedure TChildPlant.DoPhotosynthesis;
begin
  if not FIsGreen then Exit; //TPlant descendants cannot do Photosynthesis if they are not green;

  //photosynthesis implementation...
end;

正在创建另一种方法,比如DoSpecificPhotosynthesis,并覆盖它,以实现TChildPlant.DoPhotosynthesis的实际验证not FIsGreen并退出但不包括基本光合作用实施的最佳方法? (见下文)

type

  TPlant = class
  public
    IsGreen: Boolean;
    procedure DoPhotosynthesis; virtual;
    procedure DoSpecificPhotosynthesis: virtual;
  end;

  TChildPlant = class(TPlant)
  public
    procedure DoSpecificPhotosynthesis; override;
  end;

Implementation

{TPlant}
procedure TPlant.DoPhotosynthesis;
begin
  if not FIsGreen then Exit; //TPlants cannot do Photosynthesis if they are not green;

  //photosynthesis implementation (child plants must implement their specific way);
  DoSpecificPhotosynthesis;
end;

{TChildPlant}
procedure TChildPlant.DoSpecificPhotosynthesis;
begin
  //photosynthesis implementation...
end;

还有其他想法吗?

2 个答案:

答案 0 :(得分:3)

使用策略设计模式避免继承每个行为,如下所示:
这样,您不需要多个TPlant版本,只需要多个行为版本。

program Strategy;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TPhotosystesisBehavior = class
  public
    procedure DoPhotosyntesis; virtual; abstract;
  end;

  TGreenPhotosyntesisBehavior = class(TPhotosystesisBehavior)
  public
    procedure DoPhotosyntesis; override;
  end;

  TOtherPhotosynthesisBehavior = class(TPhotosystesisBehavior)
  public
    procedure DoPhotosyntesis; override;
  end;

  TPlant = class
  private
    function GetPhotoBehavior: TPhotosystesisBehavior;
    procedure SetPhotoBehavior(const Value: TPhotosystesisBehavior);
  protected
    FPhotoBehavior: TPhotosystesisBehavior;
  public
    procedure PerformPhotosyntesis;
    property PhotoBehavior: TPhotosystesisBehavior read GetPhotoBehavior write SetPhotoBehavior;
  end;


{ TGreenPhotosyntesisBehavior }

procedure TGreenPhotosyntesisBehavior.DoPhotosyntesis;
begin
  Writeln('  - Eating some solar energy, delicious!!');
end;

{ TPlant }

function TPlant.GetPhotoBehavior: TPhotosystesisBehavior;
begin
  Result:= FPhotoBehavior;
end;

procedure TPlant.PerformPhotosyntesis;
begin
  Writeln('Performing Photosynthesis: ');
  if Assigned(FPhotoBehavior) then
    FPhotoBehavior.DoPhotosyntesis;
  Writeln('Performing Photosynthesis: End');    
end;

procedure TPlant.SetPhotoBehavior(const Value: TPhotosystesisBehavior);
begin
  FPhotoBehavior := Value;
end;

{ TOtherPhotosynthesisBehavior }

procedure TOtherPhotosynthesisBehavior.DoPhotosyntesis;
begin
  Writeln('  - I Do not like Solar Enery! ');
end;

procedure TestGreenPlant;
var Plant: TPlant;
    GreenPlantBehavior: TGreenPhotosyntesisBehavior;
begin
  Writeln('TestGreenPlant: ');
  Writeln('');
  Plant := TPlant.Create;
  Plant.PerformPhotosyntesis;
  Writeln('');
  GreenPlantBehavior:= TGreenPhotosyntesisBehavior.Create;
  Plant.PhotoBehavior := GreenPlantBehavior;
  Plant.PerformPhotosyntesis;
  Writeln('');
  Writeln('TestGreenPlant: End');
  Writeln('');    
end;

procedure TestOtherPlant;
var Plant: TPlant;
    OtherPlantBehavior: TOtherPhotosynthesisBehavior;
begin
  Writeln('TestOtherPlant: ');
  Writeln('');
  Plant := TPlant.Create;
  Plant.PerformPhotosyntesis;
  Writeln('');
  OtherPlantBehavior:= TOtherPhotosynthesisBehavior.Create;
  Plant.PhotoBehavior := OtherPlantBehavior;
  Plant.PerformPhotosyntesis;
  Writeln('');
  Writeln('TestOtherPlant: End ');
  Writeln('');
end;

begin
  TestGreenPlant;
  Writeln('--------------');
  TestOtherPlant;
  Readln;
end.

<强>更新

如果您愿意,还可以将此模式与Factory结合使用,以确定要在每种类型中使用的行为。在下面的代码中,有3个重载函数来检索TPlant的实例,你不需要所有这些函数,它仅用于演示目的:

program Strategy;

{$APPTYPE CONSOLE}

uses
  SysUtils, TypInfo;

type
  TPhotosystesisBehavior = class
  public
    procedure DoPhotosyntesis; virtual; abstract;
    function ToString: String; virtual;
  end;

  TGreenPhotosyntesisBehavior = class(TPhotosystesisBehavior)
  public
    procedure DoPhotosyntesis; override;
    function ToString: String; override;
  end;

  TOtherPhotosynthesisBehavior = class(TPhotosystesisBehavior)
  public
    procedure DoPhotosyntesis; override;
    function ToString: String; override;
  end;

  TBehaviorType = class of TPhotosystesisBehavior;
  TEnumBehavior = (GreenPlant, OtherPlant, Unknown);

  TPlant = class
  private
    function GetPhotoBehavior: TPhotosystesisBehavior;
    procedure SetPhotoBehavior(const Value: TPhotosystesisBehavior);
  protected
    FPhotoBehavior: TPhotosystesisBehavior;
  public
    procedure PerformPhotosyntesis;
    property PhotoBehavior: TPhotosystesisBehavior read GetPhotoBehavior write SetPhotoBehavior;
  end;

  TPlantFactory = class
  private
    class function InternalGetPlantTyppedInstance(ABehavior: TPhotosystesisBehavior): TPlant; 
  public
    class function GetPlantTyppedInstance(AType: String): TPlant; overload;
    class function GetPlantTyppedInstance(AType: TBehaviorType): TPlant; overload;
    class function GetPlantTyppedInstance(AType: TEnumBehavior): TPlant; overload;
  end;


{ TGreenPhotosyntesisBehavior }

procedure TGreenPhotosyntesisBehavior.DoPhotosyntesis;
begin
  Writeln('  - Eating some solar energy, delicious!!');
end;

function TGreenPhotosyntesisBehavior.ToString: String;
begin
  Result:= 'TGreenPhotosyntesisBehavior';
end;

{ TPlant }

function TPlant.GetPhotoBehavior: TPhotosystesisBehavior;
begin
  Result:= FPhotoBehavior;
end;

procedure TPlant.PerformPhotosyntesis;
begin
  Writeln('Performing Photosynthesis: ');
  if Assigned(FPhotoBehavior) then
    FPhotoBehavior.DoPhotosyntesis;
  Writeln('Performing Photosynthesis: End');    
end;

procedure TPlant.SetPhotoBehavior(const Value: TPhotosystesisBehavior);
begin
  FPhotoBehavior := Value;
end;

{ TOtherPhotosynthesisBehavior }

procedure TOtherPhotosynthesisBehavior.DoPhotosyntesis;
begin
  Writeln('  - I Do not like Solar Enery! ');
end;

procedure TestGreenPlant;
var Plant: TPlant;
    GreenPlantBehavior: TGreenPhotosyntesisBehavior;
begin
  Writeln('TestGreenPlant: ');
  Writeln('');
  Plant := TPlant.Create;
  Plant.PerformPhotosyntesis;
  Writeln('');
  GreenPlantBehavior:= TGreenPhotosyntesisBehavior.Create;
  Plant.PhotoBehavior := GreenPlantBehavior;
  Plant.PerformPhotosyntesis;
  Writeln('');
  Writeln('TestGreenPlant: End');
  Writeln('');    
end;

procedure TestOtherPlant;
var Plant: TPlant;
    OtherPlantBehavior: TOtherPhotosynthesisBehavior;
begin
  Writeln('TestOtherPlant: ');
  Writeln('');
  Plant := TPlant.Create;
  Plant.PerformPhotosyntesis;
  Writeln('');
  OtherPlantBehavior:= TOtherPhotosynthesisBehavior.Create;
  Plant.PhotoBehavior := OtherPlantBehavior;
  Plant.PerformPhotosyntesis;
  Writeln('');
  Writeln('TestOtherPlant: End ');
  Writeln('');
end;

function TOtherPhotosynthesisBehavior.ToString: String;
begin
  Result:= 'TOtherPhotosynthesisBehavior';
end;

{ TPlantFactory }

class function TPlantFactory.GetPlantTyppedInstance(
  AType: TBehaviorType): TPlant;
var Behavior : TPhotosystesisBehavior;
begin
  Writeln('GetPlantTyppedInstance (TBehaviorType): ');
  Writeln('');
  Behavior := AType.Create;
  Result := InternalGetPlantTyppedInstance(Behavior);
  Writeln('');
  Writeln('  - GetPlantTyppedInstance (TBehaviorType): Type Created ');
  Writeln('');
  Writeln('GetPlantTyppedInstance (TBehaviorType): End');
  Writeln('');
end;

class function TPlantFactory.GetPlantTyppedInstance(
  AType: String): TPlant;
begin
  Writeln('GetPlantTyppedInstance (String): ');
  Writeln('');
  if AType = 'GreenPlant' then
    Result := GetPlantTyppedInstance(TGreenPhotosyntesisBehavior)
  else if AType = 'OtherPlant' then
    Result := GetPlantTyppedInstance(TOtherPhotosynthesisBehavior)
  else
    raise Exception.Create('Unkown Type');
  Writeln('');
  Writeln('GetPlantTyppedInstance (String): End');
  Writeln('');
end;

class function TPlantFactory.InternalGetPlantTyppedInstance(
  ABehavior: TPhotosystesisBehavior): TPlant;
begin
  Writeln('GetPlantTyppedInstance (TPhotosystesisBehavior): ');
  Writeln('');
  Result := TPlant.Create;
  Result.PhotoBehavior := ABehavior;
  Writeln('');
  Writeln('GetPlantTyppedInstance (TPhotosystesisBehavior): Plant Created, Type: '+ABehavior.ToString);
  Writeln('');
  Writeln('GetPlantTyppedInstance (TPhotosystesisBehavior): End');
  Writeln('');
end;

class function TPlantFactory.GetPlantTyppedInstance(AType: TEnumBehavior): TPlant;
begin
  Writeln('GetPlantTyppedInstance (TEnumBehavior): ');
  Writeln('');
  Result := GetPlantTyppedInstance( GetEnumName(TypeInfo(TEnumBehavior) , Ord(AType)) );
  Writeln('GetPlantTyppedInstance (TEnumBehavior): End');
  Writeln('');
end;

{ TPhotosystesisBehavior }

function TPhotosystesisBehavior.ToString: String;
begin
  Result:= 'TPhotosystesisBehavior';
end;

begin
  TestGreenPlant;
  Writeln('--------------');
  TestOtherPlant;

  Writeln('--------------');
  Writeln('Factory: ');

  Writeln('- Green: ');

  TPlantFactory.GetPlantTyppedInstance('GreenPlant');
  TPlantFactory.GetPlantTyppedInstance(GreenPlant);
  TPlantFactory.GetPlantTyppedInstance(TGreenPhotosyntesisBehavior);

  Writeln('');

  Writeln('- Other: ');

  TPlantFactory.GetPlantTyppedInstance('OtherPlant');
  TPlantFactory.GetPlantTyppedInstance(OtherPlant);
  TPlantFactory.GetPlantTyppedInstance(TOtherPhotosynthesisBehavior);

  Readln;
end.

重要: 如果存在低级别继承或非常简单的项目,所有这些都成为样板。您必须决定是否值得拍摄

答案 1 :(得分:2)

通过将该方法转换为函数,可以轻松解决这个简单的情况:

type
  TPlant = class(TObject)
  private
    FIsGreen: Boolean;
  protected
    function DoPhotosynthesis: Boolean; virtual;
  end;

  TChildPlant = class(TPlant)
  protected
    function DoPhotosynthesis: Boolean; override;
  end;

implementation

{TPlant}

function TPlant.DoPhotosynthesis: Boolean;
begin
  Result := FIsGreen;
  if Result then
    //Do basic photosynthesis
end;

{ TChildPlant }

function TChildPlant.DoPhotosynthesis: Boolean;
begin
  Result := inherited DoPhotosynthesis;
  if Result then
    //Do specific photosynthesis
end;

但是更复杂的构造可能会受益于实现像Strategy这样的设计模式。

无论如何,您应该问自己FIsGreen是否属于TPlant类。如果可以在几个类中划分不同的行为,那么我会:net hesistate在继承链之间再引入一个类。

相关问题