Interfaces x Generics – Que tal um ORM Básico? Parte 9

Já conseguimos utilizar os métodos de inclusão, alteração e exclusão da classe TDaoUib, conforme pôde ser visto no último artigo.

Porém, você deve ter notado a dependência excessiva (forte acoplamento) existente para efetuar tais operações. Outro ponto importante a ser destacado é que, apesar de não ter sido notado tão claramente devido a utilização de apenas uma tabela, se em determinado momento decidirmos trocar de componente, ou seja, em vez do Uib optarmos pelo IBX, tudo que for relativo a TDaoUib teria que ser modificado para que fosse possível utilizar o novo componente.

Temos que mudar esse quadro o quanto antes, senão ficaremos engessados! E o que queremos é justamente o oposto, não é mesmo? ;-)

Ajustes necessários em TDaoUib

Queremos abstrair (simplificar) o uso dos componentes de acesso de tal forma que lá na ponta, o usuário final (desenvolvedor que utilizará este código) terá o trabalho facilitado, bastando informar alguns dados básicos para que o código esteja pronto para uso. Para isso, devemos:

  • Alterar o nome da variável FTransacao para FTransaction (como definimos o nome do database em inglês, vamos manter o padrão).
  • Alterar a declaração do construtor para passar apenas o nome e local onde se encontra o banco de dados.
  • Os objetos, FDatabase e FTransaction, serão criados no construtor, bem como suas configurações iniciais.

Segue código completo de DaoUib.pas:

unit DaoUib;

interface

uses Base, Rtti, Atributos, uib, system.SysUtils, System.Classes;

type
  TDaoUib = class(TInterfacedObject, IDaoBase)
  private
    // conexao com o banco de dados
    FDatabase: TUIBDataBase;
    FTransaction: TUIBTransaction;

    // Este método configura os parâmetros da AQuery.
    procedure ConfigParametro(AQuery: TuibQuery; AProp: TRttiProperty; ACampo: string;  ATabela: TTabela);
    procedure FechaQuery;

    function ExecutaQuery: Integer;
  public
    //query para execução dos comandos crud
    Qry    : TUIBQuery;

    constructor Create(ADatabaseName: string);

    function Inserir(ATabela: TTabela): Integer;
    function Salvar(ATabela: TTabela): Integer;
    function Excluir(ATabela: TTabela): Integer;

    function InTransaction: Boolean;
    procedure StartTransaction;
    procedure Commit;
    procedure RollBack;
  end;

implementation

{ TDaoUib }

uses Vcl.forms, dialogs, System.TypInfo;

constructor TDaoUib.Create(ADatabaseName: string);
begin
  inherited Create;

  FDatabase := TUIBDataBase.Create(Application);
  //configurações iniciais da conexão
  with FDatabase do
  begin
    DatabaseName := ADatabaseName;
    Params.Add('sql_dialect=3');
    Params.Add('lc_ctype=ISO8859_1');
    Connected := True;
  end;

  FTransaction := TUIBTransaction.Create(Application);
  //configurações iniciais da transacao
  with FTransaction do
  begin
    Database := FDatabase;
    DefaultAction := etmCommit;
  end;

  Qry := TUIBQuery.Create(Application);
  Qry.DataBase := FDatabase;
  Qry.Transaction := FTransaction;
end;

function TDaoUib.InTransaction: Boolean;
begin
  Result := FTransaction.InTransaction;
end;

procedure TDaoUib.StartTransaction;
begin
  FTransaction.StartTransaction;
end;

procedure TDaoUib.RollBack;
begin
  FTransaction.RollBack;
end;

procedure TDaoUib.Commit;
begin
  FTransaction.Commit;
end;

procedure TDaoUib.ConfigParametro(AQuery: TUIBQuery; AProp: TRttiProperty;
  ACampo: string; ATabela: TTabela);
begin
  with AQuery do
  begin
    case AProp.PropertyType.TypeKind of
      tkInt64,
      tkInteger:
      begin
        Params.ByNameAsInteger[ACampo] := AProp.GetValue(ATabela).AsInteger;
      end;
      tkChar,
      tkString,
      tkUString:
      begin
        Params.ByNameAsString[ACampo] := AProp.GetValue(ATabela).AsString;
      end;
      tkFloat:
      begin
        if CompareText(AProp.PropertyType.Name, 'TDateTime') = 0 then
         Params.ByNameAsDateTime[ACampo] := AProp.GetValue(ATabela).AsType<TDateTime>
        else
         Params.ByNameAsCurrency[ACampo] := AProp.GetValue(ATabela).AsCurrency;
      end;
      tkVariant:
      begin
        Params.ByNameAsVariant[ACampo] := AProp.GetValue(ATabela).AsVariant;
      end;
    else
      raise Exception.Create('Tipo de campo não conhecido: ' + AProp.PropertyType.ToString);
    end;
  end;
