Boas práticas de programação Delphi
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.
Índice
- 1 Padrões
- 1.1 Clean Code
- 1.2 Blocos
- 1.3 Conversões
- 1.4 Variants
- 1.5 Função Vazio
- 1.6 Criação de objetos
- 1.7 Transações
- 1.8 Tratamento de exceções
- 1.9 Carga de configurações da ConfigSis
- 1.10 Cálculos com divisão
- 1.11 Reanálise
- 1.12 Campo novo no banco
- 1.13 Componentes sem função
- 1.14 Componente Combo
- 1.15 Interação massiva no banco
- 1.16 Try ... except ... raise
- 1.17 Quando postar um dcu
- 1.18 Seeker
- 1.19 Validações ao fechar tela
- 1.20 Expressões booleanas
- 1.21 Setar foco
- 1.22 Popular ListView
- 1.23 Validação de datas em relatórios
- 1.24 Linux
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:
| ||||||||||||||||||||||||||||||||||||
Fonte padrão para o sistema |
| ||||||||||||||||||||||||||||||||||||
Nomeclatura de views | Utiliza o prefixo {:alnum:}{:alnum:}*V . Exemplos:
| ||||||||||||||||||||||||||||||||||||
Nomeclatura de campos | Como prefixo, é adicionado uma caracter para definir seu tipo.
IMPORTANTE: Não criar campos do tipo booleano, pois | ||||||||||||||||||||||||||||||||||||
Tipos de campo | Alguns tipos de campos adotam um padrão em qualquer parte do sistema, são eles:
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:
| ||||||||||||||||||||||||||||||||||||
Código fonte | Em Delphi, segue os seguintes padrões abaixo:
| ||||||||||||||||||||||||||||||||||||
Tabulação | Padronizar em 2 espaços a tabulação. | ||||||||||||||||||||||||||||||||||||
TUEditMask | Mascara para hora: 99:99 | ||||||||||||||||||||||||||||||||||||
Cores |
| ||||||||||||||||||||||||||||||||||||
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
- 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.
- Sempre documentar o campo na unit de atualização e no dicionário de dados.
- Sempre considerar a necessidade de popular com algum valor este campo nos registros já existentes.
- 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