Boas práticas de programação Delphi

De UniWiki
Revisão de 17h12min de 29 de julho de 2016 por Erick (Discussão | contribs) (Linux)
(dif) ← Edição anterior | Revisão atual (dif) | Versão posterior → (dif)
Ir para: navegação, pesquisa

Este tópico está dividido em uma série de Padrões definidos pela Uniware para manter um código padronizado e conciso.

Na segunda parte do documento estão relacionadas as boas práticas de programação que a Uniware adotou como padrão para o desenvolvimento de software.

Padrões

Nomeclatura de tabelas Segue o prefixo {:alnum:}{:alnum:}*_ (2 caracteres e 1 underline)

a fim de identificar qual projeto a tabela pertence. Exemplos:

LB_ Tabelas do Unilab
SA_ Tabelas do SAC
FN_ Tabelas do Financeiro do Unilab
HP_ Tabelas importadas do Hermes Pardini
AL_ Tabelas importadas do Álvaro
CR_ Tabelas importadas do Criesp
SF_ Tabelas importadas do Sérgio Franco
LOG_ Tabelas de logs de alterações em outras Tabelas
U2U_ Interface Unilab
Fonte padrão para o sistema
  • MsSansSerif (8) = Labels e campos não editáveis.
  • Verdana (7) = Campos editáveis
  • Arial = Relatórios.
Nomeclatura de views Utiliza o prefixo {:alnum:}{:alnum:}*V . Exemplos:
Nome Aplicação Principal Tabela da view
LBVPEDI Unilab LB_PEDI
LBVEXAM Unilab LB_EXAM
Nomeclatura de campos Como prefixo, é adicionado uma caracter para definir seu tipo.
C Caracter
N Numérico
D Data e hora
E Enumerado

IMPORTANTE: Não criar campos do tipo booleano, pois
esse tipo possui imcompatibilidade entre SGDB's.
Utilizar um Enumerado de S/N quando for necessário.

Tipos de campo Alguns tipos de campos adotam um padrão em qualquer parte do sistema, são eles:
Monetário DECIMAL(12,2);
Data, hora DATETIME;
Booleano ENUM('S','N');

Para campos que armazenam o caminho de um arquivo, usar o tamanho 255

Tamanho máximo de uma tela MDI 1020x680
Nova Aplicação Certificar-se de que:
  1. Todos os programadores possuem as ferramentas e compiladores necessários para dar manutenção.
  2. Criar uma pasta no subversion para a aplicação e a cada etapa efetivamente concluída, postar os fontes com uma descrição de acordo.
  3. Atualizar o artigo Aplicativos Uniware
  • Quando a aplicação possuir uma versão de release, criar uma pasta no servidor com acesso para o suporte e disponibilizar os binários nela.
  • Atualizar o artigo Aplicativos Uniware
Código fonte Em Delphi, segue os seguintes padrões abaixo:
Tipo Padrão Exemplo
Constantes Caixa alta, espaçamento com "_" VALOR_PAGAMENTO_MINIMO
Variáveis, functions, procedures e nomes de classe Capitalizado UmNomeBemGrandeColocarAPrimeiraLetraDeCadaPalavraEmMaiusculo
Variáveis de Classe Iniciar com "F" FCodiLab
Variáveis do DB utilizar o mesmo nome do DB TUnilabNumeroAmostra.CCODIPOST
Variáveis de function/procedure Iniciar com "v" minúsculo vVersao
Variáveis globais Iniciar com "Global" GlobalPostoColeta
Parâmetros (function/procedure) Iniciar com "a" minúsculo aCodiExpr
function/procedure aninhados Iniciar com "_" _PedidoAlterado
Classes (Tipos) Iniciar com "T" TUnilabNumeroAmostra
Units Iniciar com "U" UUnilabNumeroAmostra
Forms Iniciar com "F" FExamesCad
Tabulação Padronizar em 2 espaços a tabulação.
TUEditMask Mascara para hora: 99:99
Cores
  • texto vermelho = $000000DD
  • Fundo de form = $00e6e6e6
  • Zebrado de relatório ou fundo cinza = clGrayZebra
  • Texto azul = clBlue
  • Seeker = $00AEE8BB/$00C6FFC6
Comentários Como padrão de escrita segue o modelo com a forma de comentários do código:

{Descrição da Unit}
unit MyUnit;
interface

const
  {Descrição da Constante}
  MyConstant = 4;

type
  {Descrição da Classe}
  TMyClass = class
  public
    {Descrição da Variavel}
    MyField: Integer;
    {Descrição do Método}
    procedure MyMethod;
  end;

{Descrição da Procedure}
procedure MyProcedure;

implementation

procedure MyProcedure;
begin

  { comentário do código }
  if MyField = 0 then begin
    
    { documentar aqui o propósito do SQL abaixo, e o que é 
      cada campo documentar usando comentário de linha como segue }  
    SQL := 'CREATE TABLE X (     '+ // DESCRIÇÃO DA TABELA X
           ' CAMPO1 VARCHAR(171) '+ // DESCRIÇÃO DO CAMPO 1
           ' CAMPO2 INTEGER)     '; // DESCRIÇÃO DO CAMPO 2      
  end;
 