end;

procedure TDaoUib.FechaQuery;
begin
  Qry.Close;
  Qry.SQL.Clear;
end;

function TDaoUib.ExecutaQuery: Integer;
begin
  with Qry do
  begin
    Prepare();
    ExecSQL;
    Result := RowsAffected;
  end;
end;

function TDaoUib.Excluir(ATabela: TTabela): Integer;
var
  Comando: TFuncReflexao;
begin
  //crio uma variável do tipo TFuncReflexao - um método anônimo
  Comando := function(ACampos: TCamposAnoni): Integer
  var
    Campo: string;
    PropRtti: TRttiProperty;
  begin
    FechaQuery;
    with Qry do
    begin
      sql.Add('Delete from ' + ACampos.NomeTabela);
      sql.Add('Where');

      //percorrer todos os campos da chave primária
      ACampos.Sep := '';
      for Campo in ACampos.PKs do
      begin
        sql.Add(ACampos.Sep+ Campo + '= :' + Campo);
        ACampos.Sep := ' and ';
        // setando os parâmetros
        for PropRtti in ACampos.TipoRtti.GetProperties do
          if CompareText(PropRtti.Name, Campo) = 0 then
            begin
              ConfigParametro(Qry, PropRtti, Campo, ATabela);
            end;
      end;
    end;
    Result := ExecutaQuery;
  end;

  //reflection da tabela e execução da query preparada acima.
  Result := ReflexaoSQL(ATabela, Comando);
end;

function TDaoUib.Inserir(ATabela: TTabela): Integer;
var
  Comando: TFuncReflexao;
begin
  Comando := function(ACampos: TCamposAnoni): Integer
  var
    Campo: string;
    PropRtti: TRttiProperty;
  begin
    FechaQuery;
    with Qry do
    begin
      sql.Add('Insert into ' + ACampos.NomeTabela);
      sql.Add('(');

      //campos da tabela
      ACampos.Sep := '';
      for PropRtti in ACampos.TipoRtti.GetProperties do
      begin
        SQL.Add(ACampos.Sep + PropRtti.Name);
        ACampos.Sep := ',';
      end;
      sql.Add(')');

      //parâmetros
      sql.Add('Values (');
      ACampos.Sep := '';
      for PropRtti in ACampos.TipoRtti.GetProperties do
      begin
        SQL.Add(ACampos.Sep + ':' + PropRtti.Name);
        ACampos.Sep := ',';
      end;
      sql.Add(')');

      //valor dos parâmetros
      for PropRtti in ACampos.TipoRtti.GetProperties do
      begin
        Campo := PropRtti.Name;
        ConfigParametro(Qry, PropRtti, Campo, ATabela);
      end;
    end;
    Result := ExecutaQuery;
  end;

  //reflection da tabela e execução da query preparada acima.
  Result := ReflexaoSQL(ATabela, Comando);
end;

function TDaoUib.Salvar(ATabela: TTabela): Integer;
var
  Comando: TFuncReflexao;
begin
  Comando := function(ACampos: TCamposAnoni): Integer
  var
    Campo: string;
    PropRtti: TRttiProperty;
  begin
    FechaQuery;
    with Qry do
    begin
      sql.Add('Update ' + ACampos.NomeTabela);
      sql.Add('set');

      //campos da tabela
      ACampos.Sep := '';
      for PropRtti in ACampos.TipoRtti.GetProperties do
      begin
        SQL.Add(ACampos.Sep + PropRtti.Name + '=:'+PropRtti.Name);
        ACampos.Sep := ',';
      end;
      sql.Add('where');

      //parâmetros da cláusula where
      ACampos.Sep := '';
      for Campo in ACampos.PKs do
      begin
        sql.Add(ACampos.Sep+ Campo + '= :' + Campo);
        ACampos.Sep := ' and ';
      end;

      //valor dos parâmetros
      for PropRtti in ACampos.TipoRtti.GetProperties do
      begin
        Campo := PropRtti.Name;
        ConfigParametro(Qry, PropRtti, Campo, ATabela);
      end;
    end;
    Result := ExecutaQuery;
  end;

  //reflection da tabela e execução da query preparada acima.
  Result := ReflexaoSQL(ATabela, Comando);
