我感兴趣地阅读了Nick Hodges博客Why You Should Be Using Interfaces 因为我已经爱上了编码中更高层次的接口,所以我决定看看如何将它扩展到相当低的水平,并研究在VCL类中存在哪些支持。
我需要的一个常见构造是使用TStringList做一些简单的事情,例如这段代码将一个小文本文件列表加载到逗号文本字符串中:
var
MyList : TStrings;
sCommaText : string;
begin
MyList := TStringList.Create;
try
MyList.LoadFromFile( 'c:\temp\somefile.txt' );
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
finally
MyList.Free;
end;
end;
如果我可以使用MyList作为接口编写它似乎是一个很好的简化 - 它将摆脱try-finally并提高可读性:
var
MyList : IStrings;
//^^^^^^^
sCommaText : string;
begin
MyList := TStringList.Create;
MyList.LoadFromFile( 'c:\temp\somefile.txt' );
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
end;
我看不到定义的IStrings - 当然不是在Classes.pas中,尽管在线编程时有关于它的引用。它存在吗?这是一个有效的简化吗?我正在使用Delphi XE2。
答案 0 :(得分:6)
RTL / VCL中没有可以满足您需要的界面(公开与TStrings
相同的界面)。如果你想使用这样的东西,你需要自己发明它。
您可以使用这样的包装器实现它:
type
IStrings = interface
function Add(const S: string): Integer;
end;
TIStrings = class(TInterfacedObject, IStrings)
private
FStrings: TStrings;
public
constructor Create(Strings: TStrings);
destructor Destroy; override;
function Add(const S: string): Integer;
end;
constructor TIStrings.Create(Strings: TStrings);
begin
inherited Create;
FStrings := Strings;
end;
destructor TIStrings.Destroy;
begin
FStrings.Free; // don't use FreeAndNil because Nick might see this code ;-)
inherited;
end;
function TIStrings.Add(const S: string): Integer;
begin
Result := FStrings.Add(S);
end;
当然,你会将TStrings
接口的其余部分包装在真正的类中。使用这样的包装器类来执行它,这样您就可以通过访问它的实例来包装任何类型的TStrings
。
像这样使用:
var
MyList : IStrings;
....
MyList := TIStrings.Create(TStringList.Create);
您可能更愿意添加辅助函数来实际调用TIStrings.Create
的脏工作。
另请注意,生命周期可能是一个问题。您可能需要此包装器的变体,它不会接管对基础TStrings
实例的生命周期的管理。可以使用TIStrings
构造函数参数进行排列。
我自己,我认为这是一个有趣的思想实验,但并不是一个明智的方法。 TStrings
类是一个抽象类,它具有接口提供的所有好处。我认为按原样使用它并没有真正的缺点。
答案 1 :(得分:4)
由于TStrings
是一个抽象类,因此它的接口版本不会提供太多。无论如何,该接口的任何实现者肯定都是TStrings
后代,因为没有人愿意重新实现TStrings
所做的所有事情。我认为有两个原因需要TStrings
接口:
自动资源清理。您不需要TStrings
特定的界面。而是使用JCL中的ISafeGuard
接口。这是一个例子:
var
G: ISafeGuard;
MyList: TStrings;
sCommaText: string;
begin
MyList := TStrings(Guard(TStringList.Create, G));
MyList.LoadFromFile('c:\temp\somefile.txt');
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
end;
要保护应具有相同生命周期的多个对象,请使用IMultiSafeGuard
。
与外部模块互操作。这是IStrings
的用途。 Delphi使用TStringsAdapter
类实现它,当您在现有GetOleStrings
后代上调用TStrings
时会返回该类。当您有字符串列表并且需要授予对需要IStrings
或IEnumString
接口的另一个模块的访问权限时,请使用它。这些接口很笨拙,否则它们都没有提供TStrings
所做的所有事情 - 所以除非必须这样做,否则不要使用它们。
如果你正在使用的外部模块是保证将始终使用与编译模块相同的Delphi版本编译,那么你应该使用运行时包和直接传递TStrings
个后代。共享包允许两个模块使用相同的类定义,并且内存管理大大简化。