Share Button

Inspecionando o código MVCBr irá notar que o uso de interface é predominante às diversas classes do framework.

base
Uma variável tipada por uma interface é diferente de uma variável “object” por estabelecer um independência entre o objeto e a implementação de classe do objeto. Como é comum, se diz que a interface é um contrato estabelecido entre o cliente e o fornecedor – No delphi pascal, o objeto representado por uma instância não requer o tradicional FREE para liberar o objeto. A linguagem estabelece um contador incremental de referência para a instância a cada chamada e o decrementa a medida que não seja necessária, quando alcança um “count=0” o objeto é liberado da memória.

elementar meu caro
Se o objeto é liberado de forma automática “gerenciada pelo gerenciador”, então não é necessário FREE ou DisposeOf. Em alguns trechos de código é comum encontrar uma atribuição NIL para forçar o decremento do contador de referência – com isto força a liberação da instância da memória mesmo antes de terminar o execução do trecho de código.

Quando usar Create()
O uso do “Create(..)” permite criação da classe pascal nativa “SEM” fazer uso da interface. Nestes casos a liberação do objeto da memória se dará fazendo uma chamada FREE ou DisposeOf para o mesmo;

Usando New(..)
Em geral, o uso do NEW é implícito o desejo em obter uma instância representado pela interface que a classe implementa. Como uma interface, a instância dispensa o Free ou DisposeOf, fica a cargo do contador de referência retirar o objeto da memória.

Posso converter um Objeto em uma representação Interface ?

Se o objeto em questão implementa uma interface a resposta será SIM. Toda classe que implementa uma interface pode ser criada com CREATE e atribuído a uma variável tipada para a interface que implementa. Neste caso o gerenciamento de memória é feito pela representação “Interface” e não requer Free ou DisposeOf;

Exemplo:

   type
       IMinhaClasse = interface
          [...]
       end;
       TMinhaClasse = class(TInterfacedOjbect, IMinhaClasse)
       end;

....

var LMinhaClasse : IMinhaClasse;
...
   LMinhaClasse := TMinhaClasse.create;

No caso “LMinhaClasse” não requer Free nem mesmo DisposeOf, já que foi tipada por uma interface.

Ver também – Um Adapter Interfaced para qualquer objeto

Share Button

Precisa notificar o usuário sobre algum evento?
Então você precisa de Windows Notification na sua aplicação.

base
No Windows 10 está disponível o envio de mensagens para a bandeja do windows alertando sobre alguma ocorrência.

elementar meu caro
Usar a notificação do windows é bastante simples, com a ajuda de um model do MVCBr que se encontra na pasta \AdIN\Windows base instanciar o “model” e sair utilizando – se a utilização é eventual, nem mesmo instanciar será necessário;

Unit: WinNotification.Model, WinNotification.Model.Interf;


  /// declaração da classe
   TWinNotificationModel
      ... 
    class function Notify(const AName: String; const ASubject: String;
      const AMessage: String): boolean;
   end;



  /// enviando mensagens para o windows
  TWinNotificationModel.Notify('Ola Windows',
        'Horário Visita se aproximando','Não esqueça de sair para a visita');


Código de Exemplo

Share Button

Tenho um objeto implementado utilizando uma herança nativa da DELPHI e gostaria de fazer uso de INTERFACE para este objeto sem precisar implementar uma nova classe.

base
Um patterns adapter objetiva gerar um adaptador que recebe um objeto externo e implementa novas funcionalidade.

elementar meu caro
Não estamos falando de herança, mas de um adaptador ao objeto que adiciona funcionalidade para o qual não foi desenhado originalmente.
Destaco como o adapter responde como se fosse o próprio objeto expondo procedures, funções e atributos diretos sem fazer CAST dos mesmos.

Unit: MVCBr.Patterns.Adapter;

uses MVCBr.Patterns.Adapter;

Type
    // objeto base a ser adaptado
    TMeuObjeto = Class(TObject)
       public
         texto:string;
    End;



