Delphi – 代理设计模式 – 接口问题
你好
我正在尝试在Delphi中设计模式,因为我在Delphi中找不到我喜欢的参考资料,所以我正在转换O’Reilly C#3.0 Design Patterns一书中的模式.但这不是问题.我已经从本书中创建了代理模式,但是有一些Delphi接口,构造函数和析构函数以及一般对象生命周期和行为的概念,我显然不明白. 首先,我将发布我的代码: unit Unit2; interface uses SysUtils; type ISubject = interface ['{78E26A3C-A657-4327-93CB-F3EB175AF85A}'] function Request(): string; end; TSubject = class public function Request(): string; constructor Create(); end; TProxy = class (TInterfacedObject,ISubject) private FSubject: TSubject; public function Request(): String; destructor Destroy(); override; end; TProtectionProxy = class (TInterfacedObject,ISubject) private FSubject: TSubject; FPassword: String; public constructor Create(); destructor Destroy(); override; function Authenticate(supplied: String): String; function Request(): String; end; implementation { TSubjectAccessor.TProxy } destructor TProxy.Destroy; begin if Assigned(Self.FSubject) then FreeAndNil(Self.FSubject); inherited; end; function TProxy.Request: String; begin if not Assigned(Self.FSubject) then begin WriteLn('Subject Inactive'); Self.FSubject := TSubject.Create(); end; WriteLn('Subject active'); Result := 'Proxy: Call to ' + Self.FSubject.Request(); end; { TSubject } constructor TSubject.Create; begin inherited; end; function TSubject.Request: string; begin Result := 'Subject Request Choose left door' + #10; end; { TProtectionProxy } function TProtectionProxy.Authenticate(supplied: String): String; begin if (supplied <> Self.FPassword) then begin Result := 'Protection proxy: No Access!'; end else begin Self.FSubject := TSubject.Create(); Result := 'Protection Proxy: Authenticated'; end; end; constructor TProtectionProxy.Create; begin Self.FPassword := 'Abracadabra'; end; destructor TProtectionProxy.Destroy; begin if Assigned(Self.FSubject) then FreeAndNil(Self.FSubject); inherited; end; function TProtectionProxy.Request: String; begin if not Assigned(Self.FSubject) then begin Result := 'Protection Proxy: Authenticate first!'; end else begin Result := 'Protection Proxy: Call to ' + Self.FSubject.Request(); end; end; end. 这些是模式中使用的接口和类.接下来,是使用这些类型的代码: program Structural.Proxy.Pattern; {$APPTYPE CONSOLE} uses SysUtils,Unit2 in 'Unit2.pas'; var subject: ISubject; begin ReportMemoryLeaksOnShutdown := DebugHook <> 0; try WriteLn('Proxy Pattern' + #10); try subject := TProxy.Create(); WriteLn(subject.Request()); WriteLn(subject.Request()); subject := TProtectionProxy.Create(); WriteLn(subject.Request()); WriteLn(TProtectionProxy(subject).Authenticate('Secret')); WriteLn(TProtectionProxy(subject).Authenticate('Abracadabra')); WriteLn(subject.Request()); ReadLn; finally end; except on E:Exception do Writeln(E.Classname,': ',E.Message); end; end. 仅针对接口变量分配新的对象实例是否合法?我在调试中看到首先执行TProtectionProxy的构造函数,然后执行TProxy的析构函数. 我知道我在这里问的很多,但我不知道有谁知道德尔福OOP这么好,我可以问. 谢谢. 这是适合我的新版本.谢谢你的帮助. unit Unit2; interface uses SysUtils; type ISubject = interface ['{78E26A3C-A657-4327-93CB-F3EB175AF85A}'] function Request(): string; end; IProtected = interface ['{928BA576-0D8D-47FE-9301-DA3D8F9639AF}'] function Authenticate(supplied: string): String; end; TSubject = class public function Request(): string; end; TProxy = class (TInterfacedObject,ISubject,IProtected) private FSubject: TSubject; const FPassword: String = 'Abracadabra'; public destructor Destroy(); override; function Authenticate(supplied: String): String; function Request(): String; end; implementation { TSubjectAccessor.TProxy } destructor TProxy.Destroy; begin if Assigned(FSubject) then FreeAndNil(FSubject); inherited; end; function TProxy.Request: String; begin if not Assigned(FSubject) then begin WriteLn('Subject Inactive'); FSubject := TSubject.Create(); end; WriteLn('Subject active'); Result := 'Proxy: Call to ' + FSubject.Request(); end; { TSubject } function TSubject.Request: string; begin Result := 'Subject Request Choose left door' + #10; end; { TProtectionProxy } function TProtectionProxy.Authenticate(supplied: String): String; begin if (supplied <> FPassword) then begin Result := 'Protection proxy: No Access!'; end else begin FSubject := TSubject.Create(); Result := 'Protection Proxy: Authenticated'; end; end; destructor TProtectionProxy.Destroy; begin if Assigned(FSubject) then FreeAndNil(FSubject); inherited; end; function TProtectionProxy.Request: String; begin if not Assigned(FSubject) then begin Result := 'Protection Proxy: Authenticate first!'; end else begin Result := 'Protection Proxy: Call to ' + FSubject.Request(); end; end; end. 和程序代码: program Structural.Proxy.Pattern; {$APPTYPE CONSOLE} uses SysUtils,Unit2 in 'Unit2.pas'; var subject: ISubject; protect: IProtected; begin ReportMemoryLeaksOnShutdown := DebugHook <> 0; try WriteLn('Proxy Pattern' + #10); try subject := TProxy.Create(); WriteLn(subject.Request()); WriteLn(subject.Request()); subject := nil; subject := TProtectionProxy.Create(); WriteLn(subject.Request()); if Supports(subject,IProtected,protect) then begin WriteLn(protect.Authenticate('Secret')); WriteLn(protect.Authenticate('Abracadabra')); end; WriteLn(subject.Request()); ReadLn; finally end; except on E:Exception do Writeln(E.Classname,E.Message); end; end. 我已经删除了所有构造函数,因为它们现在确实没有做任何事情.默认的无参数构造函数继承自TInrefacedObject,对吗? 谢谢 我在http://delphipatterns.blog.com/2011/02/22/proxy-2/上有完整的模式实现 解决方法
你不是说你正在使用什么版本的Delphi.您提供的代码仅在Delphi XE中有效,并在那里生成以下(正确)输出:
Proxy Pattern Subject Inactive Subject active Proxy: Call to Subject Request Choose left door Subject active Proxy: Call to Subject Request Choose left door Protection Proxy: Authenticate first! Protection proxy: No Access! Protection Proxy: Authenticated Protection Proxy: Call to Subject Request Choose left door 如果查看生成的机器代码: Project2.dpr.25: WriteLn(TProtectionProxy(subject).Authenticate('Secret')); 004122C2 A1788E4100 mov eax,[$00418e78] 004122C7 8B154CF84000 mov edx,[$0040f84c] 004122CD E8E22BFFFF call @SafeIntfAsClass 004122D2 8D4DE0 lea ecx,[ebp-$20] 004122D5 BA38244100 mov edx,$00412438 004122DA E875D9FFFF call TProtectionProxy.Authenticate 004122DF 8B55E0 mov edx,[ebp-$20] 004122E2 A1EC3C4100 mov eax,[$00413cec] 004122E7 E8BC24FFFF call @Write0UString 004122EC E82F25FFFF call @WriteLn 004122F1 E82A1CFFFF call @_IOTest 您可以看到编译器如何首先生成对SafeIntfAsClass的调用,该调用用于从ISubject指针获取指向正在实现ISubject的对象的指针.然后用这个(正确的)Self指针调用TProtectionProxy.Authenticate. 如果您尝试使用旧版本的Delphi运行相同的代码,则会失败: var subject: ISubject; begin ... subject := TProtectionProxy.Create(); WriteLn(subject.Request()); WriteLn(TProtectionProxy(subject).Authenticate('Secret')); 较旧版本的Delphi不支持从接口安全地转换回对象.然后发生的是编译器只是获取主题变量的值,并用它调用TProtectionProxy.Authenticate. 调用本身成功,因为TProtectionProxy.Authenticate是一个简单的静态方法,而不是虚方法,所以编译器只是为它生成一个绝对地址的调用.但是在TProtectionProxy.Authenticate中,Self是错误的.因为主题指针不同于正在实现ISubject的TProtectionProxy的对象指针. 旧版delphi版本的正确解决方案是引入一个额外的接口: type IProtection = interface ['{ACA182BF-7675-4346-BDE4-9D47CA4ADBCA}'] function Authenticate(supplied: String): String; end; ... TProtectionProxy = class (TInterfacedObject,IProtection) ... var subject: ISubject; protection: IProtection; ... subject := TProtectionProxy.Create(); WriteLn(subject.Request()); if Supports(subject,IProtection,protection) then begin WriteLn(protection.Authenticate('Secret')); WriteLn(protection.Authenticate('Abracadabra')); end else WriteLn('IProtection not supported!'); WriteLn(subject.Request()); 一般来说,您不应该混合基于对象和接口的访问.一旦你获得了一个对象的接口引用,你就不应该保留对它的任何对象引用(因为只要最后一个接口引用超出了某个范围,对象就会自动释放).即使Delphi XE允许您正确地从接口转换回对象,这是您应该非常小心地使用的东西. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |