# Padrões de código # Índice ### **Resumo** O livro "Padrões de Código", contém todas as informações referentes à padronização e boas práticas utilizadas na implementação e desenvolvimento dos sistemas SS. Organizado por capítulos e sempre atualizado, este livro é de suma importância para a equipe de desenvolvimento, pois apresenta as diretrizes à serem seguidas durante a manutenção do código. Logo abaixo, serão apresentados os capítulos e uma breve descrição do que é tratado em suas respectivas páginas. ### **Sumário** #### [**Delphi**](https://wiki.supersoft.com.br/books/padroes-de-codigo/chapter/delphi) [**Nomenclatura Geral**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/nomenclatura-geral) Padrão de nomenclaturas para componentes, variáveis, constantes, métodos, units e classes com exemplos ilustrativos e explicações. **[Espaçamento](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/espacamento)** Padronização de espaçamento para declaração de variáveis, atribuições, declaração de métodos, matrizes, operadores binários, operadores unários, subrotinas e uses. [**Palavras reservadas**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/palavras-reservadas) #### [**SQL**](https://wiki.supersoft.com.br/books/padroes-de-codigo/chapter/sql) [**Considerações Iniciais**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/consideracoes-iniciais) Definição do padrão para indentação de cláusulas SQL, palavras reservadas , tabelas, campos e parâmetros. [**Exemplo: UPDATE**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/exemplo-update) Exemplo de cláusula de UPDATE. [**Exemplo: DELETE**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/exemplo-delete) Exemplo de cláusula de DELETE. [**Exemplo: CASE**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/exemplo-case) Exemplos de cláusulas com CASE, tanto com expressão quanto sem expressão no CASE. [**Palavras reservadas**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/palavras-reservadas-e2c) #### [**Boas Práticas**](https://wiki.supersoft.com.br/books/padroes-de-codigo/chapter/boas-praticas) **[Versionamento](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/versionamento)** Definição das diretrizes para mensagens de versionamento. Mensagem modelo e exemplo de mensagem em criação de branch, commit de alterações e atualização de branch. [**Documentação Interna**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/documentacao-interna) Exemplos de comentários e como devem ser utilizados. [**Código Limpo**](https://wiki.supersoft.com.br/books/padroes-de-codigo/page/codigo-limpo) Definição de código limpo e *bad smells.* Exemplos de *bad smells* comuns e possíveis correções. # Delphi Capítulo destinado aos padrões como nomenclatura, indentação entre outros, usados em Delphi. # Nomenclatura Geral ### **Nomenclatura** ##### **Componentes** Os componentes possuem seus prefixos utilizando letras que remetem ao nome completo da classe do componente. Componentes herdados dos componentes listados e sem conexão ao banco de dados devem possuir o mesmo prefixo do componente principal, com exceção dos componentes visuais com conexão ao banco de dados, que utilizam o prefixo “DB“ seguido do padrão identificado logo abaixo. Exemplo: DBED\_Nome é um componente da classe TDBEdit, responsável pela representação de um nome. Seguem alguns exemplos de prefixos padrões utilizados:
ED\_: TEdit; | TS\_: TTabSheet; |
PN\_: TPanel; | RB\_: TRadioButton; |
GB\_: TGroupBox; | OD\_: TOpenDialog; |
LB\_: TLabel; | SD\_: TSaveDialog; |
MM\_: TMemo; | Q\_: TQuery, TFDQuery; |
BT\_: TButton (Botões num geral); | DS\_: TDataSource; |
CK\_: TCheckBox; | CDS\_: TClientDataSet; |
CB\_: TComboBox; | DSP\_: TDataSetProvider; |
LT\_: TListBox; | T\_: TTable; |
RG\_: TRadioGroup; | FR\_: TFrxReport; |
PC\_: TPageControl; | FDS\_: TFrxDBDataSet; |
TB\_: TToolBar; | RV\_: TRvProject; |
GD\_: TGrid; | DSC\_: TRvDataSetConnection; |
MI\_: TMenuItem; | SH\_: TShape |
IM\_: TImage | TM\_: TTimer |
DBED\_: TDBEdit; | DBGD\_: TDBGrid; |
DBCK\_: TDBCheckBox; | DBRG\_: TDRadioGroup; |
**Correto**
```PASCAL var LTeste: Integer; ```**Incorreto**
```PASCAL var LTeste : Integer; LTeste :Integer; ``` ##### **Atribuições****Correto**
```PASCAL LTeste := 15; ```**Incorreto**
```PASCAL LTeste:=15; LTeste:= 15; LTeste :=15; ``` ##### **Métodos****Correto**
```PASCAL procedure Ex(AParametro: Integer); procedure Ex(AParametro1, AParametro2: Integer); procedure Ex(AParametro: Integer; AParametro2: String); ```**Incorreto**
```PASCAL procedure Ex (AParametro1: Integer); procedure Ex( AParametro: Integer); procedure Ex(AParametro: Integer ); procedure Ex(AParametro1,AParametro2: Integer); procedure Ex(AParametro1 , AParametro2: Integer); procedure Ex(AParametro: Integer;AParametro2: String); procedure Ex(AParametro: Integer ; AParametro2: String); ``` ##### **Matrizes****Correto**
```PASCAL LTeste := LMatriz[0]; ```**Incorreto**
```PASCAL LTeste := LMatriz[ 0]; LTeste := LMatriz[0 ]; LTeste := LMatriz[ 0 ]; ``` ##### **Operadores binários****Correto**
```PASCAL LTeste := 1 + 1; ```**Incorreto**
```PASCAL LTeste := 1+1; LTeste := 1 +1; LTeste := 1+ 1; ``` ##### **Operadores unários****Correto**
```PASCAL LTeste := -1; ```**Incorreto**
```PASCAL LTeste := - 1; LTeste :=-1; ``` ##### **Subrotinas****Correto**
```PASCAL function MeuMetodo: String; procedure SubMetodo; begin //Código SubMetodo end; begin //Código MeuMetodo end; ```Incorreto
```PASCAL function MeuMetodo: String; procedure SubMetodo; begin //Código SubMetodo end; begin //Código MeuMetodo end; ``` ```PASCAL function MeuMetodo: String; procedure SubMetodo; begin //Código SubMetodo end; begin //Código MeuMetodo end; ``` ##### **Uses****Dica:** Para identificar se uma unit deve ser declarada na uses superior ou inferior, realize o seguinte teste: Copie a unit para a uses inferior e compile o programa, caso o processo de compilação não apresente erros, a unit deve permanecer na uses inferior. Caso contrário, mova a unit para a uses superior.
**No primeiro “uses“ (logo abaixo de “interface”) declarar as units no .dfm e nas assinaturas dos métodos.** ```Pascal interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs; ``` No segundo “uses“ (logo abaixo de “implementation”) declarar as units usadas na implementação do código. ```Pascal implementation uses uExisteTabela, uProcuraRegistro, uGravaSistema; ``` A fim de evitar conflito em Merge devido a alterações em mesma linha, cada Unit deve ser declarada em linha diferente. Esse erro costuma acontecer principalmente quando há refatoração ou exclusão de algum componente visual.Correto
```Pascal implementation uses uExisteTabela, uProcuraRegistro, uGravaSistema; ```Incorreto
```Pascal implementation uses uExisteTabela, uProcuraRegistro, uGravaSistema; ``` ##### **Arrays** Ao indentar elementos de um array, eles devem seguir a lógica de ordenação de parênteses. Por não estarem em níveis diferentes de indentação, devem ser indentados da seguinte formaCorreto
```Pascal TClasse.Metodo( Parametro1, Parametro2, [Item1, Item2, Item3 Item4, Item5, Item6]); ```Incorreto
```Pascal TClasse.Metodo( Parametro1, Parametro2, [Item1, Item2, Item3 Item4, Item5, Item6]); ``` ``` TClasse.Metodo( Parametro1, Parametro2, [Item1, Item2, Item3 Item4, Item5, Item6]); ``` ##### **IN em tratamento de condições**Correto
```PASCAL Result := (Self in [cdfIncidenciadecisaojudicial, cdfIncidenciaDecisaoJudicial13Sal, cdfIncidenciaDecisaoJudicialAvisoPrevioIndenizado]); ```Incorreto
```PASCAL Result := Self in [cdfIncidenciadecisaojudicial, cdfIncidenciaDecisaoJudicial13Sal, cdfIncidenciaDecisaoJudicialAvisoPrevioIndenizado]; ``` ```PASCAL Result := (Self in [cdfIncidenciadecisaojudicial, cdfIncidenciaDecisaoJudicial13Sal, cdfIncidenciaDecisaoJudicialAvisoPrevioIndenizado]); ``` # Palavras reservadas As palavras reservadas da linguagem Delphi devem ser escritas com todas as letras minúsculas, a única exceção é a palavra String, pois segue o padrão dos tipos, os quais iniciam com letra maiúscula. As palavras reservadas do Delphi são:absolute | forward | public |
abstract | function | published |
and | goto | raise |
array | if | read |
as | implementation | readonly |
asm | in | record |
assembler | index | register |
automated | inherited | reintroduce |
begin | initialization | repeat |
case | inline | resourcestring |
cdecl | interface | safecall |
class | is | set |
const | label | shl |
constructor | library | shr |
contains | message | stdcall |
default | mod | stored |
destructor | name | string |
dispid | near | then |
dispinterface | nil | threadvar |
div | not | to |
do | object | try |
downto | of | type |
dynamic | operator | unit |
else | or | until |
end | out | uses |
except | overload | var |
export | override | virtual |
exports | package | while |
external | pascal | write |
far | private | writeonly |
file | procedure | xor |
finally | program | |
for | property |
As comparações realizadas nas linhas 3-5 poderiam ter seus operadores alinhados na mesma coluna, entretanto essa não é uma prática recomendada para cláusulas mais extensas.
# Exemplo: Modificador DISTINCT ```SQL SELECT DISTINCT T.Campo1, T.Campo2, T.Campo3 T.Campo4, T.Campo5, T.Campo6 FROM Tabela T INNER JOIN OutraTabela O ON O.Campo1 = T.Campo7 WHERE T.Campo1 = :Param1 AND T.Campo8 = :Param2 ``` # Exemplo: UPDATE ```SQL UPDATE TabelaExemplo SET Numero = :PNumero, Nome = :PNome WHERE Categoria = :PCategoria AND Tipo = :PTipo ```As comparações realizadas nas linhas 2-5 poderiam ter seus operadores alinhados na mesma coluna, entretanto essa não é uma prática recomendada para cláusulas mais extensas
# Palavras Reservadas As palavras reservadas em SQL devem ser escritas com todas as letras maiúsculas:ACTIVE | DOUBLE | NUMERIC |
ADD | DROP | OF |
AND | EDIT | ON |
ALL | ELSE | ONLY |
ALTER | END | OPEN |
ANY | EXECUTE | OR |
AS | EXISTS | ORDER |
ASC | FILTER | OUTER |
ASCENDING | FIRST | PERCENT |
AT | FLOAT | PLAN |
AVG | FOR | POSITION |
BEFORE | FOREIGN | PRECISION |
BEGIN | FROM | PREPARE |
BETWEEN | FULL | PRIMARY |
BLOB | FUNCTION | PROCEDURE |
BY | GENERATOR | PUBLIC |
CASE | GEN\_ID | REAL |
CAST | GROUP | RIGHT |
CHAR | HAVING | ROWS |
COALESCE | HOUR | SELECT |
COLLATE | IF | SET |
COLUMN | IN | SIZE |
COMMIT | INACTIVE | SOME |
CONTINUE | INDEX | SQL |
COUNT | INNER | SUM |
CREATE | INSERT | TABLE |
CURRENT | INTEGER | THEN |
CURRENT\_DATE | INTO | TRIGGER |
CURRENT\_ROLE | IS | TRUNCATE |
CURRENT\_TIME | JOIN | TYPE |
CURRENT\_TIMESTAMP | KEY | UNION |
CURRENT\_TRANSACTION | LAST | UPDATE |
CURRENT\_USER | LEFT | UPDATING |
CURSOR | LENGTH | USE |
DATABASE | LIKE | USER |
DATE | LONG | USING |
DAY | MAX | VALUE |
DEC | MERGE | VALUES |
DECIMAL | MIN | VIEW |
DECLARE | MINUTE | WHEN |
DEFAULT | MONTH | WHERE |
DELETE | NAMES | WHILE |
DESC | NO | WITH |
DESCENDING | NOT | YEAR |
DISTINCT | NULL | YEARDAY |
DO | NULLIF |
Deve-se evitar ao máximo realizar commits sem mensagem, principalmente em versões e no trunk.
#### **Exemplos** --- ##### **Modelo de Mensagem**Caso XXXXX: Descrição do caso de acordo com o Mantis/Trello. \- Mensagem descritiva e em poucas palavras do que foi alterado (Opcional) Desenvolvedor |
Ao criar um branch, deve-se informar o número do caso e a sua descrição de acordo com o Mantis/Trello.
Caso XXXXX: Descrição do caso de acordo com o Mantis/Trello. \- Criação do branch (Opcional) Desenvolvedor |
Ao realizar o commit de alterações, além do cabeçalho indicando o número do caso e a descrição, deve-se adicionar por tópico as alterações feitas naquele commit.
Caso XXXXX: Descrição do caso de acordo com o Mantis/Trello. \- Alterações na lógica da rotina CalcularSaldo. \- Correções no padrão de código aplicado. \- Criação da classe TGerador (Opcional) Desenvolvedor |
Ao realizar a atualização de um branch(Merge com Trunk) , deve-se adicionar a "tag" \[Atualização do Branch\] no início do cabeçalho, para que ao consultar o log de alterações, o desenvolvedor identifique prontamente quais revisões são referentes à atualizações do branch e quais não são.
\[Atualização do Branch\] Caso XXXXX: Descrição do caso de acordo com o Mantis/Trello. (Opcional) Desenvolvedor |
Comentários que complementam o entendimento de uma rotina, mas que não tentam explicar o que está sendo feito no trecho.
--- #### **Comentários que devem ser evitados/removidos:** ##### **Comentários no cabeçalho:** ```PASCAL {**************************************************************** * Módulo : ArqTeste * * Finalidade : Realizar a exportação de Notas Fiscais * * de Serviços para Prefeitura de São Paulo * * Data : 21/02/2000 * * Programador : Fulano da Silva Santos * ****************************************************************} ```Comentários utilizados como cabeçalho que descrevem o que a unit faz, a data de implementação e o programador responsável pela implementação apesar de serem descritivos, muitas vezes não são atualizados conforme o código passa por mudanças. Essas informações podem e devem ser encontradas nas mensagens de versionamento.
--- ##### **Comentários referentes à processos passados:** ```PASCAL // MIGRACAO // Caso a classe uClass.NumDocs seja utilizada em alguma unit // a diretiva referente ao arquivo uClass.NumDocs.inc deverá ser acrescentada. // Ex: Ver units FormPri e Backup, antes do nome da unit; // No arquivo uClass.NumDocs.inc deverá ser adicionada a diretiva referente ao // sistema ao qual a classe NumDocs foi adicionada. ```Comentários como esses não acrescentam informações no entendimento do código e acabam criando uma poluição visual na unit, de modo que, com o passar do tempo a tendência é que esse tipo de comentário seja automaticamente ignorado pelos desenvolvedores.
--- ##### **Código comentado:** ```PASCAL // if ((QMANUT.FieldByName('DiasDireito').AsFloat = 0) or // (SameText(Trim(QMANUT.FieldByName('DiasDireito').AsString), ''))) and // (MessageDialog.Show('Não foi informado os dias de Direito. Deseja Continuar?', // mtConfirmation, [mbYes, mbNo], 0) = mrNo) then // begin // QMANUT.FieldByName('DiasDireito').FocusControl; // Abort; // end; ```Códigos comentados geram confusões no código, além de poluir desnecessariamente a unit, dessa maneira códigos não devem ser comentados e sim removidos. Caso necessário, pode-se visitar o histórico de revisões para acompanhar as alterações do trecho.
# Código Limpo #### **Definição de Clean Code** A apresentação de um código claro e organizado não consiste apenas na convenção nomes, constantes, classes, variáveis, espaçamento etc.. Um código limpo (*clean code*) deve ser: - Simples: fácil entendimento; - Eficiente: realizar tudo o que foi proposto; - Único: não realizar algo que outro trecho de código já faz; - Direto: não dar voltas para chegar no resultado; - Feito com atenção: o código deve ser sempre feito com preocupação e revisto depois de pronto; #### ***Bad Smells*** Em contra partida, também existem as *Bad Smells*, que como sua tradução já sugere, é algo com cheiro ruim, e representa o código com práticas que não devem ser utilizadas. Essas práticas se devem aos códigos que fogem das características de um código limpo. Abaixo são demonstrados alguns exemplos de *bad smells*. ##### **Encadeamento de If's** ```PASCAL if (Q_Manut.FieldByName('DiasDireito').AsFloat = 0) then begin if (AnsiSameText(Trim(Q_Manut.FieldByName('DiasDireito').AsString), ''))) then begin if (MessageDialog.Show('Não foi informado os dias de Direito. Deseja' + 'Continuar?', mtConfirmation, [mbYes, mbNo], 0) = mrNo) then begin Q_Manut.FieldByName('DiasDireito').FocusControl; Abort; end; end; end; ```Esse tipo de encadeamento pode ser facilmente substituído pela palavra reservada "and".
```PASCAL if (Q_Manut.FieldByName('DiasDireito').AsFloat = 0) and (AnsiSameText(Trim(Q_Manut.FieldByName('DiasDireito').AsString), ''))) and (MessageDialog.Show('Não foi informado os dias de Direito. Deseja' + 'Continuar?', mtConfirmation, [mbYes, mbNo], 0) = mrNo) then begin Q_Manut.FieldByName('DiasDireito').FocusControl; Abort; end; ``` --- ##### **Excesso de if-else** ```PASCAL if (Q_Manut.FieldByName('DiasDireito').AsFloat = 0) then LTipo := 1 else if (Q_Manut.FieldByName('DiasDireito').AsFloat = 1) then LTipo := 2 else if (Q_Manut.FieldByName('DiasDireito').AsFloat = 2) then LTipo := 3 else if (Q_Manut.FieldByName('DiasDireito').AsFloat = 3) then LTipo := 5 else LTipo := 9; ```O trecho acima pode ter sua estrutura facilitada com a utilização de um "case".
```PASCAL case Q_Manut.FieldByName('DiasDireito').AsFloat of 0: LTipo := 1; 1: LTipo := 2; 2: LTipo := 3; 3: LTipo := 5; else LTipo := 9; end; ``` --- ##### **Atribuição Indireta**Situações com atribuições indiretas aparecem constantemente no código
```PASCAL if (Q_Manut.FieldByName('DiasDireito').AsFloat = 0) then LDeveIncrementar := False else LDeveIncrementar := True; ``` ```PASCAL if (RG_TipoCliente.ItemIndex = 0) then LTipo := 0 else (RG_TipoCliente.ItemIndex = 1) then LTipo := 1; ```Basta uma pequena análise por parte do desenvolvedor para perceber que podem ser simplificadas
```PASCAL LDeveIncrementar := (Q_Manut.FieldByName('DiasDireito').AsFloat <> 0); ``` ```PASCAL LTipo := RG_TipoCliente.ItemIndex; ```