3

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.DoPhotosynthesisnot 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;

他の考えはありますか?

4

2 に答える 2

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 と組み合わせて、各タイプで使用する動作を決定することもできます。以下のコードには、TPlant のインスタンスを取得するためのオーバーロードされた関数が 3 つあります。これらすべてが必要なわけではありません。ここでは、デモンストレーションのみを目的としています。

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.

重要: 低レベルの継承がある場合、または非常に単純なプロジェクトでは、これらすべてがボイラープレートになります。ショットの価値があるかどうかを判断する必要があります

于 2013-10-30T14:19:39.147 に答える