Aplicando a RTTI para serializar informações
Neste artigo veremos inicialmente um pouco sobre a base e conceitos da arquitetura da RTTI e, em seguida, aplicaremos alguns exemplos práticos, demonstrando como é simples obter todo o poder e versatilidade da RTTI no Delphi.
A reflexão computacional (ou somente reflexão) é um tema muito antigo e consiste na capacidade de um sistema observar e interagir sobre o seu próprio comportamento. Através do conceito de reflexão, é possível obter informações sobre a estrutura e estado de um objeto de forma flexível, possibilitando o seu tratamento genérico.
É possível através de reflexão alterar o valor de um atributo ou até mesmo invocar um método de um determinado objeto sem que seja feita uma menção direta a sua instancia, como é feito tradicionalmente.
Em linguagens que não utilizam o paradigma reflexivo, no momento da compilação e geração do código de baixo nível (geralmente Assembly), as informações sobre a estrutura dos dados e objetos se perdem, porém, em uma linguagem com suporte a reflexão, essas informações são armazenadas e mantidas em tabelas de Metadados, fazendo com que seja possível obter informações sobre esta estrutura posteriormente, em tempo de execução.
A grande maioria das linguagens já possui suporte a reflexão e já fazem uso desta internamente, porém, o uso deste conceito por desenvolvedores é algo um pouco mais recente.
Outro exemplo a ser citado é a criação do Test Patterns xUnit, o padrão de projetos base para a criação do framework JUnit (BOX 1).
Quando é necessário criar rotinas de serialização de dados, mapeamento de dados de forma dinâmica e criação de frameworks genéricos, de forma a reduzir a duplicidade de código e facilitar a manutenção do mesmo.
BOX 1 - JUnit
O JUnit foi criado por Kent Beck e Erich Gamma, baseados no SUnit que é um framework de automação de testes unitários para Small Talk, criado por Kent Beck. O JUnit, assim como o SUnit, é utilizado para criação de testes unitários automatizados em Java que posteriormente serviu de base para a criação do DUnit, sua versão para Delphi. O padrão xUnit utiliza o conceito de reflexão computacional para generalizar, publicar e executar os métodos de testes unitários criados pelo desenvolvedor.
No Delphi, é possível tirar proveito do conceito de Reflexão Computacional através da Run-Time Type Information, ou como é mais conhecida, RTTI.
É através da RTTI que o próprio Delphi consegue em tempo de execução extrair e manipular informações de classes, objetos, componentes e etc. Um exemplo claro de uso da RTTI é a forma como o próprio IDE do Delphi lista as propriedades e eventos de um determinado componente no Object Inspector, que inclusive, foi o principal motivo que fez com que o Delphi passasse a ter suporte a reflexão computacional ainda em sua primeira versão. O mecanismo que torna possível a conversão dos dados armazenados em um arquivo .dfm em objetos, é a RTTI. Além destes, podemos citar ainda a forma como o framework DataSnap publica suas classes remotas e trafega informações entre o servidor e os clientes.
As versões mais recentes do DataSnap trafegam dados no formato texto em notação JSON. Apesar da possibilidade de camuflar esse tráfego em uma comunicação Delphi 2 Delphi (BOX 2), internamente, ocorre uma série de tratamentos utilizando a RTTI, o que viabiliza a comunicação entre as camadas envolvidas no processo. Ainda abordando o multicamadas, quando se está trabalhando com um servidor REST e clientes em outras linguagens, como JavaScript por exemplo, essa questão se torna evidente, devido ao fato do encapsulamento dos dados trafegados não serem camuflados.
BOX 2 – Delphi 2 Delphi
A forma mais popular de se trabalhar com o DataSnap é através de uma comunicação Delphi para Delphi, onde, no lado cliente, é utilizado o componente TSQLConnection do dbExpress para se criar uma comunicação com um servidor DataSnap. Por meio dessa conexão criada pelo componente TSQLConnection, é possível dentre outras coisas criar chamadas na aplicação cliente, que utilizam do Proxy para realizar as chamadas nos métodos remotos do servidor DataSnap. Para isso, basta utilizar-se da opção Generate DataSnap Client Classes no menu de contexto do componente TSQLConnection, quando este estiver com uma conexão DataSnap estabelecida.
Até a versão 2009 do Delphi, os recursos disponíveis da RTTI para o desenvolvedor eram extremamente limitados e de uso complexo. Porém, com o lançamento da versão 2010, a RTTI passou por significativas melhorias e seu uso passou a ser consideravelmente mais simples, fazendo com que o conceito viesse a se tornar gradativamente mais popular entre os desenvolvedores, ainda que a passos curtos.
Apesar dessa reformulação e da ainda pequena popularização da RTTI, o assunto continua extremamente teórico e com poucas aplicações práticas.
Inicialmente veremos um pouco sobre a base e conceitos da arquitetura da RTTI e, em seguida, aplicaremos alguns exemplos práticos, demonstrando como é simples obter todo o poder e versatilidade da RTTI no Delphi.
O funcionamento da nova RTTI do Delphi está totalmente vinculada ao uso da unit RTTI.pas. Nessa unit estão localizados os tipos e principais métodos utilizados para tirar proveito de seus recursos. Dentre esses tipos, vale o destaque para as seguintes estruturas:
· TRttiContext - É o elemento chave do funcionamento da RTTI. O TRttiContext é utilizando para acessar as informações dos tipos públicos de uma aplicação e seus pacotes associados.
· TRttiType - É a classe base para todos os tipos que podem ser acessados pela RTTI. Através dela é possível expor métodos e atributos de um determinado tipo, capturado pelo TRttiContext.
· TValue - Consiste em uma estrutura de armazenamento de dados genérica, largamente utilizada nas rotinas da RTTI. Geralmente as instâncias de objetos, ou o conteúdo de variáveis e estruturas em RTTI são armazenados em estruturas do tipo TValue.
A partir dessas estruturas e outras auxiliares é possível tirar proveito de todo potencial da RTTI, como veremos a seguir.
O fluxo básico de funcionamento da RTTI no Delphi é o seguinte: Primeiramente é criado um contexto RTTI através do objeto TRttiContext, a seguir, são recuperadas informações sobre uma determinada estrutura, (seja uma classe ou um record) e estas são armazenadas em uma estrutura do tipo TRttiType. Essa extração de informações é feita também através de TRttiContext. A partir do da classe TRttiType é possível obter informações sobre campos (TRttiField), propriedades (TRttiProperty), métodos (TRttiMethod) e etc. No caso de campos e propriedades, é possível extrair informações sobre o tipo de dado que é armazenado, o valor que está armazenado neste campo em uma determinada instancia, o nome do campo, se ele possui algum Custom Attribute, a visibilidade deste campo dentro do encapsulamento ("