terça-feira, 2 de julho de 2013

Class Helpers

A orientação a objetos, apesar de um conceito com certa idade (ele nasceu na década de 1960!), para muitos continua um grande mistério. A linguagem Object Pascal segue muitos dos preceitos da orientação a objetos, como herança, polimorfismo, abstração, generalização e especialização, todos devidamente consolidados. Contudo, alguns destes preceitos, como generics – o qual discutiremos mais tarde – e Class Helpers, permanecem obscuros.

Class Helpers são grandes aliados no desenvolvimento orientado a objetos, usados como extensores de classes, porém, sem a necessidade de estendê-las. Com isso, podemos separar em units diferentes recursos de uma mesma classe.


Saber usá-los pode ser um grande trunfo, principalmente em equipes de desenvolvimento colaborativo ou com um grande número de desenvolvedores envolvidos.



O que são Class Helpers?



Como o nome sugere, e em uma tradução livre do termo, eles são ajudantes para classes já desenvolvidas que precisam de manutenção, principalmente implementações ou extensões, sem a necessidade de estender-se esta classe em outra. Ela funciona como um acessório que, quando instalado, passa a ser parte integrante do objeto que o contém.

Imagine um carro qualquer. Pode ser aquele carro popular que tanto vemos nas ruas nos dias de hoje. Um carro popular, por uma razão lógica de valores e, principalmente, de consumo de combustível, não conta com ar condicionado de série (na maioria dos casos). Então, para este veículo, refrigerar o ar em seu interior é impossível.

Contudo, se instalarmos um equipamento de ar condicionado neste automóvel, ele passará a ser capaz de realizar esta tarefa. Assim, nosso carro popular poderá nos manter confortáveis, mesmo com um calor terrível.

Por que usá-los?

Da mesma forma que montadoras de veículos não constroem todos os componentes de seus produtos, um único desenvolvedor dificilmente ficará responsável por todos os componentes de software de um sistema médio ou grande. O desenvolvimento provavelmente será dividido entre outros programadores, assim como fornecedores de autopeças ou componentes desenvolvem seus produtos separadamente, para que estes, ao final de todo o processo, possam constituir um único produto.

Quando coordenamos ou integramos equipes de desenvolvimento onde vários profissionais estejam envolvidos, precisaremos criar mecanismos para evitar que o trabalho de um profissional interfira ou anule o trabalho de outros. Para isso, lançamos mão do recurso de Class Helpers, para que a colaboração ou as tarefas individuais possam ser desempenhadas sem prejuízos para o projeto.


Outra utilização é quando precisamos estender classes que não temos como estender por estarem seladas ou por não podermos controlar o produto final de uma extensão, como, por exemplo, as propriedades Lines/Strings, ambos descendentes do tipo TStrings.



Uma sintaxe estranha, só que não



O recurso de Class Helpers tem uma sintaxe que pode parecer pouco familiar a programadores Object Pascal. Por não tratar-se de uma classe, não é declarada como uma, mas, por tratar-se de um tipo, também é declarado na sessão type. A maneira mais simples de representa-la é:


  TMinhaClasse = class

    procedure MetodoPrincipal;

  end;



  TMinhaClasseHelper = class helper for TMinhaClasse;


Como se pode notar, sua sintaxe é bem simples, sem nada de obscuro. Logicamente, está representação por si só não “ajuda” em nada a classe TMinhaClasse. A representação prática deste recurso é demonstrada da seguinte forma:


  TMinhaClasseHelper = class helper for TMinhaClasse

    procedure MetodoAuxiliar;

  end;



Assim, mesmo que a classe TMinhaClasse não possua, em sua declaração, o método MetodoAuxiliar, ele pode ser invocado normalmente através de seuClass Helper. Assim como o nosso carro que, após a instalação do ar condicionado, pode refrigerar o ar em seu interior.


Estendendo a extensão? Como assim?