procedure TForm85.FormCreate(Sender: TObject);
var
   LAdapter : IMVCBrAdapter<TMeuObjeto>;
begin

   LAdapter := TMVCBrAdapter<TMeuObjeto>.create(nil);
   // atribuir valor
   LAdapter.texto := 'texto de exemplo';

   // pegando o valor do objeto
   ShowMessage(  LAdapter.texto  );

end;

Share Button

Depois do XML, JSON é a mais produtiva ferramenta para trocar de informações entre diferentes objetos ou diferentes plataformas.

base
JSON é uma notação para estrutura Chave/Valor criada originalmente em JavaScript. Comparativamente ao XML, JSON é mais compacto e totalmente transparente em objetos JavaScript o que o popularizou como a estrutura mais adequada de troca de informações entre servidor e cliente.

elementar meu caro
Quando um objeto possui um estrutura de dados e se deseja enviar ou converter em outra classe ou objeto, o JSON deve ser a primeira opção para facilitar a troca de dados entre os atores da operação.
Usar uma interface tem como foco delegar ao aplicativo decidir o momento adequado para retirar a instância da memória, não precisando se preocupar com o FREE do objeto implementando algo mais próximo de um coletor de lixo existente e outras linguagens.

como usar

TInterfacedJSON representa uma implementação da interface IJSONObject para um objeto TJSONObject.

  • Unit: System.JSON.Helper;

    Gerando o IJSONObject para o dado:

    uses System.JSON.Helper;
    procedure TForm84.FormCreate(Sender: TObject);
    var
      AJson, ANome: string;
      LJson: IJsonObject;
      ACliente: Integer;
    begin
    
      AJson := '{ "cliente":1,"nome":"jose de maria" }';
    
      LJson := TInterfacedJSON.new(AJson);
      with LJson.JsonObject do
      begin
        /// pegando nome do cliente
        ANome := s('nome');
        ACliente := I('cliente');
      end;
    
      // adicionando novo key/value
      LJson.addPair('fone':'9999-9999');
    
      //....
    end;
    
  • Unit: Data.DB.Helper
    Trocando informações entre um JSON e um TDataset:

    procedure TForm84.Button1Click(Sender: TObject);
    var LJson:IJSONObject;
    begin
    
        LJson := TInterfacedJSON.new;
        LJson.addPair('cliente',1);
        LJson.addPair('nome','jose de maria');
    
        FDMemTable1.open;
        FDMemTable1.edit;
    
        // carrega os fields com o valor do JSON
        // se o JSON for um ARRAY será carrega cada posição no array como uma nova linha na tabela.
        FDMemTable1.FromJsonObject(LJson.JsonObject,True);
    
        
    
        FDMemTable1.post;
    end;
    

    Para obter um JSON do TDataset:

       // gera um array para todas as linhas
       FDMemTable1.ToJson;
    
    
       // gera o JSON somente para a linha corrente
       FDMemTable1.Fields.ToJson;
    
    
  • Unit: System.Classes.Helper;
    Lidando JSON com Objetos:

       // gera um JSON de um objeto
       MeuObjeto.ToJson;
    
    
    
       // carregando o objecto com os valor de um JSON
       MeuObjeto.FromJson(  ... );
    
    

Share Button

ForEach faz uso de funções anônimas para realizar a tarefa.

base
Funções e/ou Procedures anônimas são úteis para gerar mecanismo de eventos sem necessidade de criar um evento para objeto. O uso de um evento é obrigatório a existência de uma classe para injetar o evento a ser chamado. Com anônimos é possível injetar um evento a qualquer chamada, não demandando a obrigação da criação de uma classe.

elementar meu caro
Um anônimo pode figurar como um “procedure” (sem retorno) ou uma “function” (com um valor de retorno). Usar como procedure ou função é dependente do objetivo que o CODER pretende empregar ao seu código, se deseja um valor de retorno a resposta será usar um função nas demais irá preferir usar um procedure.

