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

#compartilhandoconhecimento #wba10anos
THIS não é um método ou propriedade de classes no DELPHI como ocorre com JAVA.
Emprestando a definição do JAVA-ORACLE temos: “this is a reference to the current object — the object whose method or constructor is being called”…

Acompanhando os artigos do Marcos Douglas B. Santos em seu Blog: Object Pascal Programming por vezes versa sobre implementar uma class function a classes que instâncie e retorne o próprio objeto de preferência por uma INTERFACE.

Bem, a questão nos apresenta quando temos uma interface e precisamos obter a referência ao objeto criado pelo seu construtor. Neste casos, em geral, fazer um CAST da INTERFACE para o OBJETO não é garantia de sucesso.

Depois de lutar muito com o problema minha conclusão é que a melhor solução seria a INTERFACE retornar o próprio objeto criado em seu construtor – o THIS – como definido no JAVA.

Exemplo:


type
      TTransporteClass = class;
  

      ITransporte = interface
         {...}
         function This:TTransporteClass;
      end;


     TTransporteClass = class(TInterfacedObject, ITransporte)
       public
         class function New:ITransporte;
         function This:TTransporteClass;
     end;


   ...
    // class function para iniciar a instância
    class function TTransporteClass.New:ITransporte;
    begin
       result := TTransporteClass.create;
    end; 

    // function para obter o objeto instanciado
    function TTransporteClass.This:TTransporteClass;
    begin
      result := self;
    end;

A boa prática logo vai se manifestar com argumento de promover maior acoplamento do código – perfeitamente… neste caso retornar uma classe de nível superior pode contribuir em elevar o acoplamento do código… para isto, vamos trocar o retorno da function THIS:


      IThis = interface
         {...}
         function This:TObject;
      end; 

      ITransporte = interface
         {...}
      end;

      TTransporteClass = class(TInterfacedObject, ITransporte, IThis)
      .....



Share Button

Parte 1 – Criando o Host

Na parte 1 deste artigo tracei as linhas básicas para a implementação do Aplicativo Principal HOST – se ainda não o leu, será mais produtivo fazê-lo antes de continuar.

Criando um Plugin
O Plugin em si consiste em uma DLL que “exports” 2 entradas:

  1. function LoadPlugin(AAplication: IPluginApplication): IPluginItems;
  2. procedure UnloadPlugin;

A base para a criação do plugin é a unit: plugin.Service

LoadPlugin
“LoadPlugin” é utilizado quando o HOST inicia o plugin; O parâmetro IPluginApplication representa os serviços publicados no HOST disponíveis para assinatura pelos plugins. Assim todos os serviços que o HOST implementar deve se publicada para que sejam visíveis aos plugins.

Nos casos em que houver necessidades de estender os serviços publicados pelo HOST, IPluginApplication será a interface base publicada adicionada de novos serviços…   IMyPluginApplication = interface( IPluginInterface) …

O Plugin registra-se à lista de plugins 

IPluginItems – é uma lista de plugins disponíveis na DLL;

Para que IPluginItems seja populada o plugin poderá escolher entre os serviços padrões, aquele a que deseja assinar.

initialization
RegisterPlugin(TPluginMenuItemService.Create(TForm1,'',1, 'Menu base de sample'));

DoStart
Após a inicialização do LoadPlugin, ocorrerá um evento “DoStart” que inicializa cada um dos plugins da lista registrada. É neste momento que o plugin fará o pedido de assinatura  no HOST… Tudo isto é feito internamente pelo objeto “TPluginMenuItemService” do exemplo.

procedure TPluginMenuItemService.DoStart;
begin
  inherited;
  PluginApplication.RegisterMenuItem(FMenuItemName, GetCaption, self);
end;

Um esqueleto para um plugin de MenuItem

unit uMenuItemSimple;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  plugin.Interf, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    LabeledEdit1: TLabeledEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses plugin.Service, plugin.MenuItem, plugin.Control;

procedure TForm1.Button1Click(Sender: TObject);
begin
  close;
end;


initialization

RegisterPlugin(TPluginMenuItemService.Create(TForm1,'',1, 'Menu base de sample'));

finalization

end.

 

Share Button

Introdução
Tomando emprestado o WIKI “plugin” é um componente computacional que adiciona recursos a um programa existente. Quando um programa suporta “plugins” ele permite ser customizado para responder a necessidades não previstas no projeto original.

Uma interface de “plugin” deve prever a possibilidade de um conjunto de código ou janela permitir ser inserida em partes do programa principal.

Em um primeiro instante – é comum encontrar “plugins” que publicam alguma função ou procedimento a ser chamado pelo aplicativo principal. Este modelo limita as funcionalidade dos “plugins” que em geral ficam mais estáticos a serem um item de menu ou uma ou outra funcionalidade.

O “plugin” que irá assinar algum serviço do aplicativo

A idéia que motiva esta publicação é a construção de um modelo de aplicativo principal HOST que publica serviços implementados em seu código e ficam disponíveis para que os “plugins” possam assinar estes serviços.

Então o HOST se torna um “publisher” é o plugin um “subscriber”, onde o plugin que solicita a assinatura de um determinado serviço do HOST.

Com a mecânica em que o “plugin” assina aos serviços do aplicativo servidor permitirá que o “plugin” tome a decisão sobre qual tipo de serviço ele quer assinar no aplicativo principal . Um exemplo é um “plugin” assinar  para ser um item do menu –  em outros casos poderá assinar funcionalidade de uma “aba” de janela ou adicionar “frames” a alguma interface do usuário – entregando mais poder de escolha ao “plugin”.

