20 Regras para POO com Delphi

20 Regras para POO com Delphi

Tem um texto de 1999, escrito por Marco Cantù, chamado 20 Rules for OOP in Delphi, que apresenta o que ele considera 20 regras importantes para ter uma experiência de desenvolvimento ótima com Delphi. Para mim foi uma leitura rápida e que pareceu fazer todo sentido. Estou tomando a liberdade de traduzir e colocar aqui para referência futura e ao mesmo tempo compartilhar com algum incauto que se veja perdido nesse site. Me dou alguma liberdade na tradução para adaptar o texto em uma leitura mais fácil no nosso idioma, o que pode não ser perfeito, é bom lembrar.


A maioria dos programadores Delphi use seu ambiente de desenvolvimento como usariam o Visual Basic [O editor original joga as mãos ao alto horrorizado só de imaginar!], sem perceber ou fazer uso de todo poder que a ferramenta coloca em suas mãos. O Delphi é baseado em um arquitetura orientada a objetos, que não apenas impacta na estrutura da VCL (Visual Component Library – Biblioteca Visual de Componentes) mas também em toda e qualquer aplicação Delphi.

Neste artigo não pretendo tratar da teoria da POO (Programação Orientada a Objetos ou OOP, Oriented Object Programming no inglês), mas apenas sugerir algumas regras simples que podem ajudar a melhorar a estrutura dos seus programas. Estas regras gerais devem ser consideradas como sugestões, para serem aplicadas ou não dependendo do tipo de aplicação que você está desenvolvendo. Minha sugestão é simplesmente tê-las em mente.

O princípio chave que quero destacar é o encapsulamento. Nós queremos criar classes robustas e flexíveis, que futuramente permitam mudanças na implementação sem afetar o resto do programa. Este não é o único critério para uma boa POO, mas representa o alicerce. Então, se eu parecer exagerar em alguns pontos nesse artigo é porque tenho boas razões para isso.

Por fim, para reforçar que esses princípios deveriam ser usados no nosso dia-a-dia como programadores Delphi, vou focar principalmente no desenvolvimento de formulários, mesmo que algumas dessas regras se apliquem igualmente no desenvolvimento de componentes. Quem escreve componentes deve considerar POO e classes como elementos centrais. Para aqueles que esquecem da POO quando usam componentes devem considerar esse artigo como um lembrete.

Parte 1: Um formulário é uma Classe

Programadores comumente tratam formulários como objetos, enquanto de fato tratam-se de classes. A diferença é que você pode ter múltiplos objetos do tipo formulário baseados na mesma classe de formulário. O que causa confusão é o Delphi criar um objeto global padrão para cada classe de formulário que você define. Isso certamente é prático para novatos mas pode gerar um mal hábito.

Regra 1: Uma Classe, Uma Unit

Lembre sempre que o conteúdo das classes contidas nas declarações private e protected estão ocultas somente de procedimentos e classes de outras units (a unidade básica do Delphi, que são salvas como arquivos com a extensão .pas). Portanto, se você quer que o encapsulamento seja efetivo então deverá usar uma unit diferente para cada classe. Para classes simples, herdando umas das outras, podemos usar uma unit compartilhada, mas somente se o número de classes for limitado: não coloque uma hierarquia completa de umas vinte classes num única unit, mesmo que a Borland tenha feito isso no código fonte da VCL…

Pensando em formulários, o Delphi segue o princípio de “uma classe, uma unit” por padrão, o que é bem prático, certamente. Quando adicionar classes que não sejam formulários num projeto, crie novas units.

Regra 2: Nomeie Componentes

É de suma importância que sejam dados nomes claros e significativos para cada unit e formulário. Infelizmente os dois nomes devem ser diferentes, embora eu tenda a usar nomes semelhantes para os dois, como AboutForm e About.pas.

Usar um nome descritivo é importante para componentes também. A notação mais comumente usada consiste em letras minúsculas para o tipo da classe, seguidas pela função do componente, começando em maiúscula, como em btnAdd ou editName. Na verdade, existem muitas variações similares seguindo este estilo e não existe razão para dizer que uma ou outra é a melhor, isso fica por conta do seu gosto pessoal.

Regra 3: Nomeie Eventos

É até mais importante dar nomes coerentes para os métodos de manipulação de eventos. Se você nomeia um componente apropriadamente, o nome padrão de Button1Cllick, por exemplo, se torna btnAddClick. Embora possamos imaginar o que o método faz pelo nome do botão, penso que ser bem melhor um nome que descreva o que o método faz e não o componente a que está vinculado. Por exemplo, o evento OnClick do btnAdd bode ser nomeado como AddToList. Isso torna o código mais fácil de ler, particularmente quando você chama um manipulador de evento de outro método da classe; isso ajuda o desenvolvedor a vincular o mesmo método para múltiplos eventos ou para diferentes componentes, embora eu deva dizer que usar Actions é atualmente minha escolha preferida para programas não triviais.

Regra 4: Use os Métodos do Formulário

Se os formulários são classes, seu código é distribuído em métodos. Fora os manipuladores de eventos, que desempenham um papel especial, mas ainda podem ser chamados como outros métodos; muitas vezes, é útil adicionar métodos personalizados às classes do formulário. Você pode adicionar métodos executando ações e acessando o estado do formulário. É bem melhor adicionar um método público a um formulário do que deixar outras formulários manipularem seus componentes diretamente.

Regra 5: Adicione Construtores ao Formulário

Um formulário secundário, criado em tempo de execução, pode fornecer outros construtores específicos além do padrão (herdado da classe TComponent). Se você não precisar de compatibilidade com versões anteriores ao Delphi 4, sugiro que sobrescreva o método Create, adicionando os parâmetros de inicialização necessários. A listagem a seguir dá um exemplo.

<pre class="wp-block-code"><code>public
  constructor Create (Text: string); reintroduce; overload;
constructor TFormDialog.Create(Text: string);
begin
  inherited Create (Application);
  Edit1.Text := Text;
end;</code></pre>

Regra 6: Evite Variáveis Globais

Variáveis Globais (aquelas variáveis declaradas na parte interface da unit) devem ser evitadas. Seguem algumas poucas sugestões para te ajudar com isso.

Se precisar de armazenamento extra de dados em um formulário, adicione alguns campos privados nele. Dessa forma, cada instância do formulário terá sua própria cópia dos dados.

É possível usar variáveis da unidade (declaradas na parte implementation da unit) para dados compartilhados entre múltiplas instâncias da classe do formulário.

Se precisar compartilhar dados entre diferentes tipos de formulários, é possível fazer isso colocando esses dados no formulário principal ou em um objeto global e usar métodos e propriedades para acessar os dados.

Regra 7: Nunca use Form1 em TForm1

Nunca se refira a um objeto específico em um método de classe desse objeto. Em outras palavras, nunca se refira ao Form1 num método da classe TForm1. Quando precisar se referir ao objeto corrente use o identificador self. Tenha em mente que na maioria das vezes isso não é necessário já que pode se referir diretamente aos métodos e dados do objeto corrente.

Se você não seguir essa regra, irá ter problemas quando criar múltiplas instâncias do formulário.

Regra 8: Raramente use Form1 em outros Formulários

Mesmo no código de outros formulários, tente evitar referenciar diretamente objetos globais, como Form1. É bem melhor declarar variáveis locais ou campos privados para se referir a outros formulários.

Por exemplo, o formulário principal do sistema pode ter um campo private referenciando um caixa de diálogo. Obviamente essa regra se torna essencial se o plano for criar múltiplas instâncias do formulário secundário. Você pode manter uma lista em um campo do formulário principal, ou simplesmente usar o array Forms do objeto global Screen.

Regra 9: Remova Form1

Na verdade, minha sugestão é remover o objeto formulário global que é automaticamente adicionado pelo Delphi ao programa. Isso só é possível se você desativar a criação automática desse formulário (reforçando, criado pelo Delphi), algo que sugiro você faça de qualquer forma.

Acredito que remover o objeto formulário global é muito útil para novatos no Delphi, que dessa forma não mais ficarão confusos entre a classe e o objeto global. Após o objeto global ter sido removido, de fato, qualquer referência a ele resultará num erro.

Regra 10: Adicione Propriedades ao Formulário

Como já mencionei, quando precisa incluir dados para um formulário, inclua um campo private. Se precisar acessar esses dados a partir de outras classes, então adicione propriedades ao formulário. Com esta abordagem você será capaz de mudar o código do formulário e seus dados (incluindo a interface com o usuário) sem ter que mudar código de outros formulários ou classes.

Você deve também usar métodos e propriedades para inicializar formulários secundários ou caixas de diálogo e para ler seu estado. A inicialização pode também ser feita usando um construtor, como descrito anteriormente.

Rule 11: Exponha as Propriedades dos Componentes

Quando precisar acessar o estado de outro formulário, não se refira diretamente aos seus componentes. Isso iria vincular o código de outros formulários ou classes com a interface do usuário, que é a parte de uma aplicação que está mais sujeita a alterações. Em vez disso, declare uma propriedade do formulário mapeada para a propriedade do componente: isso é conseguido pelo uso do método Get que lê o estado do componente e do método Set que grava isso.

Suponha que precise mudar a interface de usuário, substituindo um componente com outro qualquer. Tudo que precisará fazer será corrigir os métodos Get e Set relacionados com a propriedade, você não terá que conferir e modificar o código fonte de todos os formulários e classes que referenciam esse componente. Você pode ver um exemplo na listagem a seguir.

<pre class="wp-block-code"><code>private
  function GetText: String;
  procedure SetText(const Value: String);
public
  property Text: String
    read GetText write SetText;
function TFormDialog.GetText: String;
begin
  Result := Edit1.Text;
end;
procedure TFormDialog.SetText(const Value: String);
begin
  Edit1.Text := Value;
end;</code></pre>

Regra 12: Matriz de Propriedades

Se precisar manipular uma série de valores em um formulário poderá declarar uma matriz de propriedades. No caso de se tratar de uma informação importante para o formulário você poderá fazer com que seja também a matriz padrão de propriedades do formulário, assim poderá acessar seus valores escrevendo diretamente SpecialForm[3].

A listagem a seguir mostra como expor os itens de uma listbox como sendo a matriz padrão de propriedades do formulário que a contém.

type
  TFormDialog = class(TForm)
  private
    ListItems: TListBox;
    function GetItems(Index: Integer): string;
    procedure SetItems(Index: Integer; const Value: string);
  public
    property Items[Index: Integer]: string read GetItems write SetItems; default;
  end;
function TFormDialog.GetItems(Index: Integer): string;
begin
  if Index >= ListItems.Items.Count then
    raise Exception.Create('TFormDialog: Out of Range');
  Result := ListItems.Items [Index];
end;
procedure TFormDialog.SetItems(Index: Integer; const Value: string);
begin
  if Index >= ListItems.Items.Count then
    raise Exception.Create('TFormDialog: Out of Range');
  ListItems.Items [Index] := Value;
end;

Regra 13: Use os “Efeitos Colaterais” nas Propriedades

Lembre-se que uma das vantagens de usar propriedades ao invés de acessar dados em variáveis globais é que você pode se aproveitar dos “efeitos colaterais” quando escrevendo (ou lendo) o valor de uma propriedade.

Por exemplo, você pode desenhar diretamente na superfície do formulário, definir o valor de múltiplas propriedades, chamar métodos especiais, mudar o estado de múltiplos componentes de uma só vez, ou disparar um evento, se ele estiver disponível.

Regra 14: Oculte Componentes

Frequentemente ouço puristas da OOP reclamando por conta do Delphi incluir a lista de componentes dos formulários na seção published, uma abordagem que não está de acordo com o principio do encapsulamento. Eles na verdade estão destacando um ponto importante, mas a maioria deles parece não saber que a solução é simples e não é preciso reescrever o Delphi ou mudar de linguagem.

As referencias aos componentes que o Delphi adiciona num formulário podem ser movidas para a seção private, assim elas não estarão acessíveis por outros formulários. Dessa forma podemos tornar compulsório o uso de propriedades mapeadas para os componentes (veja a Regra 11) para acessar seu estado.

Se o Delphi coloca todos os componentes na seção published é por causa da maneira que esses campos são vinculados aos componentes criados a partir do arquivo .DFM. Quando você define o nome de um componente, A VCL automaticamente anexa o objeto derivado desse componente com a sua referencia no formulário. Isso só é possível se a referência estiver publicada (contida na seção published) por conta do Delphi usar os métodos da RTT e TObject para fazer isso.

