Artigo no estilo: Curso

Do que trata o artigo

Este artigo ensina como criar frameworks para persistência de dados através da nova RTTI. Aborda conceitos de encapsulamento e polimorfismo, além de mostrar a implementação dos padrões Command, Singleton e Factory com exemplos práticos.


Para que serve

Um programa orientado a objetos precisa converter as informações do banco de dados em objetos antes de manipulá-las. Aprendendo como fazer isto através de frameworks e padrões de projetos você está garantindo uma vantagem em relação à concorrência.


Em que situação o tema é útil

Segundo o que este artigo propõe, você terá um sistema muito fácil de manusear, capaz de atender as necessidades que o dia-a-dia impõe. As técnicas aqui apresentadas serão úteis, por exemplo, para mostrar como usar a nova RTTI no Delphi em um exemplo real, além de fortalecer importantes regras da OO, como separação de responsabilidades e implementação correta de padrões de projeto.

Resumo do DevMan

Se você leu a segunda parte desta série, você aprendeu o quanto é importante criar classes que tenham apenas uma única responsabilidade. Também aprendeu a identificar as responsabilidades de uma classe e como fazer para separá-las. Agora está na hora de criar o banco de dados da aplicação. É claro que o resultado deve ser um código flexível o suficiente para que o SGBD possa ser alterado com pouco esforço.

No último artigo desta série o agendador sofreu grandes modificações. O comportamento do sistema continua o mesmo, pois nenhuma nova função foi adicionada. Porém internamente muita coisa mudou. Em outras palavras, o agendador continua fazendo a mesma coisa, mas de maneira diferente. O principal problema que foi corrigido na segunda versão, era o acúmulo de responsabilidades sobre a classe TForm1, pois esta classe possuía todas a regras de negócios da aplicação. Após identificar este problema, o que eu fiz, foi separar estas responsabilidades em mais classes e o design do sistema ficou como o apresentado na Figura 1.

Figura 1. Design atual do sistema

Uma vez que a maioria dos problemas de engenharia foi corrigida, está na hora de implementar uma das funções mais importantes: salvar as informações no banco de dados. Até agora o aplicativo não persiste as informações, ao invés disto, cada tarefa é adicionada a uma variável do tipo TList e é claro que quando o sistema é fechado esta lista é perdida. Agora que o sistema já está mais maduro é possível implementar a persistência dos objetos.

O objetivo da alteração

É preciso retirar da classe TForm1 toda a responsabilidade referente ao ato de salvar informações. Como você pode ver na Listagem 1, o código para salvar uma tarefa é muito simples, basta adicionar a nova tarefa na variável utilizada como banco de dados temporário da aplicação – veja linha 11 da Listagem 1. Normalmente é necessário mais código para salvar um registro como, por exemplo, abrir uma conexão, criar a tabela caso não exista, verificar se já existe um registro com o mesmo nome e somente depois salvar a tarefa. O que atualmente no agendador é representado apenas por uma linha, na vida real seriam várias linhas de códigos. O objetivo é separar toda essa lógica em classes que sejam responsáveis exclusivamente por persistir os objetos.

Listagem 1. Código para criar e adicionar uma nova tarefa


  1   procedure TForm1.SalvarTarefaClick(Sender: Tobject);
  2   var
  3     Tarefa: Ttarefa;
  4   begin
  5     Tarefa := Ttarefa.Create;
  6     Tarefa.Comando := Comando.Text;
  7     Tarefa.Nome := Nome.Text;
  8     Tarefa.Parametros := Parametros.Text;
  9     Tarefa.IniciarEm := IniciarEm.Text;
  10    Tarefa.Comentario := Comentarios.Text;
      
  11    Lista.Add(Tarefa);
      
  12    MostrarTarefas;
  13  end; 

Quando eu digo persistir os objetos, me refiro à ação de salvar as informações de todas as propriedades de um objeto no banco de dados e quando necessário for, buscar e colocar essas informações de volta em um objeto do mesmo tipo. Fazer isto de forma manual é possível, mas muito cansativo e o resultado não será bom, pois cada objeto possui diferentes propriedades. Por este motivo é óbvio que isso deve ser feito de modo automático.

Isto cria um grande problema: como obter as informações das propriedades de um objeto sem saber quais são as propriedades dele? Como alterar o valor da propriedade de um objeto se eu não sei nem de qual objeto eu estou falando?

