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.