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
Post a Comment