A solução para isso é RTTI (Run-time Type Information). Você provavelmente já sabe que o Delphi 2010 trouxe uma nova RTTI cheia de facilidades e novos recursos (veja sessão Links). É com ela que os problemas apresentados no último parágrafo serão resolvidos. É claro que você também consegue o mesmo resultado com a antiga RTTI do Delphi, porém neste artigo você vai aprender a montar um mini-framework de persistência usando a nova RTTI.

Antes de inserir um registro

Para inserir um registro em uma tabela, primeiro você precisa que a tabela exista, portanto, isto significa que será necessário que o framework crie a tabela automaticamente caso ela ainda não exista.

No caso do agendador as informações serão persistidas em XML, mas como você sabe este é apenas um detalhe. A única diferença para programas que usam banco de dados relacionais é que você terá que digitar um pouco de código SQL em seu framework – ou talvez não, depende do nível de abstração que você for usar.

Veja na Figura 2 como o framework é simples. A classe TBean mostrada representa as classes que serão persistidas pelo framework. Mais do que isso, o framework só vai persistir objetos das classes que herdem a classe TBean, que é abstrata. Ela é abstrata justamente porque a ideia é que você a estenda nas classes que serão persistidas pelo framework. No caso do agendador serão persistidas as classes TTarefa e todas as filhas de TAgendamento.

Figura 2. Design do framework

A classe TBean possui algumas características importantes. Em primeiro lugar vou destacar as duas constantes desta classe, são elas: INDEX_FIELD_NAME e CLASS_NAME_FIELD. A constante INDEX_FIELD_NAME representa o nome do campo que será usado como chave primária da tabela. Esta constante será muito importante para o framework na hora de criar a tabela. A constante CLASS_NAME_FIELD é o nome do campo onde será armazenado o nome da classe que uma determinada linha da tabela representa. Este campo será importante para situações como a da classe TAgendamento. No caso desta classe, não é a classe TAgendamento que será persistida, mas sim as suas subclasses. Isto significa que cada linha da tabela de agendamentos poderá estar representando um tipo de agendamento diferente, ou seja, uma subclasse de agendamento diferente. Logo você vai entender melhor e ver isto funcionando de verdade.

Ainda falando sobre a classe TBean, veja que ela possui um atributo ID. Uma vez que todas as tabelas possuem uma chave primária, a classe TBean já faz a declaração deste atributo para você. O atributo ID da classe TBean está diretamente relacionado com o valor da constante INDEX_FIELD_NAME. O nome deste atributo deve ser igual ao valor da constante INDEX_FIELD_NAME, pois esta constante identifica o nome do atributo usado como chave primária da tabela. Por fim, temos a declaração do método abstrato GetXmlName. Este método deve retornar o nome do arquivo XML usado para persistir as informações do objeto, em outras palavras, é o objeto TBean que sabe o nome do arquivo onde suas informações serão persistidas.

Uma fábrica dentro do framework

Existe uma fábrica dentro do framework e o nome dela é TDataSetFactory (Listagem 2). Esta classe tem o objetivo de fabricar os objetos TDataSet devidamente configurados para estabelecer conexão com o arquivo onde as informações serão persistidas. Observe que o único método disponível nesta classe é o DoDataSet. Quando invocado este método vai criar e configurar um objeto TDataSet para que ele se conecte com o arquivo correspondente ao objeto Bean passado como parâmetro para o método. É claro que se o arquivo para o qual o TDataSet vai se conectar não existir a fábrica deve criá-lo.

Internamente esta fábrica faz duas coisas que merecem destaques. Primeiro ela verifica se o arquivo XML, referente ao objeto Bean recebido como parâmetro, existe. Se não existir a fábrica o cria de acordo com as propriedades do objeto Bean. O segundo fato importante é que a aplicação usa arquivos XML justamente porque esta fábrica cria objetos TDataSet configurados para conectar em arquivos XML. Se futuramente for necessário trocar o tipo de banco de dados do agendador para Firebird, por exemplo, será muito simples, pois o maior trabalho – talvez o único – será criar uma nova fábrica que crie objetos TDataSet configurados para conectar com uma tabela específica do banco Firebird. Criada a nova fábrica, o agendador não vai nem sentir a diferença de estar se usando banco de dados.

TDataSetFactory fabrica objetos TDataSet porque quase todos, se não todos, os componentes de conexão utilizam a classe TDataSet. Portanto trocar o SGBD, ou o componente de conexão se torna muito simples, pois a fábrica retorna o objeto mais genérico entre eles.

...

Quer ler esse conteúdo completo? Tenha acesso completo