Documentção: http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Rtti.TVirtualMethodInterceptor
Talvez você nunca tenha ouvido falar, mas o Delphi possui a classe TVirtualMethodInterceptor. Ela exite para ser algo parecido com que o .Net e Java possuem à respeito de proxies dinâmicos.
Considere o código abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, RTTI; type TTeste = class function Msg: string; virtual; end; { TTeste } function TTeste.Msg: string; begin Result := 'Mensagem de teste!'; end; var Teste: TTeste; I: TVirtualMethodInterceptor; begin try Teste := TTeste.Create; try WriteLn('Mensagem da classe TTeste: ' + Teste.Msg); WriteLn; I := TVirtualMethodInterceptor.Create(Teste.ClassType); I.OnBefore := procedure(Instance: TObject; Method: TRttiMethod; const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue) begin DoInvoke := False; Result := 'Retorno da mensagem foi interceptada!'; WriteLn; WriteLn('Essa é uma mensagem da interceptação do método! Classe ' + Instance.ClassName + ', Método: ' + Method.ToString); WriteLn; end ; I.Proxify(Teste); WriteLn('Mensagem da classe TTeste: ' + Teste.Msg); finally Teste.DisposeOf; end; ReadLn; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
Como pode ver, a classe TVirtualMethodInterceptor intercepta a execução do método “TTeste.Msg” e modifica o seu retorno. A interceptação é da chamada, e não do funcionamento interno do método.
Veja que isso funciona porque o método TTeste.Msg é virtual. Quando você executar esse código, perceberá que mais de uma execução da interceptação ocorre, porque no momento de destruir a classe, outros métodos virtuais da classe são chamados. Todavia, isso pode ser evitado simplesmente tratando quando executar pelo parâmetro “Method”:
1 2 3 4 5 6 7 8 9 10 11 |
I.OnBefore := procedure(Instance: TObject; Method: TRttiMethod; const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue) begin if Method.Name = 'Msg' then begin DoInvoke := False; Result := 'Retorno da mensagem foi interceptada!'; WriteLn; WriteLn('Essa é uma mensagem da interceptação do método! Classe ' + Instance.ClassName + ', Método: ' + Method.Name); WriteLn; end; end; |
O evento “OnBefore” possui 5 parâmetros:
- Instance – Ponteiro para a instância do método que está sendo executado.
- Method – Método que está sendo executado.
- Args – Parâmetros do método que está sendo executado.
- DoInvoke – Determina se o método será chamado ou não.
- Result – Define o resultado do método.
Todos esses parâmetros são poderosos, mas chamo a atenção para os dois últimos. Com eles, é possível impedir a execução do método original e retornar um determinado valor. Assim, quem chamou o método não percebe, mas na verdade o método original não foi executado, sendo interceptado e tendo outro valor retornardo, continuando com o fluxo da aplicação.
Também é possível implementar ações para executarem após a execução do método, através de “OnAfter“:
- Instance – Ponteiro para a instância do método que está sendo executado.
- Method – Método que está sendo executado.
- Args – Parâmetros do método que está sendo executado.
- Result – Define o resultado do método.
Por se tratar de um evento que é executado depois da execução do método, ele não possui a capacidade de impedir a execução, como o evento “OnBefore”.
De qualquer forma, ele ainda pode ser útil para implementação de algumas funcionalidades, como logs, por exemplo.
Conclusão
A utilização de proxies dinâmicos é muito poderoso, porque dá a capacidade de interferir no fluxo normal do sistema, interceptando a chamada do método e alterando os resultados. Além disso, dá a possibilidade de gerar eventos para esse método, onde funcionalidades adjacentes podem ser desenvolvidas.
Show de bola… não conhecia…!!