Desenvolvendo um Sistema Financeiro em Delphi - Partes 3 e 4

Este artigo é a terceira parte de uma série de artigos sobre o desenvolvimento de um sistema financeiro em Delphi que traz a implementação da camada de acesso a dados, utilizando uma implementação do padrão DAO.

Artigo no estilo: Curso

Fique por dentro
Neste artigo iremos tratar de um tópico muito importante em um projeto orientado a objetos, que é a camada de acesso a dados. É mostrado como implementar classes DAO, onde temos o mapeamento das classes de negócio para tabelas no banco de dados e o mapeamento das propriedades para as colunas das tabelas criadas, e vice-versa. Veremos objetos sendo transformados em linhas na tabela e propriedades transformadas em colunas.

O Delphi, no que diz respeito a linguagem, implementa todos os requisitos de uma linguagem orientada a objetos, pois nos permite fazer abstração, encapsulamento, herança e polimorfismo. Porém, o que mais vemos nos desenvolvedores Delphi é o desenvolvimento de aplicações de forma estruturada, onde são explorados apenas os recursos RAD que o IDE proporciona.

Um dos grandes pontos negativos de trabalhar com Delphi é a falta de um framework de mapeamento objeto-relacional robusto e que seja apoiado pela Embarcadero.

Existem iniciativas como o DORM (open-source) e o Aurelius (comercial), mas são soluções que ainda não se tornaram populares. Neste artigo veremos uma solução manual para resolver esse problema de mapeamento objeto-relacional.

Existem boas práticas, como a Inversão de Controle (BOX 1), que no Delphi não possui um framework apoiado e incentivado pela Embarcadero.

Apesar da ferramenta não oferecer um framework nativo para trabalharmos com orientado a objetos, a Delphi Language possui todos os recursos necessários para desenvolvermos orientado a objetos, como Generics e Métodos Anônimos.

BOX 1. Inversão de Controle e Injeção de Dependência

Este princípio é a base para qualquer bom design de software orientado a objetos. O princípio da Inversão de Dependência nos diz que módulos de alto nível não devem ser dependentes de módulos de baixo nível, ambos devem depender de abstrações, que por sua vez, não devem depender de detalhes.

Inverter a dependência faz com que o cliente não fique frágil a mudanças relacionadas a detalhes de implementação, isto é, mudar um detalhe da implementação não faz com que sejam necessárias alterações no cliente.

Este princípio é bastante presente em muitos padrões de projeto, pois a maioria deles definem uma interface para que não haja dependências de implementações.

Outro padrão que geralmente anda junto com o princípio de inversão de dependência é a Injeção de Dependências, que é uma forma de conseguir a inversão de controle. Nesta solução, as dependências entre os módulos não são definidas programaticamente, mas sim pela configuração de uma infraestrutura de software (container) que é responsável por injetar em cada classe suas dependências declaradas.

O padrão de Injeção de dependências sugere que uma conexão com banco de dados seja injetada na classe. Com isso, além de inverter a dependência, a classe não precisa se preocupar com o ciclo de vida das suas dependências (no exemplo da conexão, a classe não precisa abrir ou fechar conexão, apenas receber uma referência desta conexão e a utiliza).

Impedância Objeto Relacional

A Orientação a Objetos traz como principal vantagem sobre a estruturada a representação de objetos de maneira bastante semelhante ao mundo real, o que torna-se um desafio a mais quando precisamos fazer a persistência destes nos bancos de dados relacionais.

Existem soluções de bancos de dados orientados a objetos, mas estes ainda não se tornaram populares, sendo os bancos de dados relacionais ainda muito mais utilizado que estes.

Não existe uma conversão direta entre os objetos que construímos nas linguagens de programação e o banco de dados relacionais, por isso é necessário criarmos o que é chamado de Mapeamento Objeto-Relacional, onde objetos são transformados geralmente em registros em uma tabela e vice-versa.

Mapeamento Objeto Relacional

Isso surgiu porque precisamos salvar o estado de um objeto (atributos, herança, polimorfismo) em tabelas do banco de dados. O mapeamento básico pode ser feito através de conversões manuais ou através de frameworks de persistência, onde ambas possuem vantagens e desvantagens, mas a conversão manual nos dá uma melhor performance, enquanto que com frameworks de persistência não precisamos nos preocupar com escrita de SQL.

Em Delphi os frameworks de mapeamento objeto relacional geralmente nos possibilitam a configuração de persistência através de arquivos XML ou o recurso de annotations ou custom atributes.

No mapeamento objeto relacional básico o que temos é o mapeamento de classes para uma tabela do banco de dados, temos também o mapeamento de objetos da classe para registros da tabela e as propriedades da classe para colunas da tabela.

Padrão DAO (Data Access Object)

O padrão de projeto DAO surgiu com a necessidade de separarmos a lógica de negócios da lógica de persistência de dados.

Este padrão permite que possamos mudar a forma de persistência sem que isso influencie em nada na lógica de negócio, além de tornar nossas classes mais legíveis.

Classes DAO são responsáveis por trocar informações com o SGBD e fornecer operações CRUD e de pesquisas, além de ser capaz de buscar dados no banco e transformar em objetos ou lista desses através de listas genéricas.