Assim como uma classe, Class Helpers podem ser estendidos. Mas faz sentido estender um Class Helper? Claro! Mas, obviamente, não estenderíamos um Class Helper para ser usado pela mesma classe. A função desta extensão é utilizar o mesmo Class Helper em várias classes diferentes, compartilhando recursos comuns e adicionando recursos apenas a classes que necessitam deles.
Assim, a representação desta extensão seria:


  TMinhaNovaClasse = class

    procedure NovoMetodoPrincipal;

  end;



  TMinhaNovaClasseHelper = class helper(TMinhaClasseHelper) for TMinhaNovaClasse

    procedure NovoMetodoAuxiliar;
  end;

Limitações


Por não ser um recurso exclusivo do compilador Free Pascal, estando disponível também no compilador Delphi, e para manter a compatibilidade entre os dois compiladores, apenas um único Class Helper pode estar disponível para cada classe ao mesmo tempo. Sendo assim, adicionar mais de um Class Helper pode fazer com que o código não compile. Vejamos o seguinte exemplo:


  TMinhaClasse = class

    procedure MetodoPrincipal;

  end;



  TMinhaClasseHelper = class helper for TMinhaClasse

    procedure MetodoAuxiliar;
  end;

  TMinhaClasseHelper2 = class helper for TMinhaClasse
    procedure MetodoAuxiliar2;
  end;

  var
    Obj: TMinhaClasse;
  begin
    Obj := TMinhaClasse.Create;
    Obj.MetodoAuxiliar;  // Não compila
    Obj.MetodoAuliliar2; // Compila
  end;

Como pudemos perceber, apenas o último Class Helper dentro do escopo permanecerá ativo, seguindo as regras da linguagem Pascal. Class Helpers não podem ser referenciados em nenhuma parte do código fonte, na interface ou implementação, exceto nestas três situações:

  • Quando declarando a extensão de um Class Helper;
  • Ao ser usado em uma chamada (Bit)SizeOf;
  • Ao ser usado em uma chamada TypeInfo.
Class Helpers, em sua declaração, não podem:
  • Conter construtores ou destrutores;
  • Conter campos;
  • Conter métodos abstratos;
  • Sobrescrever métodos da classe estendida.
Contudo, os métodos podem ser sobrecarregados, usando-se, normalmente, a sobrecarga de métodos, como sempre.

Ocultando métodos


Ao declarar métodos em Class Helpers, estes ocultarão métodos em classes estendidas se estes não forem sobrecarregados. Sendo assim, se um Class Helper estiver declarado, relacionando-se com uma classe, apenas os métodos da classe serão ocultos, enquanto os métodos do Class Helper permanecerão visíveis. Vejamos como isso funciona: 


  TMinhaClasse = class

    function Resposta: Integer;

  end;



  TMinhaClasseHelper = class helper for TMinhaClasse

    function Resposta: Integer;
  end;

[...]

  function TMinhaClasse.Resposta: Integer;
  begin
    Result := 33;
  end;

  function TMinhaClasseHelper.Resposta: Integer;
  begin
    Result := 42;
  end;

  var
    Obj: TMinhaClasse;
    Resp: Integer;
  begin
    Obj := TMinhaClasse.Create;
    Resp := Obj.Resposta; // Resp receberá 42
  end;

Como pudemos ver, o uso de Class Helpers é um recurso valioso e extremamente simples, podendo ser utilizado nas mais diversas situações, principalmente para facilitar a nossa vida.


Saudações.


Ajude a comunidade Lazarus brasileira. Acesse http://forum.lazbr.net/ e paticipe.

sábado, 29 de junho de 2013

Desenvolvimento multicamadas - luxo ou necessidade?

"É na mudança que encontramos propósito."
Heráclito

Após uma lacuna de 3 anos, cá estou eu. Por questões ligadas a minha vida pessoal e profissional, não pude continuar com o blog, reassumindo, agora, seu andamento. Peço desculpas a todos aqueles que acompanharam o blog, esperando mais artigos ou novidades, mas realmente não foi possível para mim.

Mas, enfim, vamos ao que interessa.

O contexto

