对于我正在研究的应用程序架构中的一个特定问题,接口似乎是一个很好的解决方案.具体来说,一些"业务对象"依赖于从实际应用程序中的数据库中提取的一堆设置.让这些业务对象请求接口(通过控制反转),并让中央TDatabaseSettings对象实现这些接口,允许更好的隔离,从而更容易进行单元测试.
然而,在Delphi中,接口似乎带有一个令人不愉快的奖励:引用计数.这意味着,如果我这样做:
type
IMySettings = interface
function getMySetting: String;
end;
TDatabaseSettings = class(..., IMySettings)
//...
end;
TMyBusinessObject = class(TInterfacedObject, IMySettings)
property Settings: IMySettings read FSettings write FSettings;
end;
var
DatabaseSettings: TDatabaseSettings;
// global object (normally placed in a controller somewhere)
//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings;
// ... do something with O
O.Free;
Run Code Online (Sandbox Code Playgroud)
在最后一行(O.Free)上,我的全局DatabaseSettings对象现在也被释放,因为它的最后一个接口引用(包含在其中O)丢失了!
一种解决方案是DatabaseSettings使用接口存储"全局" 对象; 另一种解决方案是覆盖类的引用计数机制TDatabaseSettings,因此我可以继续将其DatabaseSettings作为普通对象进行管理(这与应用程序的其余部分更加一致).
总而言之,我的问题是:如何禁用特定类的接口引用计数机制?
我已经能够找到一些建议覆盖IInterface方法_AddRef和_Release类的信息(TDatabaseSettings在示例中); 有没有人这样做过?
或者你会说我不应该这样做(令人困惑?只是一个坏主意?),并找到一个不同的解决方案来解决架构问题?
非常感谢!
Too*_*the 13
好的,你可以绕过它,但问题是你是否真的想要那样.如果要使用接口,最好完全使用它们.因此,当您体验过它时,如果混合使用类和接口变量,就会遇到问题.
var
// DatabaseSettings: TDatabaseSettings;
DatabaseSettings : IMySettings;
//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings;
// ... do something with O
O.Free;
Run Code Online (Sandbox Code Playgroud)
您现在有第二个对该接口的引用,并且丢失第一个将不会释放该对象.
它也可以保持类和对象:
var
DatabaseSettings: TDatabaseSettings;
DatabaseSettingsInt : IMySettings;
Run Code Online (Sandbox Code Playgroud)
确保在创建对象后立即设置界面.
如果你真的想要禁用引用计数,你只需要创建一个实现IInterface的TObject的新后代.我已在D2009中测试了以下示例,它的工作原理如下:
// Query Interface can stay the same because it does not depend on reference counting.
function TMyInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
constructor TMyInterfacedObject.Create;
begin
FRefCount := 1;
end;
procedure TMyInterfacedObject.FreeRef;
begin
if Self = nil then
Exit;
if InterlockedDecrement(FRefCount) = 0 then
Destroy;
end;
function TMyInterfacedObject._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TMyInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
Run Code Online (Sandbox Code Playgroud)
FreeRef只是像_Release那样降低引用数量.您可以在通常使用Free的地方使用它.
_AddRef,_Release并且_QueryInterface,事实上,要覆盖什么.但是,你应该非常清楚你正在做什么,因为这会导致内存泄漏或奇怪的,难以发现的错误.
不要从中返回TInterfacedObject,而是从其中继承TObject并实现返回1的前两个方法的自己版本.
不要从TInterfacedObject继承,而是从标准System.Generics.Defaults单元的TSingletonImplementation继承.
| 归档时间: |
|
| 查看次数: |
5284 次 |
| 最近记录: |