Também deverá receber os objetos, converter em instruções SQL e mandar para o bando de dados. Toda interação com a base se dará através destas classes, nunca das classes de negócio, muito menos de formulários.

Se aplicarmos este padrão corretamente será abstraído completamente o modo de busca e gravação dos dados, tornando isso transparente para aplicação, facilitando muito na hora de fazermos manutenção na aplicação ou migração de banco de dados.

Também conseguimos centralizar a troca de dados com o SGBD, assim teremos um ponto único de acesso a dados e nossa aplicação um ótimo design orientado a objeto.

Generics

Generics é um recurso que foi incorporado na versão 2009 do Delphi, que permite criar estruturas genéricas. Na orientação a objetos podemos definir o tipo de retorno de uma lista de objetos e somente serão permitidas inserções de objetos daquele tipo, do contrário ocorrerá um erro ainda em tempo de compilação, assim, evitando erros em tempo de execução do tipo Invalid Type Casting, muito comum para quem trabalhava com listas de objetos em versões anteriores a 2009 em Delphi.

Para fazer uso de Generics temos que declarar o namespace System.Generics.Collections na seção uses do código.

As duas principais classes deste namespace são a TList<T> e TobjectList<T>.

A Classe TList<T> possibilita guardar coleções de qualquer tipo de dados, tanto primitivos quanto objetos. Já a classe TObjectList<T>, que é uma especialização de TList, pode armazenar somente objetos.

Ela tem a vantagem de que no momento que liberarmos sua instância da memória, todos os objetos contidos nela também são liberados, diferentemente das instâncias de TList, onde no caso de as usarmos para armazenar objetos, precisamos liberar cada objeto individualmente da memória.

FireDAC

FireDAC é a versão da Embarcadero para o AnyDAC, que foi adquirida e integrada ao Delphi e o C++ Builder. No Delphi o FireDAC está disponível na versão XE3 e XE4 com um conjunto de componentes extras, e a partir da versão XE5 vem na instalação padrão.

É um conjunto de componentes de alta performance, de fácil utilização e provê conexão com vários bancos de dados, tanto locais quanto coorporativos.

Cada banco de dados possui um driver e trabalhado de forma diferente. Os drivers do FireDAC são nativos para cada banco de dados e ainda possui pontes para ODBC e dbExpress. Vários bancos de dados são suportados, entre eles temos: MySQL, SQL Server, Oracle e SQLite.

Para quem tem projetos multicamadas com Delphi e DataSnap (BOX 2), ou também servidores REST, basta migrar a parte Server de DBExpress para FireDAC, pois o TFDQuery é um TDataSet, portanto compatível com o TDataSetProvider e TClientDataSet.

Na parte Client, continua o TClientDataSet sem necessidade de qualquer alteração. Tem suporte a Firemonkey e VCL e possui um conjunto de componentes visíveis e não-visíveis, DataSets, Adapters.

BOX 2. DataSnap

ODatasnapé o mecanismo desenvolvimento multicamadas do Delphi.Servidoresde aplicaçãoDatasnappodem ser desenvolvidos tanto em Delphi quanto C++Builder, porém a grande vantagem está no cliente, que pode ser desenvolvido em qualquer linguagem de programação que tenha suporte a JSON.

Em 2009 foi criado um novo driver para oDBExpress, mas ao invés deste driver conectar-se a um banco de dados qualquer, ele se conectaria a um servidor de aplicação.

Assim, a partir do Delphi 2009 a conexão do cliente com o servidor de aplicação passou a ser feita utilizandoDBExpresstrafegando os dados utilizando o protocolo TCP/IP.

Classe Base DAO

Abra o projeto no Delphi para continuarmos. Para facilitar o trabalho com as classes DAO, criamos uma classe base a qual será herdada por todas as classes DAO que irão interagir com o banco de dados, conforme pode ser visto na Listagem 1.

Listagem 1. Classe base TDAO 01 unit DAO.Base; 02 interface 03 uses 04 DAO.ConnectionFactory, FireDAC.Comp.Client; 05 type 06 TDAO = class 07 protected 08 Connection: TFDConnection; 09 function GetKeyValue(ATable: string): Integer; 10 public 11 constructor Create; 12 destructor Destroy; override; 13 end; 14 implementation 15 { TDAO } 16 constructor TDAO.Create; 17 begin 18 Connection := TConnectionFactory.GetConnection; 19 end; 20 destructor TDAO.Destroy; 21 begin 22 Connection.Free; 23 inherited; 24 end; 25 function TDAO.GetKeyValue(ATable, AColumn: string): Integer; 26 var 27 SQL: string; 28 Id: Integer; 29 begin 30 SQL := 'select coalesce(max(‘ + AColumn + ‘),0) + 1 from ' + ATable; 31 Id := Integer(Connection.ExecSQLScalar(SQL)); 32 result := Id; 33 end; 34 end.

Nesta classe temos a declaração de uma referência para TFDConnection com escopo protegido, de maneira que possamos acessar este atributo em todas as classes derivadas desta. Usaremos este objeto em todas as classes descendentes, tanto para executar comandos de consulta quanto comandos de atualização no banco de dados."

[...] continue lendo...

Artigos relacionados