Fala ai Radizeiros e Radizeiras, tudo bem com vocês?

Como nós que trabalhamos com as rotinas fiscais em nossos software usando o ACBr para facilitar as implementações, nos deparamos com inúmeras linhas de códigos, e as vezes isso nos atrapalha.

Nessa série de post que estou publicando aqui no blog, mostro como aplicar as boas práticas para essas rotinas fiscais, e hoje estarei mostrando como reduzir o acoplamento e gerenciar o consumo de memória do ACBr.

Tem uma questão que muita gente que começa a trabalhar com interface acaba se complicando no gerenciamento de memória, quando tenta fazer isso que nós fizemos no post anterior, caso não tenha visto clique aqui, onde tenho um DataModule que implementa uma interface.

Nessa nossa implementação eu poderia colocar o que é de costume, que seria uma class function new ficando desta forma.

...
public
    class function new : iModelFiscalNFeComponentes<TACBrNFe>;
...
class function new : iModelFiscalNFeComponentes<TACBrNFe>;
begin
    Result := Self.Create(nil);
end;

Quando chamar o new ele irá me retornar uma interface, se ele retorna uma interface, nós temos o ARC (Automatic reference count) do Delphi que já faz esse gerenciamento de memória para nós.

Só para titulo de teste, irei colocar um botão no meu formulário, camada de visão, onde dentro do onClick irei chamar o meu DataModule do ACBrNFe.

...
procedure TForm2.Button1Click(Sender: TObject);
begin
    TdmACBrNFe.New;
end;
...

Você pode observar na imagem abaixo que ele retorna a interface.

Dentro do meu projeto em View Source, logo após o Initialize irei colocar o ReportMemoryLeaksOnShutdown.

begin
    Application.Initialize;
    ReportMemoryLeaksOnShutdown := True;
    Application.MainFormOnTaskbar := True;
    Application.CreateForm(TForm2, Form2);
    Application.Run;
end.

Foi adicionado para que ele retorne os memory leak do projeto.

Como nós instanciamos nosso DataModule usando o New, teoricamente ele não tem que dar o memory leak porque estou trabalhando com interface.

Após compilar nosso projeto e clicar no botão ele irá gerar a classe, após fechar nossa aplicação.

Olha quanto memory leak, mas o Delphi não gerencia isso para nós?

Teoricamente sim, só que temos um problema que é o seguinte, quando dizemos que nossa classe herda de TDataModule, e logo ela herda de TComponent, e o TComponent sobrescreve os métodos do TInterfacedObject que são.

...
{ IInterface }
function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
...

Ele faz um tratamento diferente para esses métodos da interface do que o TInterfacedObject faz, isso faz com que de problemas no gerenciamento de memória.

Mas como eu faço para resolver isso?

O que deve ser feito para resolver esse problema, é fazer o gerenciamento de memória na mão, é o jeito mais fácil de resolver  esse problema.

Para isso iremos fazer desta forma.

...
procedure TForm2.Button1Click(Sender: TObject);
var
    a : TdmACBrNFe;
    b : iModelFiscalNFeCompoentes<TACBrNFe>;
begin
    a := TdmACBrNFe.Create(nil);
    try
        b := a;
    finally
        a.DisposeOf;
    end;
end;
...

Observe que eu instancio minha classe do TdmACBrNFe e gerencio ele dentro do try finally, onde a variável a recebe a instancia da classe TdmACBrNFe e logo a variável b recebe a instância de a, como implementa a mesma interface da classe TdmACBrNFe então ele pode receber a variável a.

Desta forma nós conseguimos trabalhar com a interface sem que venhamos ter o vazamento de memória.

Após fechar não temos o vazamento de memória, fazendo assim estamos gerenciando todo esse vazamento manualmente.

Agora iremos criar uma Factory, para gerar o DataModule e já fazer o gerenciamento da memória.

Na unit de interface iremos criar uma interface para a Factory.

iModelFiscalNFeFactory = interface
    ['{ED5E5B80-22AD-49C9-BCB9-82652E734A38}']
    function ACBr : iModelFiscalNFeCompoentes<TACBrNFe>;
