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.