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:

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

….

var LMinhaClasse : IMinhaClasse;

LMinhaClasse := TMinhaClasse.create;

[/code]

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

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;
[code]

/// 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’);

[/code]

Código de Exemplo

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;

[code]
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;
[/code]

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:

    [code]
    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;
    [/code]

  • Unit: Data.DB.Helper
    Trocando informações entre um JSON e um TDataset:
    [code]
    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;
    [/code]

    Para obter um JSON do TDataset:

    [code]
    // gera um array para todas as linhas
    FDMemTable1.ToJson;

    // gera o JSON somente para a linha corrente
    FDMemTable1.Fields.ToJson;

    [/code]

  • Unit: System.Classes.Helper;
    Lidando JSON com Objetos:
    [code]
    // gera um JSON de um objeto
    MeuObjeto.ToJson;

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

    [/code]

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:
    [code]
    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;
    [/code]
  • Unit: Data.DB.Helper
    ForEach em um objeto TDataset:
    [code]
    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);
    [/code]

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:

[code language=”pascal”]
// 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;
[/code]

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.

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:
[code]

TComponentX = class(TComponent)
public
published
end;

[/code]
(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:
[code]
TComponentHelper = Class Helper TComponent
public
Ligado:Boolean;
end;
[/code]
(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:
[code]
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.
[/code]

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

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:

[code lang=”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>
[/code]

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

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:

[code]

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;

[/code]

Considerando uma estrutura de RECORD (origem e destino):

[code]
TRecordOrigem = record
nome:string;
email:string;
end;

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

[/code]

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:

[code]
var sJson:string;
dados : TRecordOrigem;

dados.nome := ‘xxxx’;
dados.email := ‘yyyy’;

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

[/code]

No RECORD de destino, fazendo a carga do JSON:

[code]
var destino:TRecordDestino;

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

[/code]

“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):

[code]
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);

[/code]

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

[code]
uses MVCBr.Observable;
..

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

[/code]

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