end;

end.

Clean Code

  • Nome: O nome deve dizer…: Por que existe; - O que faz; - Como é usado; Use nomes que revelem sua intenção
  • Se considerando os conhecimentos dos nossos padrões um nome de classe ou método requer um comentário, ele pode não estar revelando sua intenção: int d; // days ( não deixe de escrever um comentário mas considere melhorar o nome)
  • Faça distinções significativas: Ex de como não fazer: a2[i] = a1[i];
  • Use nomes pronunciáveis: Ex de como não fazer: private Date genymdhms;
  • Evite palavras que não são palavras
  • Nomes de classes devem ser substantivos e não devem conter verbos
  • Nomes de métodos devem conter verbos
  • Usar o Exit como Exit e não como else!!!!!
  • Evitar o uso de níveis de indentação desnecessários e aumentar níveis de indentação a fim de quebrar condições complexas.

// check if the employee is eligible for benefits
if ((employee.flags == 100) && (employee.age > 65))

if (employee.isEligibleForBenefits())

  • “A primeira regra dos métodos e funções é que eles devem ser pequenos. A segunda regra é que eles devem ser menores ainda.” Uncle Bob
  • “Métodos e funções devem fazer apenas uma coisa, fazê-la certa e somente fazê-la.” Cuidado com efeitos colaterais. Eles são mentiras contadas pelo método, quando diz que fará uma coisa, mas faz outras “escondidas”

public boolean checkPassword (String username, String password) {
  String passwordStatus = crypto.decrypt(password);
  if (passwordStatus.equals(“OK”)) {
    >>>>>> Session.initialize(); <<<<<<<
    return true;
  }
  return false;
}

  • “Se necessário use várias palavras para que o método seja facilmente entendido e possa transmitir o que ele realmente faz.”
  • Métodos com muitos parâmetros devem ser evitados e possuírem uma boa justificativa para existirem (Usar bom senso, discutir com a equipe)
  • Métodos devem fazer algo ou retornar algo, mas não as duas coisas. Isso gera confusão, além de atribuir mais de uma responsabilidade.
  • DRY = Preste atenção no código repetido. Evite duplicidades reaproveitando seus métodos.
  • Comentários:
    • podem ser mentirosos e trazer desinformação, mesmo sem intenção;
    • Eles não recebem “manutenção”, sendo que quanto mais velhos maior a chance de estarem errados.
    • Comentários não vão esconder o código ruim.
    • Geralmente você comenta para que se faça entender. Quando pensar em comentar, é sinal que seu código deve ser refatorado.
    • Se for dar manutenção em códigos grandes e não for refatorar, comente.
    • Se precisar dar manutenção em código legado comentado, procure melhorar o código a fim de não precisa de comentários, ou atualize o comentário.
    • Se o comentário não for para explicar um código mal escrito e for importante, faça.
  • Dê espaços entre operadores, parâmetros e vírgulas.

public double(int a,int b,int c){
   Double sum=number+(one*two);
}
 
public double (int a, int b, int c) {
 Double sum = number + (one * two);
}

  • Forneça o contexto na exception: Crie mensagens informativas para os erros, mencione o que aconteceu, o que estava tentando fazer, e por que o erro ocorreu. E não deixe de mostrar a mensagem de erro técnica da exception.

Blocos

Utilizar o begin na mesma linha do início do bloco. Ex:

if condição then begin
  bloco de comandos;
end;
while condição do begin
  bloco de comandos;
end;

Utilizar begin e end mesmo que o bloco de comandos seja composto de uma única linha.

Não utilizar o bloco de comandos na mesma linha do if, while, etc... (Motivo foge da padronização e dificulta o debug) Ex:

if condição then comando;

Conversões

Sempre fazer conversões seguras.

StrToInt(uma string)

garantir que a string contenha um número válido, ou tratar a exceção.

De preferência utilizar funções que já fazem este tratamento:

  • Fun_Convert.SafeStrToInt
  • Fun_Convert.SafeStrToDateTime
  • Fun_Convert.DateTimeToSTR
  • Fun_Convert.StrFloatAcert
  • Fun_Convert.SafeStrToFloat

Usar os TUQuery.AsString(), TUQuery.AsInteger(), etc.. ao invés de TUQuery.FieldByName().AsString ou TUQuery[]

Variants

Evitar ao máximo o uso de variants. Se utilizar, quando for converter o conteúdo sempre tratar possíveis erros de conversão.

Usar IfNull nas strings. Se for conversão para inteiro, data, float, garantir que o dado é válido ou tratar exceção.

O tipo Variant do Delphi quando recebe um tipo ponto flutuante ele trunca em 5 decimais!!!!!!!!

Função Vazio