prática

  • Unit: MVCBr.ApplicationController
    ForEach(….): executa um loop sobre uma lista de itens enquanto existir item ou a Função Anônima retornar FALSE;
    Exemplo:

    function TApplicationController.FindModel(AGuid: TGuid): IModel;
    var
      rst: IModel;
    begin
      rst := nil;
      ForEach(
        function(ACtrl: IController): boolean
        begin
          result := false;
          ACtrl.GetModel(AGuid, rst);
          if assigned(rst) then
            result := true;
        end);
      result := rst;
    end;
    
  • Unit: Data.DB.Helper
    ForEach em um objeto TDataset:

    ForEach(function (ds:TDataset):boolean
       begin
            /// executa para cada item do dataset
            result:= false;  /// não finalizar o loop e ir para o proximo
       end);
    

Share Button

Sabe aqueles momento que você queria ser uma mosquinha para ver o que o usuário está fazendo? Seus problemas estão resolvidos, vamos fazer um monitoramento remoto do aplicativo sem ter IDE instalado lá.

Contexto: O sistema operacional windows possui um mecanismo de propagação de mensagens para o DebugView. É o modelo de monitoramento nativo do windows para comunicação de ocorrências internas ao processo do próprio windows.
Download: DebugView

DebugView is an application that lets you monitor debug output on your local system, or any computer on the network that you can reach via TCP/IP. It is capable of displaying both kernel-mode and Win32 debug output generated by standard debug print APIs, so you don’t need a debugger to catch the debug output your applications or device drivers generate, and you don't need to modify your applications or drivers to use non-Windows debug functions in order to view its debug output.

Instalando o servidor

Se o DebugView for utilizado localmente, basta carregar o aplicativo para a memória que ele já inicia a captura das informações transmitidas pelo windows; Caso deseje monitorar remotamente utilize a opção “dbgview /a” ao chamar o DebugView (consultar o manual para mais opções ou /?); “/a” iniciará o DebugView como um servidor TCP/IP que permite conexão a partir de estações remotas;

Instalando o cliente

Quando o DebugView está rodando em uma máquina remota é possível conectar diretamente através da opção de menu “Computer/Connect” indicando o IP onde está rodando o servidor.

Preparando o aplicativo DELPHI

No aplicativo DELPHI inserir chamadas para a função:

// uses Winapi.Windows;
procedure OutputDebug(const txt: string);
var
  i: integer;
  x: integer;
const
  n = 1024;
begin
{$IFDEF MSWINDOWS}
  try
    i := 0;
    x := length(txt);
    repeat
      OutputDebugString({$IFDEF UNICODE}PWideChar{$ELSE}PAnsiChar{$ENDIF}(ExtractFileName(ParamStr(0)) + ':' + copy(txt, i + 1, n)));
      i := i + n;
    until i >= x;
  except
  end;
{$ENDIF}
end;

Inserindo a chamada a procedure “OutpuDebug” tudo que for passado como parâmetro será transmitido via DebugView diretamente para o cliente que estiver monitorando o servidor sem necessidade de rodar dentro da IDE do Delphi para receber notificações do aplicativo.

Share Button

Por algum tempo fiquei pensando em uma solução para Class Helper ganhar poder de adicionar uma nova variável. Confesso que já estava desistindo, parecia não ser possível… mas – pensando bem….

Digamos que você tenha uma classe base que gostaria de fazer um Class Helper dela:

Exemplo de uma classe original:

     
     TComponentX = class(TComponent)
      public
      published
     end;

(Code:A)

O desafio é como incluir uma nova variável a classe TComponentX utilizando de Class Helper…

Razões para usar um "Class Helper" e não uma herança: 
    se houver possibilidade de injetar a variável através de herança, 
    é provável que não irá precisar criar um "Class Helper" 
    - as razões para criar um "Class Helper" 
    é dispensar mudanças nos objetos em units já implementadas.

