加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Delphi – 代理设计模式 – 接口问题

发布时间:2020-12-15 09:31:03 所属栏目:大数据 来源:网络整理
导读:你好 我正在尝试在Delphi中设计模式,因为我在Delphi中找不到我喜欢的参考资料,所以我正在转换O’Reilly C#3.0 Design Patterns一书中的模式.但这不是问题.我已经从本书中创建了代理模式,但是有一些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的析构函数.
在创建TProtectionProxy之后,应该在逻辑中验证Authenticate(‘Abracadabra’),但是在调试器中FPassword在构造函数中分配时是空的吗?这个非常令人费解.但是当我关闭应用程序时,在析构函数中,密码存在?
TProtectionProxy(主题)还可以,但我读过不推荐但是(主题为TProtectionProxy)由于某些原因没有编译(运算符不适用……)?
由于FSubject字段,我添加了析构函数.这可以吗?
字段变量是否可以在声明它的同一行上启动,或者我需要在TProtectionProxy中的构造函数中启动?

我知道我在这里问的很多,但我不知道有谁知道德尔福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,对吗?
我离开了Self,我想知道为什么不应该使用它?

谢谢

我在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允许您正确地从接口转换回对象,这是您应该非常小心地使用的东西.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读