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…

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 ...

Desenvolvedor de software desde 1995. Em 1998, abriu sua própria empresa, a Lukas Sistemas, desde então passou a atender diversas empresas, principalmente autopeças. Apaixonado por Delphi, porém não o impede de flertar com outras linguagens sempre que possível. Mora na cidade de Balsas/MA com sua esposa e dois filhos.

23 thoughts on “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. Olha, por enquanto não vou opinar, mas estou acompanhando e entendendo bem melhor o Generics no Delphi. Continue postando..Valeu pelos artigos!!

  3. 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 !

  4. 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!

  5. 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.

  6. 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.

  7. Caro, não poderia deixar de agradecer!

    Excelente dissertação! Faço esse comentário após ler até o Post da Parte 9…

    Raras vezes encontrei um material de tão excelente qualidade, um material que nos levasse do início ao fim do raciocínio sem deixar nada para traz, e o mais importante, totalmente funcional!

    Vale lembrar que até aqui, já estou executando os testes convertendo o ORM para trabalhar com FireDAC. Funcionando Legal!

    Se possível, poderia disponibilizar o Source para um Merge?

    Abraço e muito sucesso por ai! Vamos aos próximos Posts, ansioso para ver as demais maravilhas… Hehehe!

    1. Opa, fala Charles. Obrigado pelo comentário.

      Com relação ao merge, até disponibilizei os fontes no github, porém, no momento, estou envolvido na criação de um curso para este espaço: http://www.cursos.luizsistemas.com.br e fora que a Receita irá eliminar a NFe 3.1 em agosto, então já viu, né! rsrss A correria como sempre está uma doidera aqui.

      Então me falta tempo para voltar a abrir este código e ver todas as atualizações que ainda não subi para o git. Só para você ter ideia, já prorroguei a abertura das inscrições do curso por 3 vezes.

Deixe um comentário para Ricardo Cancelar resposta

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

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.