你好,我正在尝试在 Delphi 中使用设计模式,但是由于找不到喜欢的 Delphi 参考资料,所以我正在将 O'Reilly C# 3.0 设计模式书中的模式转换为 Delphi。但这不是问题所在。我已经按照此书创建了代理模式,但是似乎有一些 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(subject)没问题,但我读到不推荐使用,但是(subject as 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,我想知道为什么不应该使用它?
谢谢。
我在http://delphipatterns.blog.com/2011/02/22/proxy-2/上有完整的模式实现。
Self.
。在调用Free
(或FreeAndNil
)之前不要测试 Assigned(),Free 可以处理nil
引用。始终在构造函数中调用inherited;
。是的,将结果的subject
分配给对象构造的方式是正确的。但不要尝试将接口转换为对象。如果想要调用Authenticate
,请通过接口公开它。 - David Heffernanobj=nil
,那么你可以安全地调用obj.Free
和FreeAndNil(obj)
,因为TObject.Free
会执行if Assigned(obj)
测试。你的代码是正确的,只是有点啰嗦。同样的情况也适用于Self
。这不是 Python。你可以省略Self
,它会被隐式推断出来。只有当你有一个同名的局部变量将你的字段超出作用域时才需要使用它。 - David HeffernanSelf
是可选的,除非存在歧义,更好的方式是尽量避免歧义!在调用free之前不要测试Assigned(obj)
。应使用FreeAndNil(obj)
代替。 - David Heffernan