Se quiser entender os detalhes, confira a próxima listagem, que tem o código do método SetReference da classe TComponente, que é chamado por InsertComponent, RemoveComponent e SetName.

<pre class="wp-block-code"><code>procedure TComponent.SetReference(Enable: Boolean);
var
  Field: ^TComponent;
begin
  if FOwner <> nil then begin
    Field := FOwner.FieldAddress(FName);
    if Field <> nil then
      if Enable then
        Field^ := Self
     else
        Field^ := nil;
  end;
end;</code></pre>

Uma vez sabendo disso, você percebe que movendo as referencias dos componentes da seção published para a seção private você perde esse comportamento automático. Para resolver o problema, simplesmente faça manualmente colocando o seguinte código para cada componente no manipulador do evento OnCreate do formulário:

<pre class="wp-block-code"><code>Edit1 := FindComponent('Edit1') as TEdit;</code></pre>

O segundo passo que você deverá seguir é registrar as classes do componente no sistema, de forma que suas informações da RTTI sejam incluídas no programa compilado e disponibilizados para o sistema. Só é preciso fazer isso apenas uma vez para cada classe de componente, e somente se você mover todas as referências de componentes desse tipo para a seção private. Você pode incluir essa chamada de evento mesmo se ela não for necessária, já que uma chamada extra ao método RegisterClasses é inofensiva. O método RegisterClasses é adicionado usualmente na seção initialization da unidade contendo o formulário:

