接口委派+覆盖

时间:2014-06-27 14:41:02

标签: delphi oop interface delegation

由于Delphi中缺少多重继承,我需要使用接口委派。这对我来说是一个非常新的主题,我在将重写与接口委派相结合时遇到了问题。

班级TMyNode必须从TBaseClass继承,并且需要实施IAddedStuff。我想在IAddedStuff中使用TAddedStuffDefaultImplementation的所有函数的默认实现,因此我不需要在任何地方都有重复的getter / setter代码。所以,我已经使用DefaultBehavior委派了这些内容。

问题是,TAddedStuffDefaultImplementation意味着拥有虚拟方法,所以我想直接在TMyNode中覆盖它们。如果我写FDefaultImplementation: TAddedStuffDefaultImplementation;而不是FDefaultImplementation: IAddedStuff;,这确实有用。

但是现在,由于某些原因,TAddedStuffDefaultImplementation会增加x: TBaseClass;的参考计数器,因此无法释放它。我该怎么办?

我的简化复制代码如下:

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  IAddedStuff = interface(IInterface)
  ['{9D5B00D0-E317-41A7-8CC7-3934DF785A39}']
    function GetCaption: string; {virtual;}
  end;

  TAddedStuffDefaultImplementation = class(TInterfacedObject, IAddedStuff)
    function GetCaption: string; virtual;
  end;

  TBaseClass = class(TInterfacedObject);

  TMyNode = class(TBaseClass, IAddedStuff)
  private
    FDefaultImplementation: TAddedStuffDefaultImplementation;
  public
    property DefaultBehavior: TAddedStuffDefaultImplementation read FDefaultImplementation
      write FDefaultImplementation implements IAddedStuff;
    destructor Destroy; override;

    // -- IAddedStuff
    // Here are some functions which I want to "override" in TMyNode.
    // All functions not declared here, should be taken from FDefaultImplementation .
    function GetCaption: string; {override;}
  end;

{ TAddedStuffDefaultImplementation }

function TAddedStuffDefaultImplementation.GetCaption: string;
begin
  result := 'PROBLEM: CAPTION NOT OVERRIDDEN';
end;

{ TMyNode }

destructor TMyNode.Destroy;
begin
  if Assigned(FDefaultImplementation) then
  begin
    FDefaultImplementation.Free;
    FDefaultImplementation := nil;
  end;

  inherited;
end;

function TMyNode.GetCaption: string;
begin
  Result := 'OK: Caption overridden';
end;

var
  x: TBaseClass;
  gn: IAddedStuff;
  s: string;
begin
  x := TMyNode.Create;
  try
    TMyNode(x).DefaultBehavior := TAddedStuffDefaultImplementation.Create;
    Assert(Supports(x, IAddedStuff, gn));
    WriteLn(gn.GetCaption);
  finally
    WriteLn('RefCount = ', x.RefCount);
    // x.Free; // <-- FREE fails since FRefCount is 1
  end;
  ReadLn(s);
end.

1 个答案:

答案 0 :(得分:5)

如果您委派IAddedStuff,那么您还应该在另一个类上实现非默认行为,并通过构造函数注入传递它。

此外,如果要混合对象和接口引用,请确保引用计数不会发生冲突。使用接口委派时,容器对象的引用会发生变化。

program Project1;

{$APPTYPE CONSOLE}

uses
  Classes,
  SysUtils;

type
  IAddedStuff = interface(IInterface)
  ['{9D5B00D0-E317-41A7-8CC7-3934DF785A39}']
    function GetCaption: string; {virtual;}
  end;

  TAddedStuffDefaultImplementation = class(TInterfacedObject, IAddedStuff)
    function GetCaption: string; virtual;
  end;

  TAddedStuffOverriddenImplementation = class(TAddedStuffDefaultImplementation)
    function GetCaption: string; override;
  end;

  TBaseClass = class(TInterfacedPersistent);

  TMyNode = class(TBaseClass, IAddedStuff)
  private
    FAddedStuff: IAddedStuff;
    property AddedStuff: IAddedStuff read FAddedStuff implements IAddedStuff;
  public
    constructor Create(const addedStuff: IAddedStuff);
  end;

{ TAddedStuffDefaultImplementation }

function TAddedStuffDefaultImplementation.GetCaption: string;
begin
  result := 'PROBLEM: CAPTION NOT OVERRIDDEN';
end;

{ TAddedStuffOverriddenImplementation }

function TAddedStuffOverriddenImplementation.GetCaption: string;
begin
  Result := 'OK: Caption overridden';
end;

{ TMyNode }

constructor TMyNode.Create;
begin
  FAddedStuff := addedStuff;
end;

var
  x: TBaseClass;
  gn: IAddedStuff;
begin
  x := TMyNode.Create(TAddedStuffOverriddenImplementation.Create);
  try
    Assert(Supports(x, IAddedStuff, gn));
    WriteLn(gn.GetCaption);
  finally
    x.Free;
  end;
  Readln;
  ReportMemoryLeaksOnShutdown := True;
end.

修改

在评论中讨论后,我建议如下:

program Project1;

{$APPTYPE CONSOLE}

uses
  Classes,
  SysUtils;

type
  IAddedStuff = interface(IInterface)
  ['{9D5B00D0-E317-41A7-8CC7-3934DF785A39}']
    function GetCaption: string;
  end;

  TAddedStuffDefaultImplementation = class(TInterfacedObject, IAddedStuff)
    function GetCaption: string; virtual;
  end;

  TBaseClass = class(TInterfacedPersistent);

  TMyNode = class(TBaseClass, IAddedStuff)
  private
    FAddedStuff: IAddedStuff;
    property AddedStuff: IAddedStuff read FAddedStuff implements IAddedStuff;
  public
    constructor Create;
  end;

  TAddedStuffOverriddenImplementation = class(TAddedStuffDefaultImplementation)
  private
    FMyNode: TMyNode;
  public
    constructor Create(AMyNode: TMyNode);
    function GetCaption: string; override;
  end;

{ TAddedStuffDefaultImplementation }

function TAddedStuffDefaultImplementation.GetCaption: string;
begin
  result := 'PROBLEM: CAPTION NOT OVERRIDDEN';
end;

{ TMyNode }

constructor TMyNode.Create;
begin
  FAddedStuff := TAddedStuffOverriddenImplementation.Create(Self);
end;

{ TAddedStuffOverriddenImplementation }

constructor TAddedStuffOverriddenImplementation.Create(AMyNode: TMyNode);
begin
  FMyNode := AMyNode;
end;

function TAddedStuffOverriddenImplementation.GetCaption: string;
begin
  Result := 'OK: Caption overridden';
end;


var
  x: TBaseClass;
  gn: IAddedStuff;
begin
  x := TMyNode.Create;
  try
    Assert(Supports(x, IAddedStuff, gn));
    WriteLn(gn.GetCaption);
  finally
    x.Free;
  end;
  ReadLn;
  ReportMemoryLeaksOnShutdown := True;
end.