Delphi中带有继承的流畅接口

4
我有以下流畅接口声明和实现该接口的类:
type
  IDocWriter = interface
    ['{8CB5799A-14B1-4287-92FD-41561B237560}']
    function Open: IDocWriter;
    function Close: IDocWriter;
    function Add(const s: string): IDocWriter;
    function SaveToStream(Stream: TStream): IDocWriter;
  end;

  TDocWriter = class(TInterfacedObject, IDocWriter)
  public
    function Open: IDocWriter;
    function Close: IDocWriter;
    function Add(const s: string): IDocWriter;
    function SaveToStream(Stream: TStream): IDocWriter;
  end;

{ TDocWriter }

function TDocWriter.Open: IDocWriter;
begin
  Result := Self;
  // DoOpen
end;

function TDocWriter.Close: IDocWriter;
begin
  Result := Self;
  // DoClose
end;

function TDocWriter.Add(const s: string): IDocWriter;
begin
  Result := Self;
  // DoAdd
end;

function TDocWriter.SaveToStream(Stream: TStream): IDocWriter;
begin
  Result := Self;
  // DoSaveToStream
end;

我可以像这样使用上述代码:
var
  Stream: TStream;
  ...
  TDocWriter.Create
    .Open
    .Add('abc')
    .Close
    .SaveToStream(Stream);

我必须通过添加 SaveToString 函数来扩展上述接口。
我不想将该方法添加到原始的 IDocWriter 接口中,因为它并不适用于所有接口实现。所以我做了以下操作。
type
  IStrDocWriter = interface(IDocWriter)
    ['{177A0D1A-156A-4606-B594-E6D20818CE51}']
    function SaveToString: string;
  end;

  TStrDocWriter = class(TDocWriter, IStrDocWriter)
  public
    function SaveToString: string;
  end;

{ TStrDocWriter }

function TStrDocWriter.SaveToString: string;
begin
  Result := 'DoSaveToString';
end;

为了使用 IStrDocWriter 接口,我必须编写代码。
var
  Writer: IDocWriter;
  s: string;

  Writer := TStrDocWriter.Create
    .Open
    .Add('abc')
    .Close;
  s := (Writer as IStrDocWriter).SaveToString;

但我希望能够在不声明 Writer 变量的情况下使用它,就像以下代码一样(当然,这段代码无法编译)
  s := TStrDocWriter.Create
    .Open
    .Add('abc')
    .Close
    .SaveToString;   // Undeclared identifier SaveToString

有没有任何方法可以实现这个目标?
对上述接口和类进行任何类型的更改都是可以接受的(除了将这两个接口合并成一个之外)。

1
整个设计在我看来都是错的。Fluent太糟糕了! - David Heffernan
请原谅我的好奇心,但这个接口是否需要流畅性是自我强加的,还是源于实际业务需求?如果是后者,具体是什么需求呢? - MartynA
1
@MartynA 这是有原因的自我造成。接口是因为它是引用计数的,消除了try..finally块的需要。流畅是因为它消除了声明变量的需要,并且在您拥有较长链时使代码更易读。它可以在创建某些文档或报告时内联使用,否则您通常会遇到混乱的代码。想象一下TTextDocWriter、TRichTextDocWriter、TPdfDocWriter、TMetafileDocWriter... - Dalija Prasnikar
1个回答

4
您可以这样编写代码:

您可以这样编写:

s := (TStrDocWriter.Create
    .Open
    .Add('abc')
    .Close as IStrDocWriter)
    .SaveToString; 

不,这并不太好。这两个俗语,流利的接口和继承,并不能很好地混合在一起。

看起来对预期目的足够好。我知道流畅不支持继承,但是... - Dalija Prasnikar
3
看起来这是为什么我们需要 Delphi 中的接口助手的一个很好的例子。 - Stefan Glienke
@Stefan,你知道接口助手是否有QC/QP报告吗? - Dalija Prasnikar
1
@Dalija,根据Marco Cantù的说法,他们正在关注uservoice,并且有一个关于此功能的请求:https://delphi.uservoice.com/forums/4433-language-features/suggestions/1345443--class-helper-for-interfaces。 - Stefan Glienke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接