Agora, vamos usar o “Class Helper” para injetar uma nova variável à classe TComponentX:

    TComponentHelper = Class Helper TComponent
      public
         Ligado:Boolean;
    end;

(Code:B)

Se estiver habituado ao uso de Class Helper, já deve ter notado que este exemplo (Code:B) não é compilado no Delphi – reclamando que um “Class Helper” não pode inserir uma nova variável. Não seja apressado, já lembrou que existe um “Class Var” e logo sugere escrever assim: ” class var Ligado:Boolean; “… Se este for o seu caso, não tenho boa notícia – você terá criado uma única referência para a variável, não podendo receber valores diferentes nas várias instâncias que a aplicação desejar manter carregada na memória – ou seja, criou uma simples variável GLOBAL que é compartilhada com todas as instâncias da classe;

No nosso caso, queremos que cada instância tenha o seu próprio valor, portanto não pode utilizar um “Class Var” como solução.

PROPOSTA:

unit Unit1;

interface

uses System.Classes, System.Generics.Collections;

type

  TComponentHelper = Class Helper for TComponent
  protected
    function GetLigado: boolean;
    procedure SetLigado(const AValue: boolean);
  public
    property Ligado: boolean Read GetLigado write SetLigado;
  end;

implementation

var
  LComponentLigado: TDictionary<TComponent, boolean>;

function TComponentHelper.GetLigado: boolean;
begin
  result := true; // responde um padrão, para quando não existir;
  if LComponentLigado.ContainsKey(self) then
    result := LComponentLigado.Items[self]; // pega o valor da lista
end;

procedure TComponentHelper.SetLigado(const AValue: boolean);
begin
  LComponentLigado.AddOrSetValue(self, AValue); // inclui na lista
end;

initialization

LComponentLigado := TDictionary<TComponent, boolean>.Create;

finalization

LComponentLigado.free;

end.

A implementação permite adicionar novas variáveis ao objeto do “Class Helper” sem interferir no funcionamento da classe padrão.

Share Button

ORMBr é um framework que vejo ser da maior importância para elevar o CRUD a um novo patamar – vamos dar uma mãozinha para torná-lo mais RAD.

Com o uso de Templates é possível automatizar alguma coisa rotineiras no DELPHI e o deixar mais produtivo. Hoje vamos a um exemplo de como fazer isto.

Primeira informação a saber é que um Template é um XML estruturado que pode ser escrito até no EDITOR de NOTAS, como meu amigo IVAN gosta de fazer ;-).

Quando o Delphi é carregado ele lê os XMLs na pasta de usuário:

  // pasta de templates
  C:\Users\USUARIO\Documents\Embarcadero\Studio\code_templates\Delphi

Vamos criar um arquivo com nome: ORMBr.CreateEntity.xml e copiar para a pasta de templates.

Conteúdo do XML:

   <?xml version="1.0" encoding="utf-8" ?>
<codetemplate	xmlns="http://schemas.borland.com/Delphi/2005/codetemplates"
				version="1.0.0">
	<template name="ormbr.createEntity" invoke="manual">
		<description>
			 Cria modelo para uma tabela
		</description>
		<author>
			 amarildo lacerda
		</author>
        <point name="table">
          <text>table</text>
          </point>
        <point name="description">
          <text>description</text>
          </point>
		<code language="Delphi" delimiter="|"><![CDATA[


interface

uses
  DB,
  Classes,
  SysUtils,
  Generics.Collections,
  /// orm
  ORMBr.types.blob,
  ORMBr.types.lazy,
  ORMBr.types.mapping,
  ORMBr.types.nullable,
  ORMBr.mapping.Classes,
  ORMBr.mapping.register,
  ORMBr.mapping.attributes;

type

  [Entity]
  [Table('|table|', '|description|')]
  T|table| = class
  private

  public

  end;


implementation

initialization

TRegisterClass.RegisterEntity(T|table|);

end.

           ]]>
		</code>
	</template>
