Neste post irei mostrar como eu crio minhas classes de conexões deixando-as de uma forma genérica, onde não ficamos refém de componentes de conexão, onde ficará muito fácil se quisermos trocar do Firedac para uma outra middle de conexão.
Isso garante que nosso projeto tenha um tempo de vida útil muito maior, pois desacoplando nossa classe de conexão ficamos livres para facilmente migrarmos para qualquer componente de acesso a banco.
Você ainda está preso ao passado?
Muitas pessoas estão ainda presas com seus projetos completamente acoplados, e muitos ainda nem conseguiram se livrar do BDE até hoje, ou ainda estão usando o DBExpress e não conseguem mudar para o Firedac, porque existe muita dependência dentro do seu código, colocaram muitos componentes em seus formulários, com isso gerando uma dependência, dificultando que seu projeto venha evoluir, deixando seu software refém dos componentes de acesso, e isso não é legal para o nosso mercado, onde constantemente vem sofrendo mudanças e nossos softwares ficando cada dia mais para trás e não acompanhando esse crescimento.
Organizando a casa.
Iremos trabalhar com MVC e Programação Funcional, onde iremos criar interfaces e classes que implementem essas interfaces, deixando nossos códigos mais limpos e de fácil manutenção.
Iremos abstrair para as interfaces todas as propriedades das conexões com o banco de dados, desta forma quando abstrairmos nós não ficamos refém de componentes de conexão, sendo assim fica muito mais fácil se quisermos trocar de componente de conexão.
Agora vamos implementar nosso model de conexão, neste ponto iremos criar nossas interfaces e as classes que implementam essas interfaces, essas padrões explico melhor em um dos meus treinamentos de MVC com DELPHI.
Nesta classe que implementa a interface de conexão já iremos fazer diretamente para o firedac, mas a forma que iremos implementar já irá ficar pronto para que possa ser usado qualquer outro Middle de acesso.
Primeiro vamos criar nossas interfaces, nesse código abaixo podemos ver que foi criado a interface iModelConexao, essa é a interface padrão que todos os Middle de acesso irão trabalhar em nossos projetos.
type iModelQuery = interface; iModelConexao = interface function Connection : TObject; end; iModelConexaoFactory = interface function Conexao : iModelConexao; function Query : iModelQuery; end; iModelQuery = interface function Query : TObject; function Open(aSQL : String) : iModelQuery; function ExecSQL(aSQL : String) : iModelQuery; end; iModelEntidade = interface function DataSet ( aValue : TDataSource ) : iModelEntidade; procedure Open; end; iModelEntidadeFactory = interface function Produto : iModelEntidade; end;
Em nossa interface iModelConexao nós possuímos o método Connection, e neste retorna TObject, onde todo componente do Delphi herdam de TObject.
Quando definimos um retorno mais ancestral em nossos método, podemos retornar qualquer objeto de conexão, deixando assim nossas implementações mais genéricas.
Com nossas interfaces criadas agora vamos criar as classes que implementam essas interfaces.
Type TModelFiredacConexao = class(TInterfacedObject, iModelConexao) private FConexao : TFDConnection; public constructor Create; destructor Destroy; override; class function New : iModelConexao; function Connection : TObject; end; implementation { TModelFiredacConexao } function TModelFiredacConexao.Connection: TObject; begin Result := FConexao; end; constructor TModelFiredacConexao.Create; begin FConexao := TFDConnection.Create(nil); FConexao.Params.DriverID := 'FB'; FConexao.Params.Database := 'localhost/3050:C:\MVC_Blog\Database\DADOS.FDB'; FConexao.Params.UserName := 'SYSDBA'; FConexao.Params.Password := 'masterkey'; FConexao.Connected := true; end; destructor TModelFiredacConexao.Destroy; begin FreeAndNil(FConexao); inherited; end; class function TModelFiredacConexao.New: iModelConexao; begin Result := Self.Create; end;
Como vamos trabalhar com o Firedac iremos precisar de um objeto de conexão, para isso temos o FConexao do tipo TFDConnection, no método Connection que retorna o objeto concreto de conexão com o retorno desse o objeto FConexao.
Como não criamos componentes em tela precisamos criar nosso objeto, para isso no método create dessa classe é criado esse objeto, e passamos também os atributos de conexão desse objeto, pois quando é solicitado uma conexão já irá ser recebido uma conexão pronta.
iModelQuery
Type TModelFiredacQuery = class(TInterfacedObject, iModelQuery) private FQuery : TFDQuery; FConexao : iModelConexao; public constructor Create(aValue : iModelConexao); destructor Destroy; override; class function New(aValue : iModelConexao) : iModelQuery; function Query : TObject; function Open(aSQL : String) : iModelQuery; function ExecSQL(aSQL : String) : iModelQuery; end; implementation { TModelFiredacQuery } constructor TModelFiredacQuery.Create(aValue: iModelConexao); begin FConexao := aValue; FQuery := TFDQuery.Create(nil); FQuery.Connection := TFDConnection(FConexao.Connection); end; destructor TModelFiredacQuery.Destroy; begin FreeAndNil(FQuery); inherited; end; function TModelFiredacQuery.ExecSQL(aSQL: String): iModelQuery; begin Result := Self; FQuery.ExecSQL(aSQL); end; class function TModelFiredacQuery.New(aValue: iModelConexao): iModelQuery; begin Result := Self.Create(aValue); end; function TModelFiredacQuery.Open(aSQL: String): iModelQuery; begin Result := Self; FQuery.Open(aSQL); end; function TModelFiredacQuery.Query: TObject; begin Result := FQuery; end;
Nesta classe criamos o query do firedac, para que possamos realizar as conexões com as entidades do banco de dados, nesta classe trabalhamos com injeção de dependências, caso não esteja familiarizado com isso eu explico melhor no curso de POO EXPERT+PROGRAMAÇÃO FUNCIONAL, pois como estamos trabalhando com o TFDQuery ele irá precisar conhecer a conexão, para isso nós passamos para o método New e Create a interface de conexão e não um objeto de conexão, por este motivo é criado o objeto FConexao para armazenar a conexão, fazendo isso deixamos nosso código desacoplado.
No método create recebemos a conexão, passamos para o objeto FConexão, criamos nossa FQuery e passamos a conexão para ele, mas você pode ver que fazemos um casting pois o FDQuery só trabalha com o TFDConnection.
iModelConexaoFactory:
Type TModelConexaoFactory = class(TInterfacedObject, iModelConexaoFactory) private public constructor Create; destructor Destroy; override; class function New : iModelConexaoFactory; function Conexao : iModelConexao; function Query : iModelQuery; end; implementation { TModelConexaoFactory } function TModelConexaoFactory.Conexao: iModelConexao; begin Result := TModelFiredacConexao.New; end; constructor TModelConexaoFactory.Create; begin end; destructor TModelConexaoFactory.Destroy; begin inherited; end; class function TModelConexaoFactory.New: iModelConexaoFactory; begin Result := Self.Create; end; function TModelConexaoFactory.Query: iModelQuery; begin Result := TModelFiredacQuery.New(Self.Conexao); end;
A classe TModelConexaoFactory é a classe responsável de retornar uma instancia das classes de conexão, fazendo com que o meu acoplamento seja menor ainda, pois se em algum lugar a classe precisar do objeto de conexão, ele não irá precisar saber da classe de qualquer Middle de acesso, no curso de MVC é explicado mais afundo sobre isso.
Contemplando os dados.
Agora vamos tratar de nossas entidades, entidade é o espelho das tabelas do nosso banco de dados, visto que já temos nossa conexão pronta para trabalhar e sem dependência de componentes, totalmente desacoplada.
iModelEntidade
Type TModelEntidadeProduto = class(TInterfacedObject, iModelEntidade) private FQuery : iModelQuery; public constructor Create; destructor Destroy; override; class function New : iModelEntidade; function DataSet ( aValue : TDataSource ) : iModelEntidade; procedure Open; end; implementation { TModelEntidadeProduto } constructor TModelEntidadeProduto.Create; begin FQuery := TModelConexaoFactory.New.Query; end; function TModelEntidadeProduto.DataSet(aValue: TDataSource): iModelEntidade; begin Result := Self; aValue.DataSet := TDataSet(FQuery.Query); end; destructor TModelEntidadeProduto.Destroy; begin inherited; end; class function TModelEntidadeProduto.New: iModelEntidade; begin Result := Self.Create; end; procedure TModelEntidadeProduto.Open; begin FQuery.Open('SELECT * FROM PRODUTO'); end;
Essa classe representa nossa tabela de PRODUTO do banco de dados, e a mesma implementa a interface de iEntidade, herdando todos os seus metodos, temos o objeto FQuery do tipo iModelQuery, que no Create dessa classe nós instanciamos esse objeto a classe TModelConexaoFactory , onde é pedido a Query dessa factory, lembrando que quem implementa não instancia as classes de conexão e query, mas a factory, deixando nossas implementações desacopladas.
No método DataSet nós fazemos um casting do dataset da query, no metodo Open nós passamos o select para a tabela de PRODUTO.
IModelEntidadeFactory:
type TModelEntidadesFactory = class(TInterfacedObject, iModelEntidadeFactory) private FProduto : iModelEntidade; public constructor Create; destructor Destroy; override; class function New : iModelEntidadeFactory; function Produto : iModelEntidade; end; implementation { TModelEntidadesFactory } constructor TModelEntidadesFactory.Create; begin end; destructor TModelEntidadesFactory.Destroy; begin inherited; end; class function TModelEntidadesFactory.New: iModelEntidadeFactory; begin Result := Self.Create; end; function TModelEntidadesFactory.Produto: iModelEntidade; begin if not Assigned(FProduto) then FProduto := TModelEntidadeProduto.New; Result := FProduto; end;
Como falei anteriormente que a entidade é o espelho das tabelas do banco de dados, nós criamos uma factory que iremos implementar um método para cada entidade do banco de dados, fazendo assim não termos que criar uma classe para cada entidade, deixando tudo por conta de nossa classe factory.
Realizando o controle de tudo.
Assim como no model que está representando a camada que interage com o banco de dados, precisamos de uma outra camada que faz o meio de campo, para isso entra o controller.
Nós criamos a interface de controller que possui o método que tem como retorno a factory das entidades do model.
type iController = interface function Entidades : iModelEntidadeFactory; end;
iController
Type TController = class(TInterfacedObject, iController) private FModelEntidades : iModelEntidadeFactory; public constructor Create; destructor Destroy; override; class function New : iController; function Entidades : iModelEntidadeFactory; end; implementation { TController } constructor TController.Create; begin FModelEntidades := TModelEntidadesFactory.New; end; destructor TController.Destroy; begin inherited; end; function TController.Entidades: iModelEntidadeFactory; begin Result := FModelEntidades; end; class function TController.New: iController; begin Result := Self.Create; end;
Nesta classe que implementa a interface iController, o nosso controller não precisa conhecer as entidades, e sim a factory, o acoplamento é zero.
A visão além do alcance…
Nós como programadores Delphi estamos muito acostumados a colocar tudo na tela onde nossa camada de visão está ao alcance de tudo, e sendo assim refém dos componentes.
Agora irei lhe mostrar como nossa camada de visão irá receber os dados de nosso banco de dados.
Lembra que falei que nosso controller que faz o meio de campo? Então ele irá pegar essas informações e repassar para a nossa camada de visão.
Em nosso formulário temos um button, dbgrid e um datasource:
Em nossa camada de visão criamos um objeto com o tipo iController, e no evendo oncreate do formulário instanciamos a classe controller:
... private { Private declarations } FController : iController; ... procedure TForm3.FormCreate(Sender: TObject); begin FController := TController.New; ReportMemoryLeaksOnShutdown := true; end;
Desta forma o evento onclick do botão fica mais claro e simples de se implementar a chamada aos dados:
procedure TForm3.Button1Click(Sender: TObject); begin FController .Entidades .Produto .DataSet(DataSource1) .Open; end;
Viu como fica simples programar usando a programação funcional, e usando o padrão MVC, com isso nossa camada de visão não precisa saber quem é o seu provedor de dados, bastando simplesmente você alterar sua Middle de conexão, sem precisar alterar uma linha se quer da camada de visão.
A nossa classe de conexão e todo o código usado nesse artigo pode ser baixando no link a baixo:
Esse artigo foi retirado de uma das aulas do nosso treinamento de MVC COM DELPHI, nesse treinamento você irá aprender de forma prática como implementar a arquitetura MVC do absoluto zero, criar menus dinâmicos, CRUD completo com estrutura MVC e ainda saberá como seu projeto pode ficar completamente independente de componente de conexão, trabalhando simultaneamente com Firedac e Zeos e ainda exemplos reais de aplicação das técnicas para resolver problemas do dia a dia, Como:
* Criando a estrutura MVC do Zero
* Criando menus desacoplados com MVC
* Estruturando a conexão com Banco de Dados
* CRUD com acoplamento Zero
* Possibilidade de trabalhar com múltiplos componente de conexão no mesmo projeto.
Somente para você caro leitor estou disponibilizando este curso com um super desconto CLICANDO AQUI.