<pre class="wp-block-code"><code>RegisterClasses(&#91;TEdit]);</code></pre>

Regra 15: O Assistente de Formulário OOP

Repetir essas duas operações descritas cima para cada componente em cada formulário com certeza é chato e demorado. Para evitar essa sobrecarga excessiva, escrevi um assistente simples que gera as linhas de código que são necessárias adicionar ao programa em uma pequena janela. Você precisara simplesmente de duas operações de copiar e colar para cada formulário.

O assistente não coloca o código no lugar certo automaticamente: ainda estou trabalhando para resolver isso e você pode conferir meu website (www.marcocantu.com) por uma versão atualizada.

Parte 2: Herança

Depois desse primeiro conjunto de regras dedicadas às classes, e particularmente classes de formulários, segue agora uma lista curta de sugestões e dicas relacionadas com herança e herança visual de formulários.

Regra 16: Herança Visual de Formulários

Este é um mecanismo poderoso, se usado corretamente. Pela minha experiência, seu valor aumenta de acordo com o tamanho do projeto. Em um programa complexo você pode usar a relação hierárquica entre os formulários para operar em grupos de formulários usando polimorfismo.

Herança visual de formulários permite que você compartilhe o compartilhamento comum de múltiplos formulários: pode ter métodos em comum, propriedades, manipuladores de eventos, componentes, propriedades de componentes, manipuladores de eventos de componentes, e por aí vai.

Regra 17: Limite Dados Protegidos

Quando construindo uma hierarquia de classes, alguns programadores tendem a usar principalmente campos protected, já que campos private não são acessíveis pelas subclasses. Não vou dizer que isso é sempre errado, mas certamente contra o encapsulamento. A implementação de dados protected é compartilhada entre todos os formulários herdados, e você pode precisar atualizar todos eles no caso da definição original dos dados mudar.

Repare que se você seguir a regra de ocultar componentes (Regra 14) os formulários herdados não poderão acessar os componentes privados da classe base. Num formulário herdado, um código como Edit1.Text := ”; não será mais compilado. Percebo que isso não será muito útil, mas ao menos em teoria deveria ser considerado positivo, não negativo. Se achar que isso é uma concessão excessiva ao encapsulamento, declare as referencias ao componente na seção protected do formulário base.

Regra 18: Métodos de Acesso Protegido

É bem melhor, por outro lado, manter as referencias aos componentes na seção private e adicionar funções de acesso para suas propriedades na classe base. Se essas funções de acesso são usadas somente internamente e não são parte da interface da classe, você deve declará-las como protected. Por exemplo, os métodos GetText e SetText do formulário, descritos na Regra 11, podem virar protected e o texto do edit ser acessador usando:

SetText('');

Na verdade, como o método foi mapeado para uma propriedade, podemos simplesmente escrever:

Text := '';

Regra 19: Métodos Virtuais Protegidos

Outro ponto chave para ter uma hierarquia flexível é declarar métodos virtuais que podem ser chamados de classes externas para obter polimorfismo. Se essa é uma abordagem comum, é bem menos frequente ver métodos virtuais protegidos sendo chamados por outros métodos públicos. Esta é uma técnica importante, já que você pode personalizar o método virtual nas classes derivadas, modificando o comportamento dos objetos.

Regra 20: Métodos Virtuais para Propriedades

Mesmo métodos de acesso as propriedades podem ser declarados como virtuais, de forma que uma classe derivada possa mudar o comportamento de uma propriedade sem precisar redefini-la. Essa abordagem é raramente usada pela VCL mas é muito flexível e poderosa. Para isso, simplesmente declare como virtuais os métodos Get e Set citados na Regra 11. O formato básico se parecerá com o código a seguir:

type
  TFormDialog = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    Edit1: TEdit;
  protected
    function GetText: String; virtual;
    procedure SetText(const Value: String); virtual;
  public
    constructor Create (Text: string); reintroduce; overload;
    property Text: String read GetText write SetText;
  end;

No formulário herdado você pode agora sobrescrever o método virtual SetText para adicionar algum comportamento extra:

procedure TFormInherit.SetText(
  const Value: String);
begin
  inherited SetText (Value);
  if Value = ‘’ then
    Button1.Enabled := False;
end;

O Código

Todos os fragmentos de código desse artigo podem ser encontrados no projeto de exemplo OopDemo, incluído no disco desse mês (nota do tradutor: esse artigo tem origem na Revista Delphi de Julho e 1999, dê uma procurada na internet). Você deve conferir, em particular, o formulário secundário (na unit frm2) e também a derivada (na unit inher). Perceba que para usar, ao mesmo tempo, um código de inicialização num construtor personalizado e referências a componentes privados, se faz necessário definir a propriedade OldCreateOrder do formulário. Do contrário, o código de inicialização do construtor do formulário (que usa os componentes) será executado antes do método OnCreate do formulário, que faz a conexão entre as referências do componente real.

Neste disco você também irá encontrar pacotes compilados de uma primeira versão rascunho do Assistente de Formulário POO, que você deve (espero) ser capaz de encontrar uma versão mais completa no meu website.

Conclusão

Programas em Delphi de acordo com os princípios da boa POO está longe de ser óbvio, como algumas das regras que destaquei demonstram. Não acho que deva considerar todas as minhas regras compulsórias, já que algumas delas podem testar sua paciência. As regras devem ser aplicadas de acordo com o contexto apropriado, e se tornam mais e mais importante conforme o tamanho da aplicação cresce, junto com o número de programadores que estão trabalhando nela. Mesmo para pequenos programas, todavia, tenha em mente os princípios POO por trás das minhas regras (encapsulamento antes de tudo) pode realmente ajudar.

Certamente existem outras regras de ouro que você pode se deparar, já que nem entrei no assunto da manipulação de memória ou problemas com RTTI, que são tão complexos que merecem artigos específicos.

Minha conclusão é que essas regras que destaquei tem um custo, em termos de código extra: que é o preço que você paga para ter um programa mais flexível e robusto. Este é o preço da programação orientada a objetos. Vamos torcer para que as versões futuras do Delphi nos ajudem a diminuir esse preço.


Marco Cantu é o autor da série Dominando o Delphi (Mastering Delphi), Manual para Desenvolvedores Delphi (Delphi Developer’s Handbook) e do livro online gratuito Pascal Essencial (Essential Pascal). Ele dá aulas sobre fundamentos e tópicos avançados do Delphi. Confira seu website em www.marcocantu.com para mais informações. Você pode encontrá-lo em seu grupo de notícias público: veja o site para detalhes.

About author

Você pode gostar também

Diversos 0 Comments

Outlook: Aviso de Ausência

Vira e mexe precisamos nos ausentar da empresa onde trabalhamos, por motivo de férias, reuniões externas ou treinamentos, por exemplo. Quando isso acontece é importante deixar um aviso de ausência

Dicas e Tutoriais 0 Comments

O Básico do Básico de HTML

Dá uma olhadinha nessa sopa de letrinhas: Esse código aí em cima é um código escrito usando a Hypertext Markup Language ou HTML para os íntimos. Essa sopa de letrinhas,