Segurança x Flexibilidade

Ainda que “flexibilidade” seja o principal objetivo, segurança segue o mesmo caminho da flexibilidade. Há que se questionar o quanto um HOST pode ser seguro o bastante para não permitir um assinante malicioso. Não tenho resposta para a questão, já que um “plugin” maldoso poderia assinar a serviços para aplicar táticas maliciosas – questão que precisa ser avaliada.

diagrama

Uma interface para publicar serviços

Considerando que o programa principal tem um formulário:

TMeuMenu = class(TForm)

end;

o que precisamos fazer é dotar o formulário principal de uma interface que permita publicar os seus serviços:

  IPluginApplication = interface
    ['{6ED989EA-E8B5-4435-A0BC-33685CFE7EEB}']
    procedure RegisterMenuItem(const AParentMenuItemName, ACaption: string; ADoExecute: IPluginMenuItem);
    procedure RegisterToolbarItem(const AParentItemName, ACaption: string;  ADoExecute: IPluginToolbarItem);
    procedure RegisterAttributeControl(const AType,ASubType: Int64;  ADoExecute: IPluginControl);
  end;

Com isto o formulário passa a implementar a interface de serviço de “plugins” da seguinte forma:

TMeuMenu = class(TForm, IPluginApplication)
….
end;

 

Registrando o Plugin para o Aplicativo Principal

Antes de carregar um “plugin” o aplicativo host precisa conhece-lo (uma lista de plugins registrados). Uma forma simplista, é guardar uma lista de “plugins” disponíveis em um arquivo INI do host. A lista é necessária para que o aplicativo possa fazer carga dos seus “plugins”. Uma vez feito a carga do plugin, estes  irão assinar os serviços a que desejam interagir com o host.

O framework utiliza a implementação de DLLs para troca de código com o aplicativo principal, publicando duas chamadas – uma para carga inicial da DLL e outra para quando o aplicativo principal esta encerrando o uso do plugin:

function LoadPlugin(AAplication: IPluginApplication): IPluginItems;
procedure UnloadPlugin;
...
exports LoadPlugin, UnloadPlugin;

Carregando o Plugin
Sabedor destas duas entradas disponíveis no “plugin”, resta escrever os métodos de carga do plugin (ver TPluginManager nos fontes):

function LoadPluginService(APlugin: string;
  AAppliction: IPluginApplication): Integer;
var
  F: function(APApplication: IPluginApplication): IPluginItems;
  H: THandle;
begin
  result := -1;
  H := LoadLibrary(PWideChar(APlugin));
  if H > 0 then
  begin
    try
      @F := GetProcAddress(H, 'LoadPlugin');
      if assigned(F) then
        result := LPluginManager.FPlugins.Add(H, F(AAppliction))
      else
        raise Exception.Create('Não carregou o plugin');
    except
      FreeLibrary(H);
    end;
  end;
end;

Note que a função “LoadPlugin” do plugin espera que seja enviado um IPluginApplication e retorna uma lista de plugins existente na mesma DLL, já que uma DLL pode conter 1 ou mais plugins.

O IPluginApplication, representa a interface do HOST que publica os serviços que os plugins poderão assinar no HOST – Internamente a DLL irá montar uma lista de plugins disponíveis. Cada plugin da DLL se encarrega de assinar os serviços do HOST.

Estendendo os serviços do HOST

A interface que publica os serviços no HOST é o IPluginApplication que já possui três serviços básicos para a assinatura sendo eles:

  1. MenuItem – Assinatura para se registrar em um item de menu do HOST;
  2. ToolbarItem – Assinatura para se registrar em um item da Toolbar;
  3. AttributeControl – Uma assinatura a utilizar o plugin como um “control” ou atributo de um janela.

Caso deseje implementar outros serviços para o HOST, estendendo suas funcionalidades é possível estender a interface IPluginApplication e implementa-la no HOST:

IMyPluginApplication  =  interface(IPluginAppliction)
     procedure   RegisterXXX(....);
end;

// no host
 TMyMainMenu = class(TForm,  IMyPluginApplication )
 ...
 end;

 

Parte 2 – Construindo o Plugin

(No final o código estará disponível no GIT)
….

Share Button

Quando estava preparando os exemplos de código para o artigo sobre ForIn para FireDAC, lembrei de um artigo do meu amigo Marcos Douglas publicado no Object Pascal Programming que discorre sobre a programação Imperativa ou Estruturada.

Na Ciência da Computação, programação imperativa é um paradigma de programação que descreve a computação como ações, enunciados ou comandos que mudam o estado (variáveis) de um programa. Muito parecido com o comportamento imperativo das linguagens naturais que expressam ordens, programas imperativos são uma sequência de comandos para o computador executar. (Wikipedia)

O artigo traz como objetivo discutir o fim do FreeAndNil propondo a utilização de Interface na programação PASCAL. Extraindo o conceito é possível escrever código utilizando diretamente interface o que demostra o poder da linguagem frente aos novos paradigmas.

Reescrevendo TFDQuery com interface, podemos fazer:

   TQueryIntf.New(FDConnection1)
      .Table('sigcad a')
      .FieldNames('codigo,nome')
      .Where('codigo between :codigo_de and :codigo_ate')
      .ParamValue('codigo_de', 1)
      .ParamValue('codigo_ate',5)
      .open
      .DoQuery( procedure (ds:TDataset)
          begin
            memo1.Lines.Add( 'Carregou: '+intToStr(ds.RecordCount) )
          end);