Atuando no mercado de desenvolvimento de software há vários anos, nas mais diversas situações com as mais diversas soluções, sempre me deparava com um problema comum a todas: a impossibilidade da centralização do processamento. Como é comum no mercado brasileiro de software, muitas empresas basearam seu desenvolvimento no paradigma de desenvolvimento Cliente/Servidor, onde a própria aplicação utilizada pelo usuário geria suas regras de negócio, fazia o acesso ao banco de dados, obtinha, manipulava e persistia os dados do sistema. Em pequenas aplicações este paradigma não se mostra tão problemático. O problema se apresenta, e de maneira muito intensa, quando aquele projeto pequeno começa a crescer, principalmente, sem planejamento. Apesar de parecer absurdo, isto é muito mais comum do que parece.

O problema

Quando planejamos uma aplicação, devemos ter em mente que, independente do público alvo inicial, uma aplicação deve estar preparada para atender, mesmo que em um único segmento, clientes que podem ir desde um único terminal, com um único usuário, até grupos empresariais, com dezenas ou centenas de filiais. Para atender este segundo cenário, as empresas geralmente, por possuírem aplicações construídas em apenas 2 camadas, utilizam a conexão direta ao banco de dados através de redes de alta latência (aka Internet), ou, o que considero a menos eficiente das soluções, a replicação em lotes da informação entre vários servidores de bancos de dados, os quais também utilizarão redes de alta latência para isso.

Como a maioria das empresas opta por soluções de banco de dados gratuitas, principalmente Firebird, o cenário se complica ainda mais. Alguns servidores de bancos de dados, por possuírem protocolos mal estruturados, e cito mais uma vez o Firebird, seu desempenho em redes que não sejam locais torna-se, realmente, sofrível. Como pude perceber ao longo de todos este anos, muitas empresas pequenas de software, e até mesmo algumas médias e outras grandes, nasceram do casamento de dois fatores comuns a todas elas: muita força de vontade e nenhum conhecimento técnico. E isso tem feito com que ferramentas disponíveis há vários anos no mercado sejam incapazes de atender demandas simples, como uma segunda unidade de uma empresa, em um local afastado e com pouca oferta de conectividade com a internet. Além das duas "soluções" apresentadas anteriormente, as empresas utilizam-se do famigerado Windows Terminal Service - o famoso TS - para que seja possível a integração entre as unidades e possibilitar a centralização do processamento e armazenamento de dados.

As desvantagens do modelo Cliente/Servidor

As desvantagens em utilizar qualquer uma destas soluções são inúmeras: alta latência e desempenho geral da aplicação altamente prejudicado, possibilidade de inconsistência de informações, falhas de replicação, elevados custos em infraestrutura para atender a virtualização de terminais, etc. Isso, ainda, sem citar a falta se segurança dos protocolos de bancos de dados que podem fazer com que dados críticos trafeguem como texto plano pela rede.

Esta última situação pode ser contornada adicionando à comunicação protocolos de criptografia e compressão providos por outros componentes de software. Isso resolve o problema de segurança, mas traz mais um empecilho: este componente adicionado à comunicação implica, obviamente, em uma carga extra de processamento em ambas as pontas desta comunicação. Alguns dirão: "mas com os computadores atuais, esta carga adicional pode ser desprezível". Sim, talvez, mas dependendo da latência da rede, mesmo comprimido e criptografado, ambas as pontas podem enfrentar problemas com o protocolo de comunicação.

Outra grande desvantagem do desenvolvimento Cliente/Servidor está, acreditem, nos próprios programadores. Muitos programadores que utilizam Object Pascal, por possuírem IDEs RAD, estão tremendamente acostumados a arrastar componentes, acessar seus eventos e escrever as regras de negócio dentro deles. Isso faz com que, ao invés de cumprir apenas o seu papel, que é receber e exibir dados, a interface com o usuário também controle as regras de negócio da aplicação. Com isso, e usando uma extrapolação simples, se um ou mais formulários controlarem a mesma regra de negócio e esta estiver replicada em cada um deles, a manutenção em um único ponto não garante que esta manutenção seja replicada em cada um dos formulários, gerando divergências entre as regras.

E, por fim, aplicações Cliente/Servidor possuem um baixo índice de integrabilidade, fazendo com que integrações com outras aplicações sejam custosas e, na sua imensa maioria, constituídas de uma série de gambiarras artifícios técnicos dispensados para solução e/ou atendimento de necessidades levantadas segundo preceitos de engenharia de contorno. Isso faz com que a aplicação integrada tenha uma regra específica para processar as informações que serão entregues à aplicação integradora, o que pode, por falha no desenvolvimento da regra em uma das pontas, gerar divergências e erros na outra.