</codetemplate>

Se tudo correr bem, agora quando você digitar no editor de código -> “orm + CTRL+J” deverá paracer na lista de opções de templates uma opção “ormbr.createEntity” ….

Share Button

Trocar informações entre RECORDs com estrutura diferentes pode ser um trabalho relativamente cansativa. Uma opção é converter o RECORD de origem em JSON e em seguida carregar o JSON no RECORD de destino.
Executar esta atividade é possível fazendo uso das classes RTTI do delphi. Então a idéia mais básica é criar métodos para gerar JSON e carga do JSON com o RECORD desejado.

Uma proposta utilizando generics para lidar com JSON fontes:


 TJsonRecord<T: Record > = class
  public
     /// gerar JSON
    class function ToJson(O: T; const AIgnoreEmpty: Boolean = true;
      const AProcBefore: TProc < TJsonObject >= nil): string;
     /// carregar um JSON
    class procedure FromJson(O: T; AJson: string);
  end;

Considerando uma estrutura de RECORD (origem e destino):

     TRecordOrigem = record
       nome:string;
       email:string;
     end;


     TRecordDestino = record
       nome:string;
       email:string;
       outros:integer;
     end;

A estrutura de identificação do RECORD por generics tem por finalidade aplicar RTTI para descobrir quais os FIELDS disponíveis no RECORD a ser transformado. Assim quando queremos gerar o JSON, fazemos:

   var sJson:string;
       dados : TRecordOrigem;
   ...
   dados.nome := 'xxxx';
   dados.email := 'yyyy';

   sJson := TJsonRecord<TRecordOrigem>.ToJson(dados);   // retorna o JSON

No RECORD de destino, fazendo a carga do JSON:

   var destino:TRecordDestino;
   ...

   TJsonRecord<TRecordDestino>.FromJson(destino,sJson);

Share Button

“Observer” é inigmático. Você nem esperava e chega aquela notícia que alguma coisa aconteceu. A janela estava lá mostrando uma fila de clientes em atendimento, mas de repente alguém gritou que chegou um cliente com preferência no atendimento e a fila precisa ser reorganizada para acomodar aquele cliente com preferência… Certamente você já viu um cenário deste ou participou de uma fila destas.
Vejamos os atores para o cenário: de uma lado existe alguém pronto para receber a informação de necessidade de reorganizar a fila, ele fica lá só esperando alguém gritar – de outro tem um lá na porta da instituição com sede de saber novidades para sair falando para todo mundo.

Trabalhar com “Observer” é em outras palavras montar uma rede de informação de notícias. Existe um ator que assina o serviço de notícia e outro que distribui as notícias – é isto que um “patterns observer” faz.

Olhando para o MVCBr, o primeiro passo é assinar o serviço de notícias (aquele que fica esperando uma notícia para tomar uma ação):

   uses MVCBr.Observable;

   ... on form show
   // assinar o serviço de notícias
   TMVCBrObservable.subscribe( form1, 'reorganizar.fila',  
                               procedure (json:TJsonValue)
                               var nQtde:integer;
                               begin
                                   nQtde = json.getvalue<integer>('qtde');
                                   abrirVagaNaFila(nQtde);
                               end);

   .... on destroy
   // retirar a assinatura do serviço
   TMVCBrObservable.UnSubscribe(form1);

Do outro lado esta o distribuidor de notícias, aquele que grita que precisa entrar um cliente preferêncial na fila:

   uses MVCBr.Observable;
   ..
    
   // em algum lugar em que a notícia nasce
   TMVCBrObservable.send(  'reorganizar.fila',   TJsonObject.parse(   '{"qtde":1}' ) );


Quando o “observer” recebe a notícia, ele com sede de espalhar a notícia, sai procurando todo mundo que se candidatou para receber aquele serviço… e pá… manda a notícia uma-a-uma para cada assinante.

Aproveitem: git