end;

end.

Como a nossa classe já tem um objeto para conexão e um para a transação, não precisamos mais dos componentes UIB instanciados em nosso formulário principal, o frmMain:

Exclua UIBDatabase1 e UIBTransaction1.

Eu quero Generics, Eu quero Interfaces…

Eu irei propor dois modelos para você, caro leitor, ambos com um objetivo em comum: resolver o problema do forte acoplamento existente. O primeiro será baseado em Generics e o segundo em Interface + Singleton (Design Pattern). No final deste artigo irei pedir a sua opinião a respeito dos modelos apresentados. Portanto, continue lendo…

Modelo 1: Classe TDaoGenerico

Abra a unidade Base.pas e abaixo da interface IDaoBase, coloque:

...
IDaoBase =  interface
    ['{D06AAE8D-D5F7-47E7-BF11-E26687C11900}']

    function Inserir(ATabela: TTabela): Integer;
    function Salvar(ATabela: TTabela): Integer;
    function Excluir(ATabela: TTabela): Integer;

    function InTransaction: Boolean;
    procedure StartTransaction;
    procedure Commit;
    procedure RollBack;

  end;

  //DAO genérico
  TDaoGenerico<T: IDaoBase> = class
  private
    FCon: T;  //classe de conexao uib, ibx, ...
    procedure SetCon(const Value: T);
  public
    property Con: T read FCon write SetCon;
  end;
...

Ctrl+Sift+C para gerar o SetCon, que ficará com o seguinte código:

procedure TDaoGenerico<T>.SetCon(const Value: T);
begin
  FCon := Value;
end;

Pronto! Simples assim!

Agora, iremos inserir um datamodule em nosso projeto. Assim, tudo que for relativo a banco de dados faremos neste local.

Salve como udmPrin (Ctrl+S). No nome, coloque dmPrin:

Por fim, configure o auto-create forms (Ctrl+Shift+11) da seguinte forma:

O datamodule deve ser o primeiro.

Em public de dmPrin, crie uma variável do tipo IDaoBase e uma variável da nossa classe genérica:

unit udmPrin;

interface

uses
  System.SysUtils, System.Classes, uib, Base, DaoUib;

type
  TdmPrin = class(TDataModule)
  private
    { Private declarations }
  public
    { Public declarations }
    Conexao: IDaoBase;
    Dao: TDaoGenerico<TDaoUib>;
  end;
...

No OnCreate, faça:

procedure TdmPrin.DataModuleCreate(Sender: TObject);
begin
  // configuração da nossa conexão - utilizando DaoUib
  Conexao:= TDaoUib.Create('C:\Users\Luiz\Documents\RAD Studio\Projects\Persistencia\Bd\BANCOTESTE.FDB');

  // DAO genérico
  Dao := TDaoGenerico<TDaoUib>.Create;
  Dao.Con:= TDaoUib(Conexao);
end;

Analisando o código:

  • Linha 4, o tipo de conexão é criado, que neste caso é da classe TDaoUib. Note que agora estou passando o caminho e nome do banco de dados no Create desta classe.
  • Linha 7, criamos o nosso DAO genérico. Como estamos passando T como sendo TDaoUib, somente objetos derivados desta classe serão aceitos.
  • Linha 8, setamos a propriedade Con com o objeto criado na Linha 4.

Com isso, evitaremos a dependência de algumas unidades durante o CRUD (Ah! Eu não esqueci do único método que está faltando:R – de Read. Veremos como recuperar[consultar] dados nos próximos artigos). Precisaremos apenas adicionar o datamodule e a classe que representa a tabela que estamos alterando. É bom lembrar que, por enquanto, temos apenas uma tabela chamada Teste.

Nossa classe genérica recebe T do tipo IDaoBase e a propriedade Con será desse tipo. Vê-lo funcionando irá facilitar o entendimento.

Preparando o Teste

Vamos começar pelo formulário frmTesteAtributos:

Como já testamos o botão Nome Tabela e Chave Primária, não há mais necessidade de tê-los neste formulário. Portanto, vamos excluí-los (exclua também os códigos destes botões):

