您好 我正在尝试在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的析构函数。 在创建TProtectionProxy之后,应该在逻辑中验证Authenticate('Abracadabra'),但是在调试器中FPassword在构造函数中分配时是空的吗?这个非常令人费解。但是当我关闭应用程序时,在析构函数中,密码存在? TProtectionProxy(主题)还可以,但我读过不推荐但是(主题为TProtectionProxy)由于某些原因没有编译(运算符不适用......)? 由于FSubject字段,我添加了析构函数。这可以吗? 字段变量是否可以在声明它的同一行上启动,或者我需要在构造函数中启动,就像在TProtectionProxy中一样?
我知道我在这里问的很多,但我不认识任何知道Delphi 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)
private
FSubject: TSubject;
public
function Request(): String;
destructor Destroy(); override;
end;
TProtectionProxy = 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,对吗? 我离开了Self,我想知道为什么不应该使用它?
谢谢
上有完整的模式实现答案 0 :(得分:5)
您没有说您正在使用的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的对象的指针。然后用这个(正确的)自我指针调用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, ISubject, 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允许您从接口正确地回放到对象,这是您应该非常小心使用的。
答案 1 :(得分:2)
仅针对接口变量分配新的对象实例是否合法?
我在调试中看到首先执行TProtectionProxy的构造函数,然后执行TProxy的析构函数。
如果要销毁TProxy对象,首先将主题分配为nil:
subject := TProxy.Create();
WriteLn(subject.Request());
WriteLn(subject.Request());
subject := nil;
subject := TProtectionProxy.Create();
..
创建TProtectionProxy之后,应该在逻辑中验证Authenticate('Abracadabra'),但是在调试器中,FPassword在构造函数中分配时为空?这个非常令人费解。
但是当我关闭应用程序时,在析构函数中,密码是否存在?
这是因为主题是全局变量。在调用readln:
之前,您可以将其指定为nil以手动强制对象销毁主题:=零; Readln;
TProtectionProxy(主题)还可以,但我读过不推荐但是(主题为TProtectionProxy)由于某种原因没有编译(运算符不适用......)?
由于FSubject字段,我添加了析构函数。那可以吗?
字段变量是否可以在声明它的同一行上启动,或者我需要在构造函数中启动,就像在TProtectionProxy中一样?
如果您不打算更改FPassword,可以将其声明为常量:
TProtectionProxy = class (TInterfacedObject, ISubject)
private
FSubject: TSubject;
const FPassword: String = 'Abracadabra';
public
constructor Create();
destructor Destroy(); override;
function Authenticate(supplied: String): String;
function Request(): String;
end;
不要使用Self
- 您的代码中不需要它。