O modelo multicamadas

Visando resolver este problema, alguns malucos cientistas dos laboratórios da Xerox, durante o desenvolvimento do projeto Smalltalk, criaram um modelo de análise e desenvolvimento que, até hoje, se mostra muito eficiente e que permite que vários componentes de software interajam entre si: o modelo multicamadas. Este modelo estabelece que, para que um projeto seja eficiente e possua um baixo custo de manutenção e alta manutenabilidade, suas regras de negócios devem, sempre, ficar isoladas da interface com o usuário, de modo a não interferir em sua usabilidade, e garantir a unificação do processamento dos dados relativos à aplicação. Estas regras, ainda, deveriam estar igualmente isoladas dos dados a serem inseridos, manipulados ou persistidos, acoplando-se a eles o mínimo possível.

Alguns podem ter notado uma nítida semelhança com a arquitetura MVC (Model View Controler), também criada durante o desenvolvimento do Smalltalk, pois de fato ela existe, mas é preciso entender que estes conceitos não implicam, necessariamente, em uma unanimidade. Planejar e construir um software sob a arquitetura MVC não implica em um desenvolvimento multicamadas (como pode ser visto no desenvolvimento web com PHP ou Java), assim como desenvolvimento multicamadas não está limitado ao modelo imposto pela arquitetura MVC.

O planejamento e desenvolvimento de aplicações multicamadas resolvem, por si só, todos os problemas que o desenvolvimento Cliente/Servidor pode trazer: unifica as regras de negócio em apenas um ponto, estabelece um protocolo de comunicação mais enxuto e leve, ideal para redes de alta latência, dá a possibilidade da utilização de interfaces com o usuário em múltiplas plataformas, reduz os custos de manutenção da aplicação como um todo, aumenta sua manutenabilidade e, por consequência, seu ciclo de vida, etc. Ou seja, planejar e desenvolver aplicações em múltiplas camadas agrega à própria aplicação uma gama infinita de possibilidades.

Quando necessário, aplicações diversas podem, da maneira convier a seus desenvolvedores, integrar-se a aplicações desenvolvidas sob esta arquitetura, sem a necessidade da criação de regras específicas para a esta integração. Aplicações multicamadas geralmente têm, para facilitar este processo, métodos públicos que serão utilizados para estes fins, mantendo as regras de negócio apenas na aplicação integradora, sem que a aplicação integrada tenha que inteirar-se de suas particularidades em seu desenvolvimento.

Enfim, o modelo de desenvolvimento multicamadas se mostra infinitamente superior ao modelo Cliente/Servidor pois possibilita, ao mesmo tempo, um ganho relativamente alto de performance em cenários críticos como a possibilidade da expansão dos horizontes da própria aplicação, que pode atender desde a menor das demandas até aquelas em que o modelo de 2 camadas é incapaz de atender.

Saudações.

Ajude a comunidade Lazarus brasileira. Acesse http://forum.lazbr.net/ e paticipe.

sábado, 6 de fevereiro de 2010

3 Camadas com Lazarus

"Aquele que se empenha a resolver as dificuldades resolve-as antes que elas surjam."
Sun Tzu

Olá a todos.

Neste primeiro artigo, gostaria de fazer uma pequena introdução. Sou analista/programador em linguagens Object/Free Pascal e PHP, especialista em Software Livre, com foco em desenvolvimento de aplicações multiplataforma. Por que uso FreePascal e não Java para este desenvolvimento? Simples: porque Java não gera um executável nativo ao ambiente, tendo por necessidade a criação de uma camada intermediária entre ele e o Sistema Operacional, provida pela Máquina Virtual Java (JVM). O custo disso é uma aplicação mais onerosa em recursos e pouco aplicável ao cenário computacional brasileiro.

O Objetivo do Blog