Tire a unidade Atributos da cláusula uses deste formulário. Ficaremos apenas com os botões de inclusão, alteração e exclusão, conforme acima.

Abaixo, vemos o código do botão inserir:

procedure TfrmTesteAtributos.btnInserirClick(Sender: TObject);
var
  ATab: TTeste;
  Dao: IDaoBase;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    Dao := TDaoUib.Create(dmPrin.UIBDataBase1, dmPrin.UIBTransaction1);
    with ATab do
    begin
      id := 1;
      Estado := 'MA';
      Descricao := 'MARANHÃO';
      Habitantes := 6569683;
      RendaPerCapta := 319;
    end;
    Registros := Dao.Inserir(ATab);
    Memo1.Lines.Add(Format('Registro inserido: %d', [Registros]));
    Memo1.Lines.Add(Format('id: %d, nome: %s',[ATab.Id, atab.Descricao]));
  finally
    ATab.Free;
  end;
end;

Não precisamos mais do objeto Dao (linhas 4 e 9). Consequentemente, também não será mais necessário a unidade Base e nem tão pouco da unidade DaoUib na cláusula uses. Como resultado teremos uma menor dependência (acoplamento fraco).

Teremos então, na cláusula uses (abaixo de implementation) apenas as unidades:

implementation

{$R *.dfm}

uses
  Teste, udmPrin;

Abaixo, código dos três botões. Note que já implementa o controle de transações.

procedure TfrmTesteAtributos.btnInserirClick(Sender: TObject);
var
  ATab: TTeste;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    with ATab do
    begin
      id := 1;
      Estado := 'MA';
      Descricao := 'MARANHÃO';
      Habitantes := 6569683;
      RendaPerCapta := 319;
    end;
    dmPrin.Dao.Con.StartTransaction;
    try
      Registros := dmPrin.Dao.Con.Inserir(ATab);

      dmPrin.Dao.Con.Commit;

      Memo1.Lines.Add(Format('Registro inserido: %d', [Registros]));
      Memo1.Lines.Add(Format('id: %d, nome: %s',[ATab.Id, atab.Descricao]));
    except
      on E: Exception do
      begin
        dmPrin.Dao.Con.RollBack;
        ShowMessage('Ocorreu um problema ao executar operação: ' + e.Message);
      end;
    end;
  finally
    ATab.Free;
  end;
end;

procedure TfrmTesteAtributos.btnSalvarClick(Sender: TObject);
var
  ATab: TTeste;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    with ATab do
    begin
      id := 1;
      Estado := 'MA';
      Data   := Now;
      Descricao := 'MARANHÃO (ALTERADO)';
      Habitantes := 6569683;
      RendaPerCapta := 319;
    end;
    dmPrin.Dao.Con.StartTransaction;
    try
      Registros := dmPrin.Dao.Con.Salvar(ATab);

      dmPrin.Dao.Con.Commit;

      Memo1.Lines.Add(Format('Registro inserido: %d', [Registros]));
      Memo1.Lines.Add(Format('id: %d, nome: %s',[ATab.Id, atab.Descricao]));
    except
      on E: Exception do
      begin
        dmPrin.Dao.Con.RollBack;
        ShowMessage('Ocorreu um problema ao executar operação: ' + e.Message);
      end;
    end;
  finally
    ATab.Free;
  end;
end;

procedure TfrmTesteAtributos.btnExcluirClick(Sender: TObject);
var
  ATab: TTeste;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    ATab.Id := 1;
    dmPrin.Dao.Con.StartTransaction;
    try
      Registros := dmPrin.Dao.Con.Excluir(ATab);

      dmPrin.Dao.Con.Commit;

      Memo1.Lines.Add(Format('Registro excluido: %d', [Registros]));
    except
      on E: Exception do
      begin
        dmPrin.Dao.Con.RollBack;
        ShowMessage('Ocorreu um problema ao executar operação: ' + e.Message);
      end;
    end;
  finally
    ATab.Free;
  end;
end;

Faça o teste: inclua, altere e exclua.
Você percebeu que não é necessário fechar o programa para ver os dados no BD? Sim, os dados já estão sendo persistidos após efetuarmos o Commit no banco.

Modelo 2: Classe TDaoSingleton

