oop - Delphi - Proxy Design Pattern - interface problem -


hi trying design patterns in delphi and, since couldn't find reference material in delphi, converting patterns have in o’reilly c# 3.0 design patterns book. not problem. have created proxy pattern book there concepts of delphi interfaces, constructors , destructor , general object lifetime , behavior apparently don't understand. first post code:

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)     freeandnil(self.fsubject);   inherited; end;  function tproxy.request: string; begin   if not assigned(self.fsubject) begin     writeln('subject inactive');     self.fsubject := tsubject.create();   end;   writeln('subject active');   result := 'proxy: call ' + 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) 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)     freeandnil(self.fsubject);   inherited; end;  function tprotectionproxy.request: string; begin   if not assigned(self.fsubject) begin     result := 'protection proxy: authenticate first!';   end else begin     result := 'protection proxy: call ' + self.fsubject.request();   end; end;  end. 

these interfaces , classes used in pattern. next, code uses these types:

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;                end;    except     on e:exception       writeln(e.classname, ': ', e.message);   end; end. 

is legal assign new object instance against interface variable? see in debugging constructor tprotectionproxy executed first , destructor tproxy. after tprotectionproxy created, authenticate('abracadabra') should validated in logic in debugger fpassword empty while assigned in constructor? 1 puzzling. when close application, in destructor, password present? tprotectionproxy(subject) ok read not recommended (subject tprotectionproxy) not compiling reason (operator not applicable...)? have added destructors because of fsubject field. ok? can field variable initiated on same line declared or need initiate in constructor in tprotectionproxy?

i know lot asking here don't know knows delphi oop can ask.

thank you.


this new version works me. thank help.

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)     freeandnil(fsubject);   inherited; end;  function tproxy.request: string; begin   if not assigned(fsubject) begin     writeln('subject inactive');     fsubject := tsubject.create();   end;   writeln('subject active');   result := 'proxy: call ' + 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) 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)     freeandnil(fsubject);   inherited; end;  function tprotectionproxy.request: string; begin   if not assigned(fsubject) begin     result := 'protection proxy: authenticate first!';   end else begin     result := 'protection proxy: call ' + fsubject.request();   end; end;  end. 

and program code:

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) begin         writeln(protect.authenticate('secret'));         writeln(protect.authenticate('abracadabra'));       end;       writeln(subject.request());       readln;                end;    except     on e:exception       writeln(e.classname, ': ', e.message);   end; end. 

i have removed constructors cause don't anything. , default parametherless constructors inherited tinrefacedobject, correct? have left self, hear why shouldn't used?

thank you

i have full pattern implementation on http://delphipatterns.blog.com/2011/02/22/proxy-2/

you not saying version of delphi using. code have given valid in delphi xe , produces following (correct) output there:

proxy pattern  subject inactive subject active proxy: call subject request choose left door  subject active proxy: call subject request choose left door  protection proxy: authenticate first! protection proxy: no access! protection proxy: authenticated protection proxy: call subject request choose left door 

if @ generated machine code:

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 

you can see how compiler first generates call safeintfasclass used isubject pointer pointer object implementing isubject. tprotectionproxy.authenticate being called (correct) self pointer.

if try run same code older versions of delphi, fail:

var   subject: isubject; begin ...       subject := tprotectionproxy.create();       writeln(subject.request());       writeln(tprotectionproxy(subject).authenticate('secret')); 

older versions of delphi did not support safely casting interface object. happens compiler takes value of subject variable, , calls tprotectionproxy.authenticate it.

the call succeeds because tprotectionproxy.authenticate simple static method, not virtual method, compiler generates call absolute address it. inside tprotectionproxy.authenticate, self wrong. because subject pointer different object pointer tprotectionproxy that's implementing isubject.

the correct solution older delphi versions introduce additional interface:

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) begin         writeln(protection.authenticate('secret'));         writeln(protection.authenticate('abracadabra'));       end else         writeln('iprotection not supported!');       writeln(subject.request()); 

generally speaking, should never mix object , interface based access. once got interface reference object, shouldn't keep object references (because object automatically freed whenever last interface reference goes out of scope somewhere). , though delphi xe allows correctly cast interface object, should use very carefully.


Comments

Popular posts from this blog

apache - Add omitted ? to URLs -

redirect - bbPress Forum - rewrite to wwww.mysite prohibits login -

php - How can I stop spam on my custom forum/blog? -