end;

Observe que no método ACBr está retornando a interface genérica que está recebendo a classe TACBrNFe,  desta forma estamos gerando um acoplamento para o componente ACBr, mas como estamos trabalhando com ele optamos dessa forma.

Como o ACBr não é interfaceado da muito mais trabalho, tende que escrever muita coisa para criar uma camada de abstração.

Vamos agora criar nossa classe de Factory, onde todo componente que for criado, será criado por essa nossa Factory.

unit Blog.Model.Fiscal.NFe.Componentes.Factory;

interface

uses
   Blog.Model.Fiscal.NFe.Componentes.Interfaces, ACBrNFe,
   Blog.Model.Fiscal.NFe.Componentes.ACBr;

type
    TModelFiscalNFeComponentesFactory = class (TInterfacedObject, iModelFiscalNFeFactory)
    private
        FACBr : TdmACBrNFe;
    public
        constructor Create;
        destructor Destroy; override;
        class function New : iModelFiscalNFeFactory;
        function ACBr : iModelFiscalNFeCompoentes<TACBrNFe>;
    end;

implementation

uses
    System.SysUtils;

{ TModelFiscalNFeComponentesFactory }

function TModelFiscalNFeComponentesFactory.ACBr: iModelFiscalNFeCompoentes<TACBrNFe>;
begin
    if not Assigned(FACBr) then
        FACBr := TdmACBrNFe.Create(nil);
    Result := FACBr;
end;

constructor TModelFiscalNFeComponentesFactory.Create;
begin
end;

destructor TModelFiscalNFeComponentesFactory.Destroy;
begin
    if Assigned(FACBr) then
        FreeAndNil(FACBr);
    inherited;
end;

class function TModelFiscalNFeComponentesFactory.New: iModelFiscalNFeFactory;
begin
    Result := Self.Create
end;

end.

Observe que possuímos um objeto do tipo do nosso DataModule, e dentro do método ACBr estamos verificando se está criado o DataModule, e no Destroy caso esteja instanciado limpamos da memória.

Desta forma já resolvemos o problema de memory leak do ACBr.

Agora quando vamos para View para testarmos o problema de memória do DataModule.

Ao clicarmos no botão do nosso formulário e ao invés de chamarmos diretamente o componente do ACBr, nós chamamos a nossa Factory, a minha fábrica de componentes.

...
procedure TForm2.Button1Click(Sender: TObject);
begin
    TModelFiscalNFeComponentesFactory.New.ACBr;
end;
...

Executando o projeto…

Após fecharmos não encontramos o erro de memory leak.

A princípio esse é o trabalho que tivemos para desacoplar o componente do ACBr dos nossos formulários de tudo que iremos usar.

Tudo que precisarmos usar do ACBr, vamos pedir para a nossa fabrica.

Observe que ao passarmos o método _this temos todos os métodos usado pelo componente do ACBrNFe, e minha View não possui nenhuma acoplamento do componente do ACBr, não existe declarado nas Uses da view o componente do ACBr, a única declaração que temos é o da fábrica de componentes.

Seguindo esses padrões já conseguimos reduzir o acoplamento, eu continuo tendo acesso aos métodos do componente ACBr sem precisar ter o ACBr acoplado no meu formulário ou em qualquer lugar.

Viu como vamos melhorando todo nosso código para implementação do ACBrNFe, esse é apenas o primeiro post da nossa série de Boas práticas para geração de arquivos fiscais com ACBr, este post foi extraído de um dos meus treinamentos que ensino todas as técnicas de boas práticas com clean code para geração de arquivos fiscais.

Com as técnicas aplicadas nesse treinamento, alem de aprender a aplicar na criação e emissão da NF-e, você pode também aplicar facilmente para o SPED e o SINTEGRA, ou seja, o que é problema para você hoje, depois desse treinamento você irá enxergar como oportunidade.

CLIQUE AQUI PARA SABER MAIS SOBRE O TREINAMENTO BOAS PRÁTICAS PARA GERAÇÃO DE ARQUIVOS DISCAIS COM ACBr