Para ficar claro, e assim visualizar melhor as diferenças existentes entre o modelo 1 e o 2, façamos o seguinte:

  • Crie uma pasta chamada “Persistencia”;
  • Feche o Delphi e mova a pasta do nosso projeto para dentro da pasta criada;
  • Renomeie a pasta do projeto para “Generico”;
  • Copie a pasta “Generico” (Ctrl+C) e cole (Ctrl+V) dentro de “Persistencia”. Irá criar uma cópia;
  • Renomeie a nova pasta (a “cópia”) para Interface.

Desta forma teremos 2 projetos: um para o modelo 1 (Generico) e o outro para o modelo 2 (Interface). Espero ter conseguido deixar claro esta parte.

No Delphi, abra o projeto que está na pasta “Interface”. Feito isso, abra a unidade Base.pas.

Iremos remover a classe TDaoGenerico e no seu lugar colocaremos uma interface e uma classe, a TDaoSingleton. Ela irá utilizar o Padrão Singleton (teremos uma implementação simples do padrão, mas é bom olhar o comentário que fiz no post Design Pattern: Singleton). Então, vamos lá:

unit Base;

interface

uses DB, SysUtils, Classes, System.Generics.Collections;

type
  IBaseDados = interface
  ['{9B0F9364-AB16-4C12-B4B7-4E2287840232}']
  end;

  ITransacao = interface
  ['{2F1DCA7A-E7F4-4EC3-BDB2-22B99C8CA7DB}']
  end;

  TTabela = class(TObject)
  end;

  IDaoBase =  interface
    ['{D06AAE8D-D5F7-47E7-BF11-E26687C11900}']

    function Inserir(ATabela: TTabela): Integer;
    function Salvar(ATabela: TTabela): Integer;
    function Excluir(ATabela: TTabela): Integer;

    function InTransaction: Boolean;
    procedure StartTransaction;
    procedure Commit;
    procedure RollBack;
  end;

  IDaoSingleton = interface
    ['{0E2A5F26-3BBB-4022-8C2F-9BD88AD7CA89}']
  end;

   TDaoSingleton = class(TInterfacedObject, IDaoSingleton)
  private
    FCon: IDaoBase;
    class var FInstancia: IDaoSingleton;
    procedure SetCon(const Value: IDaoBase);
  public
    class function Get: TDaoSingleton; static;
    property Con: IDaoBase read FCon write SetCon;
  end;

implementation

{ TDaoSing }

class function TDaoSingleton.Get: TDaoSingleton;
begin
  If not Assigned(FInstancia) Then
    FInstancia := TDaoSingleton.Create;

  Result := FInstancia as TDaoSingleton;
end;

procedure TDaoSingleton.SetCon(const Value: IDaoBase);
begin
  FCon := Value;
end;

end.

Muito parecido com a classe genérica, porém com esta não precisaremos nos preocupar com instanciação e destruição.

Abra o dmPrin e faça as seguintes alterações:

unit udmPrin;

interface

uses
  System.SysUtils, System.Classes, uib, Base, DaoUib;

type
  TdmPrin = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Conexao: IDaoBase;
  end;

var
  dmPrin: TdmPrin;

implementation

{%CLASSGROUP 'System.Classes.TPersistent'}

{$R *.dfm}

procedure TdmPrin.DataModuleCreate(Sender: TObject);
begin
  // configuração da conexão - utilizando DaoUib
  Conexao := TDaoUib.Create('C:\Users\Luiz\Documents\RAD Studio\Projects\Persistencia\Bd\BANCOTESTE.FDB');

  // dao genérico
  TDaoSingleton.Get.Con := Conexao;
end;

end.
  • Observe que não precisamos mais do objeto chamado Dao.
  • Na linha 33, setamos a propriedade Con com o objeto Conexao (TDaoUib).

No formulário frmTesteAtributos, passamos a utilizar TDaoSingleton em vez de TDaoGenerico:

implementation

{$R *.dfm}

uses
  Teste, Base;