NÃO usar a função Vazio para Tipos que não são Variant. Ex: TDateTime não funciona e tem mais uma lista de tipos que não funcionam com a função Vazio. Para inteiro e string funciona.

Criação de objetos

Sempre que criar um objeto garantir a destruição dele com try ... finally.

Transações

Sempre que abrir uma transação garantir que ela será comitada ou dado rollback com try ... except.

Tratamento de exceções

Tratar todas as exceções possíveis com try ... except.

Se a exceção não foi totalmente tratada, ao capturá-la fazer o tratamento parcial e dar raise; (jogar a exceção adiante)

NUNCA usar um try ... except end; As raras exceções a esta regras devem ser feitas conscientemente e deixar claro para a equipe o motivo e em comentário no código.

Carga de configurações da ConfigSis

Sempre carregar parâmetros do sistema na carga da tela e não ficar carregando sob demanda depois. Evitar principalmente esta carga dentro de loops o que além de correr o risco da configuração mudar no meio do caminho (gerando um bug) estará degradando o desempenho.

Cálculos com divisão

Sempre que tiver um cálculo de divisão no código tem que ter um if o divisor for zero não faça a conta.

Reanálise

Sempre que alguma situação não foi prevista na análise, analisar o que precisa em conjunto. Não tentar decidir sozinho.

Campo novo no banco

  1. Sempre que possível criar uma foreign key para garantir a integridade do banco. Se não criar a FK tem que criar o índice.
  2. Sempre documentar o campo na unit de atualização e no dicionário de dados.
  3. Sempre considerar a necessidade de popular com algum valor este campo nos registros já existentes.
  4. Sempre considerar a necessidade de criar um índice para o campo.

Componentes sem função

Uma tela que tem um componente que não é utilizado, excluí-lo da tela. Se não puder, tornar o componente invisível ao invés de deixá-lo desabilitado e aparecendo. Ex: Botões de alterar em telas de seleção, quando é uma tela que não tem alteração.

Componente Combo

Usar o Style csOwnerDrawFixed quando for um combo da biblioteca. O Componente buga se não for assim.

Se não for da biblioteca por padrão usar csDropDownList.

Interação massiva no banco

(ex: Gravar xxxxx registros no banco) Utilizar uma query, instanciada antes do loop de interação com o banco, a SQL também fica fora. Chamar somente os GraveParametro() vai ganhar desempenho ao usuário;

Try ... except ... raise

Em um bloco "try... except on e: Exception" não utilizar raise e!!! Ao invés disso usar só raise; senão a exceção é passada adiante com a mensagem em branco.

Exemplo errado:

try
  ...
except
  on e: Exception do begin
    ...
    raise e;
  end;
end;

Exemplo correto:

try
  ...
except
  on e: Exception do begin
    ...
    raise;
  end;
end;

Outro exemplo correto:

try
  ...
except
  on e: Exception do begin
    ...
    raise Exception.Create('mensagem '+e.message);
  end;
end;

Quando postar um dcu

Quando for de um componente visual do Delphi. O motivo é facilitar a atualização de componentes apra não ter que recompilar o componente no delphi de cada um.

Seeker

Não utilizar o mesmo seeker para dois campos. Sempre duplicar para que cada campo tenha seu próprio seeker.


Validações ao fechar tela

As validações devem estar no OnClose da Janela. O OnClick do botão deve apenas chamar o Close.

Expressões booleanas

Nas expressões booleanas que envolvam uma váriável e uma ou mais funções, iniciar a expressão sempre pela variável quando o operador for AND. 

Exemplo: vOk := vOk AND RFunc1;

vOk := vOk AND RFunc1 AND RFunc2;

Setar foco

Toda vez que for dar um setfocus, testar com canfocus antes, a fim de evitar erro de cannot focus....

Popular ListView

Quando carregar novos valores para uma listView utilizar o seguinte padrão:

 lvwMain.Items.BeginUpdate;
   
 lvwMain.Items.Clear;
 
   código();
 
 lvwMain.Items.EndUpdate;
 
 lvwMain.Refresh;

Se for utilizado desta forma a listview será carregada muito mais rápido e não ficará piscando ao usuário.

Validação de datas em relatórios

Na UDateTimeUtils existe a função ValidaDataRelatorio que é necessário passar dois "TUDateEdit" que é data inicial e data final e dois "TUMaskEdit" referentes a hora inicial e final. Sempre utilizar esta função para validar os campos de data de relatórios.

Caso o campo hora inicial e final seja passado "NIL" o campo valida apenas a data, assim sendo uma função genérica de data e hora

Linux

ListView

Sempre que popular uma ListView deverá ser no padrão do tópico "Popular ListView".

Caso a última coluna for alinhada a direita provavelmente a ScrollBar irá sobrepor o seu conteúdo, como solução colocamos no código (FormShow):

 lvArquivos.Width := lvArquivos.Width + 1;
 
 lvArquivos.Width := lvArquivos.Width - 1;
ComboBox

Combobox deverão ter seu Style do tipo: csDropDownList