Ao contrário da maioria dos programadores Delphi (afinal, sou um na minha "vida real"), para os quais a única plataforma de desenvolvimento é a plataforma Microsoft, não sou apaixonado por esta plataforma. Sou amante do Software Livre há 14 anos, dos quais dediquei 12 à especialização em ferramentas livres. Me especializei em ferramentas como o PostgreSQL, o MySQL (que hoje pertence à Oracle) e, por fim, o Lazarus. Com este último, tenho uma relação quase tão longa quanto minha relação com o Delphi. Vi o projeto nascer, acompanhei seu crescimento, e vejo com muita alegria o que ele é hoje. Ainda não é a ferramenta que todos os programadores Delphi usariam, mas não deixa nada a desejar à qualquer outra ferramenta livre de desenvolvimento. A IDE encontra-se em um estágio de maturidade impressionante, chegando a substituir o Delphi em meus novos projetos.

Baseado nisto, meu intuito com este blog não é publicar dicas de como fazer isto ou aquilo. Isto você encontrará abundantemente pela internet, principalmente em blogs. Eu, inclusive, recomendo este e este. O primeiro é o programmer ObjectPascal, do meu grande amigo Silvio Clecio, e o segundo é o Dicas for Lazarus, de Isaac Trindade. Ambos são excelentes fontes de referência. Meu intuito maior é discutir e apresentar soluções implementadas com o Lazarus, soluções estas extremamente portáveis, aplicáveis em qualquer ambiente e, principalmente, com custo reduzido.

Portanto, este nosso 1º artigo abordará um tema muito discutido nos fóruns Delphi ultimamente: aplicações em 3 camadas.

O Desafio

Há alguns dias, conversando com meu amigo Silvio Clecio, queixei-me sobre uma solução que necessitava para um novo projeto. Preciso criar um ambiente separado em camadas, onde minha camada de negócios possa ficar isolada fisicamente da minha camada de aplicação. Para desenvolver tal solução, tinha duas alternativas: criava um WebService em Java para hospedá-lo em um servidor Web, ou desenvolvia um AppServer com Delphi utilizando o DataSnap. Nenhum dos dois cenários me agradava, pois não queria nem (re)aprender Java, muito menos ficar preso a plataformas proprietárias.

A Proposta

No dia seguinte, a caminho do trabalho, a solução veio como uma epifania: SOCKETS! Observando o protocolo de comunicação das ferramentas de Instant Messaging percebi a simplicidade da solução. Um servidor executa uma thread que "escuta" uma porta TCP qualquer. Esta thread será a responsável por estabelecer os canais de comunicação entre o servidor e seu(s) cliente(s). Um cliente abre um canal (socket) de comunicação com o servidor, avisando que está ativo e que enviará uma requisição. O servidor então se prepara para receber esta requisição, enviada como texto puro pelo cliente. Enviada a solicitação, o cliente espera pela resposta do servidor de que recebeu a solicitação. Resposta recebida, o cliente fica a espera do processamento da solicitação.

Enquanto o cliente aguarda, o servidor processa a solicitação. Como já sabe quem é o cliente e onde ele está, retorna o resultado de seu processamento pelo mesmo canal de comunicação com o cliente, que também tem uma thread responsável por ouvir uma porta TCP, respondendo à solicitação. Esta resposta também vem em texto puro, o que torna sua transmissão mais leve que a transmissão de pacotes binários. O cliente então processa o retorno da solicitação, exibindo as informações necessárias ao usuário e fechando o canal de comunicação.

Os Problemas

Transmitir os comandos ao servidor e receber suas respostas em texto puro pode tornar tornar esta solução altamente insegura. Qualquer um capaz de capturar estas transmissões será capaz de ler e interpretar seu conteúdo. Em um ambiente LAN este problema pode, mas não deve, ser ignorado. Mas em ambientes WAN, onde as camadas de negócio e aplicação estão localizadas a alguns quilômetros de distância, isto passa a ser um problema gravíssimo.

Além disso, existe o fato de que o Lazarus ainda não suporta interfaces MDI (funcionalidade testada até a versão 0.9.29), o que pode agregar uma baixa usabilidade à solução.

E, por último, há o problema gerado por solicitações que geram muitos dados de retorno. Um exemplo seria a solicitação dos dados da base de CEPs. Ela conta, hoje, com quase 700.000 registros. Isso colocado em texto puro geraria um arquivo de 76 MB, o que tornaria a consulta, em cenários em que a banda disponível é muito estreita, impraticável.

As Soluções

Como será utilizado o projeto Synapse nesta solução, os comandos de solicitação e retorno podem sem criptografados, utilizando a implementação da OpenSSL disponível no próprio projeto. Com isso, cada cliente terá um par exclusivo de chaves de criptografia, as quais serão utilizadas para esta comunicação. O formato de transmissão das solicitações pode variar entre CSV e XML, sendo mais provável este último por sua compatibilidade com outros protocolos de comunicação.

Para contornar a falta da solução em MDI, uma maneira elegante de se construir a aplicação é usar o conceito TDI (Tabbed Document Interface). Este conceito já é amplamente utilizado, sendo sua aceitação um processo natural. Ele é utilizado, principalmente, em navegadores, como o Mozilla Firefox, o Google Chrome e até mesmo o Internet Explorer. Outros softwares já aderiram a este conceito, como o Mozilla Thunderbird, por exemplo.

Para contornar o problema do excesso de dados em um arquivo de retorno, todas as consultas são parametrizadas, para que retornem o mínimo de informações possível. Exemplo: não podemos pesquisar todos os CEPs do estado de Minas Gerais, pois ele possui mais de 800 municípios. Ao contrário, trazemos apenas os municípios. O usuário então escolhe de qual município consultará o CEP. Caso o município contenha apenas um CEP, ele será mostrado instantaneamente, senão, listará os bairros para que o usuário possa efetuar a consulta. Escolhido o bairro, serão, por fim exibidas as ruas e seus respectivos CEPs. Ou seja, dividimos uma consulta de 50.000 resultados em 3 consultas com, no máximo, 800 resultados.

A Plataforma

Para esta solução, foram escolhidos o Lazarus 0.9.29-23675 e o compilador FPC 2.4.1, ambos em ambiente Slackware Linux 13.0 com Kernel 2.6.32.6 otimizado para o hardware onde se abrigam as ferramentas.

Esta solução prevê a utilização de vários SGBDs, como o MySQL 5.1 e o Firebird 2.1, mas, para um melhor suporte aos requisitos de estabilidade, segurança e confiabilidade do sistema, o SGBD escolhido foi o PostgreSQL 8.4.2-1, também para Linux.

A parte de conexão com o SGBD fica por conta da biblioteca ZeosLib, versão 6.6.6-stable, utilizando um pooling multithread para as consultas e operações com o banco de dados.

Já os sockets de comunicação e a criptografia SSL ficam por conta do projeto Synapse.

Demais aspectos relativos ao desenvolvimento serão demonstrados em artigos futuros.

Considerações Finais

A escolha da plataforma para o desenvolvimento é lógica, até mesmo óbvia. Toda a plataforma é composta por ferramentas livres e por nenhuma delas fui obrigado a dispensar um centavo sequer. Mas, se não há custo para o ambiente de desenvolvimento, qual será o valor da solução depois de pronta? A resposta é simples: R$ 0,00. Esta solução, assim como todas as ferramentas envolvidas em sua confecção, não terá valor comercial algum, sendo acessível a qualquer um, em qualquer lugar.

Mas alguns devem estar pensando em porque escolhi o uso de sockets em detrimento ao WebService Toolkit, disponível para Lazarus já há algum tempo. A resposta também é simples: estabilidade. Como o WST ainda não atingiu um estágio de maturidade apropriado, ao contrário da Synapse, que mostra resultados excelentes, inclusive no Delphi, não achei seguro colocá-lo em produção. Além do fato de que um AppServer funcionando por sockets dispensa a implementação de um servidor Web Apache para hospedar a camada de negócios.

Bem pessoal, este é um breve resumo do projeto que estou iniciando. Colaboradores serão sempre benvindos.

No próximo artigo, explicarei em detalhes a comunicação por sockets e a implementação do OpenSSL na comunicação.

Até mais.

Ajude a comunidade Lazarus brasileira. Acesse http://forum.lazbr.net/ e paticipe.