procedure TfrmTesteAtributos.btnInserirClick(Sender: TObject);
var
  ATab: TTeste;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    with ATab do
    begin
      id := 1;
      Estado := 'MA';
      Descricao := 'MARANHÃO';
      Habitantes := 6569683;
      RendaPerCapta := 319;
    end;
    TDaoSingleton.Get.Con.StartTransaction;
    try
      Registros := TDaoSingleton.Get.Con.Inserir(ATab);

      TDaoSingleton.Get.Con.Commit;

      Memo1.Lines.Add(Format('Registro inserido: %d', [Registros]));
      Memo1.Lines.Add(Format('id: %d, nome: %s',[ATab.Id, atab.Descricao]));
    except
      on E: Exception do
      begin
        TDaoSingleton.Get.Con.RollBack;
        ShowMessage('Ocorreu um problema ao executar a operação: ' + e.Message);
      end;
    end;
  finally
    ATab.Free;
  end;
end;

procedure TfrmTesteAtributos.btnSalvarClick(Sender: TObject);
var
  ATab: TTeste;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    with ATab do
    begin
      id := 1;
      Estado := 'MA';
      Data   := Now;
      Descricao := 'MARANHÃO (ALTERADO SINGLETON)';
      Habitantes := 6569683;
      RendaPerCapta := 319;
    end;
    TDaoSingleton.Get.Con.StartTransaction;
    try
      Registros := TDaoSingleton.Get.Con.Salvar(ATab);

      TDaoSingleton.Get.Con.Commit;

      Memo1.Lines.Add(Format('Registro inserido: %d', [Registros]));
      Memo1.Lines.Add(Format('id: %d, nome: %s',[ATab.Id, atab.Descricao]));
    except
      on E: Exception do
      begin
        TDaoSingleton.Get.Con.RollBack;
        ShowMessage('Ocorreu um problema ao executar a operação: ' + e.Message);
      end;
    end;
  finally
    ATab.Free;
  end;
end;

procedure TfrmTesteAtributos.btnExcluirClick(Sender: TObject);
var
  ATab: TTeste;
  Registros: Integer;
begin
  ATab := TTeste.Create;
  try
    ATab.Id := 1;
    TDaoSingleton.Get.Con.StartTransaction;
    try
      Registros := TDaoSingleton.Get.Con.Excluir(ATab);

      TDaoSingleton.Get.Con.Commit;

      Memo1.Lines.Add(Format('Registro excluido: %d', [Registros]));
    except
      on E: Exception do
      begin
        TDaoSingleton.Get.Con.RollBack;
        ShowMessage('Ocorreu um problema ao executar a operação: ' + e.Message);
      end;
    end;
  finally
    ATab.Free;
  end;
end;

Além de trocar a classe TDaoGenerico pela classe TDaoSingleton, no uses deixamos de utilizar a unidade udmPrin e em seu lugar, utilizamos a unidade Base.

Teste novamente…

Finalmente, os fontes!

Baixe os fontes aqui.

Passando a bola

Agora é com você! Quero que analise os dois modelos atentamente e depois me diga:

  • Qual dos 2 gostou mais?
  • Pontos positivos e negativos de cada um?
  • Tem alguma sugestão para melhorar o que foi feito?
  • Gostaria de continuar a utilizar os componentes TUIBDatabase e TUIBTRansaction diretamente, instanciando o componente no datamodule e passando eles por parâmetro, como era feito anteriormente?

Vou aguardar o seu comentário! Depois continuaremos o ORM básico com o modelo mais aceito.

Obrigado e até o próximo artigo!

Qual o modelo você prefere para o ORM Básico?

  • Modelo 1: Classe TDaoGenerico (63%, 5 Votes)
  • Modelo 2: Classe TDaoSingleton (38%, 3 Votes)

Total Voters: 8

Carregando ... Carregando ...
Sobre o autor: Luiz Carlos (60 Posts)

Desenvolvedor de software Balsas/MA


Compartilhe:

21 Replies to “Interfaces x Generics – Que tal um ORM Básico? Parte 9”

  1. 1) Eu gostei mais do primeiro (Genérico).

    2) O Genérico: + Diminui a quantidade de units na uses (diminui o acoplamento).
    + Permite criar mais de uma instância do Dao (pode ser útil em alguns casos, eu trabalho com SQL Server e crio duas conexões em meu projeto, uma para a database principal e outra para minha database de controle de CEP)
    – Mais complexa de entender (pra mim que sou iniciante nessa técnica).
    – Código um pouco maior.

    Interface: + Código menor e mais fácil de entender.
    + Garantia de que só existirá 1 instância da conexão (Singleton).
    – Mais acoplado que o genérico

    3) Eu não entendi bem o motivo de ter colocado a conexão e a transação como público, eu deixaria como private.
    Eu adicionaria uma opção (poderia ser um enum) para que fosse possível escolher a qual banco vamos conectar (se é produção, teste, desenvolvimento por exemplo), nesse caso acho que talvez a string de conexão fosse melhor, para não ter que colocar no datamodule 3 conexões e só usar 1.

    4) Eu deixaria passando a conexão (o componente) mesmo no local da string para criar a conexão, acho que assim é mais fácil para manter ou se for usada uma opção para escolher a qual banco vamos conectar, neste caso, acho que o mais indicado é a string mesmo.

    1. Excelente sua participação!

      Que venha mais!

      Com relação ao motivo da conexão e transação ser pública: a princípio eu tinha uma ideia diferente para eles, mas no fim, acabou não sendo necessária essa modificação. Vou mudar isso no próximo artigo.

      Vamos lá pessoal! Façam como o Ricardo… a discussão será benéfica para o nosso ORM básico. Portanto, participem.

      1. Para não causar confusão, alterei o artigo.

        Voltei a conexão (FDataBase) como era anteriormente, ou seja, no private. Quando lancei o artigo, dia 22/10, eu havia dito que era para excluir esta variável e que iriamos permitir acesso direto a um objeto do tipo TUIBDataBase. Mas no final , ficou sem função permitir o acesso direto à conexão.

        Quem baixou os fontes anteriormente, baixe-os novamente.

  2. Bom Luiz, como havia dito no facebook, o modelo em si, pra mim não faz muita diferença. Qualquer um dos dois será bem vindo, alias, fica a dica… o porque não os dois ? Ja que é um ORM, pode-se implementar os dois modelos e cada um usa como quiser. Agora, a minha preocupação é quanto a abertura da base de dados, se ela será aberta uma unica vez e posteriormente sua utilização com as querys devidas ou se a cada vez que precisar usa uma query, iria abrir a base. No meu ponto de vista, a segunda opção causaria uma lentidão no software, toda vez que precisa-se de uma informação, imagine isso com acesso via Internet por exemplo, na qual todos nós sabemos que o protocolo do Firebird não é la essas coisas. Uma outra dica interessante que o amigo Ricardo colocou, seria termos uma string de conexão (Produção, Teste, Desenvolvimento, etc)… acho isso muito legal, principalmente se pensarmos em Multi-empresa, sei que não seria o caso… mas poderiamos adaptar !

  3. Muito obrigado, Douglas!

    Poderemos, claro, deixar os dois modelos ativos no mesmo projeto. Só penso se isso não traria alguma confusão… ter uma forma somente simplificaria o trabalho por parte de quem for usar o código. Porém, isso não seria algo tão importante a ponto de parar o projeto, visto que ambos os modelos conseguiram atingir o objetivo inicial – conectar à base!

    Chamei o tema à tona justamente para ter um feedback e claro debater as ideias propostas no artigo. Quanto mais debatermos, mas familiar o assunto se tornará (interface, generics, singleton, etc).

    No nível atual em que se encontra o projeto, creio que o papel mais importante ele já é capaz de fazer, ou seja, nos livrar da montagem do SQL. Claro que, à media que o ORM avançar, novos requisitos e necessidades surgirão! Como por exemplo, a sugestão da string de conexão!

  4. Luiz, eu voto no modelo Generics, já pensando no futuro do ORM, eu sugiro o uso de cash de objetos Query e Coneccion usando Dicionary, para evitar ficar criando e destruindo objetos para a mesma tabela.

    1. Obrigado João Carvalho!

      Interessante a sua sugestão. A ideia aqui é manter o projeto no básico, sem tornar o código complexo demais. É ter no final, um código simples e de fácil utilização.

      Portanto, nos brinde com mais detalhes. Desenvolva sua ideia para que possamos emitir uma opinião mais precisa.

  5. Gostaria de parabeniza-lo pela sua iniciativa, seguinte o generics eu sei que funciona no D2010, mas esses novo padrao tambem funciona, ou só aparti do XE2?
    a principio fico com generics pois os frameworks de outras linguagem tambem usam fica menos dificil de compreender.

    1. Olá Leandro

      Métodos anônimos e generics já estão no Delphi desde a versão 2009. A nova RTTI a partir do 2010.

      Desta forma, se você estiver utilizando a versão 2010, creio que não terá problemas. Qualquer coisa avisa, assim poderemos ver o que pode ser feito.

      Abraços.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *