.NET - Sugestões e dicas para escrever um 'bom código'


Qualquer um pode escrever código. Com poucos meses de experiência em programação, você pode escrever aplicações úteis. Fazê-las funcionar é fácil, mas escrever código da maneira correta requer mais trabalho do que fazer o código funcionar.

A maioria dos programadores escrevem um 'código que funciona', mas não um 'bom código'. Escrever um 'bom código' é uma arte e você deve aprender e praticar sempre para escrever um 'bom código'.

Todo mundo pode ter diferentes definições para o termo 'bom código'. Na minha definição, as características de um bom código são:

A maioria dos desenvolvedores estão inclinados a escrever código para obter um melhor desempenho comprometendo a confiabilidade e facilidade de manutenção. Considerando o retorno do investimento a longo prazo (Return On Investment-ROI), eficiência e desempenho vem abaixo da confiabilidade e da facilidade de manutenção. Se o seu código não é confiável e de fácil manutenção, você (e sua empresa) estarão gastando muito tempo para identificar problemas, tentando entender o código ao longo da vida de sua aplicação.

Existem vários fatores que contribuem para 'escrever um bom código' e assim desenvolver aplicativos confiáveis e de fácil manutenção. Neste artigo vamos abordar alguns deles. Vamos começar com a padronização...

Por que padronizar a escrita do código ?

As convenções de nomenclatura, padrões de codificação e as melhores práticas descritas neste documento são compilados a partir de minha própria experiência e de diversos materiais e orientações da Microsoft e de outras fontes.

Não existe uma norma padrão que deve ser obrigatoriamente usada na padronização de código. Na verdade existem várias normas e nenhuma delas esta totalmente errada ou totalmente certa. Seguir alguma regra é melhor do que não seguir nenhuma, assim o importante é você adotar uma abordagem e segui-la.

Se individualmente a padronização já é difícil imagem em uma equipe de desenvolvedores trabalhando no mesmo projeto ?

Quando se tem uma equipe com diferentes habilidades e gostos é mais difícil obter uma padronização pois tem que se gastar um tempo para convencer todos a seguir os mesmos padrões.

Neste caso uma abordagem adequada seria definir um documento padrão a partir de um consenso comum e usar este documento como um modelo a ser seguido pela equipe. Após o início dos trabalhos o código seria revisado para verificar que todos estão seguindo as regras definidas.

Convenções de nomenclatura

Veremos a seguir algumas convenções de nomenclaturas usadas. O objetivo não é definir uma delas mas mostrar o que já foi e esta sendo usado e indicar a mais adequada.

Notação Húngara

A Notação húngara, criada por Charles Simonyi, visa a facilitar o reconhecimento do tipo de variável num programa. O nome foi dado a partir de uma brincadeira comum entre os primeiros a conhecer a notação que a achavam estranha, fazendo o seguinte comentário: "É tão estranho que até parece húngaro".(wikipéida : http://pt.wikipedia.org/wiki/Nota%C3%A7%C3%A3o_h%C3%BAngara )

Tipos de indicadores usados pela notação húngara:

Nome Descrição Exemplo:
bVerdade boolean
sNome string
uValor inteiro
msgAviso message
s String
sz Aponta o primeiro caracter da terminação zero da string
st Ponteiro da string, o primeiro byte é contado dos caracteres
h handle (título)
msg Message
fn function (usada com pointer)
c char (8 bits)
by unsigned char (byte or uchar - 8 bits)
n Int
b Boolean (verdadeiro ou falso)
f Flag (boolean, logical)
u integer
w Word
ch Char, com texto ASCII
l long int (32 bits)
dw unsigned long int (dword - 32 bits)

A notação húngara era muito usada na linguagem Visual Basic mas atualmente não se recomenda usar este padrão.

CamelCase

CamelCase é a denominação em inglês para a prática de escrever palavras compostas ou frases, onde a primeira letra da primeira palavra é iniciada com minúscula e unidas sem espaços. É um padrão largamente utilizado em diversas linguagens de programação, como Java, Ruby, PHP e Python, principalmente nas definições de Classes e Objetos. ( http://pt.wikipedia.org/wiki/CamelCase)

Ex: valorDoDesconto , nomeCompleto, valorDoImpostoSobreServico

PascalCase

Escreve palavras compostas ou frases montadas com palavras onde a primeira letra de cada palavra é iniciada com maiúscula. O padrão PascalCase é mais utilizado para nomear métodos e classes.

Ex: CalculaImpostoDeRenda(), ValorDoDesconto

Usando CamelCase e PascalCase

Poderiamos dizer que atualmente a grande maioria dos desenvolvedores das linguagens C# e VB .NET usa uma mistura de CamelCase com PascalCase.

Estrutura Nomenclatura Exemplo
Classe Pascal PessoaJuridica
Interface I + Pascal ICliente
Método Público , Propriedades Pascal NomeCompleto()
Método Privado Camel Case calculaDesconto()
Variável Pública Pascal SobreNome
Variável Privada Camel Case impostoPredial
Constantes Maiúsculas com sublinhado        VALOR_DESCONTO    

Sugestões para escrever um bom código

A seguir temos algumas sugestões para escrever um bom código começando com a padronização:

Padronização

1. Usar PascalCase para nomes de classes

2. Usar PascalCase para nomes de métodos

3. Usar CamelCase para variáveis e parâmetros de métodos

int totalContador = 0;
void ExibirMensagem(string nome)
{
   string mensagemCompleta = "Ola " + nome;
}

4. Usar o prefixo 'I' com CamelCase para nomear interfaces : IEntidade

5. Não usar a notação húngara para nomear variáveis.

6. Utilize nomes significativos para nomear variáveis. Não use abreviações.

Ruim Bom
string nom string nome
int valSal      int valorSalario
Dim ende as String Dim endereco as String

7. Não use nome de variáveis com um único caractere como i, n, c, s, t. Prefira usar nomes como : indice, temp, contador, etc. A exceção seria a utilização de i para os contadores em laços for/next.

8. Não utilize sublinhado para variáveis locais.Ex: _email (ruim)

9. Não utilize nomes de variáveis que são usadas como palavras chaves.

10. Utilize o prefixo apropriado para cada elemento de interface usado.

Ex: Principais controles e abreviações sugeridas:

Controle Prefixo
Label lbl
TextBox txt
DataGrid dtg
Button btn
Hyperlink hlk
DropDownlist ddl
ListBox lst
DataList dtl
CheckBox chk
RadioButton rdo
Image img
Panel pnl
Table tbl

Indentação e espaçamento

1. Use TAB para indentação. Não use espaços. Defina o tamanho do Tab como igual 4.

2. Comentários devem estar no mesmo nível que o código (usa o mesmo nível de recuo).

Bom Ruim
//Formato de uma mensagem e exibição
string mensagemCompleta = "Olá" + nome;
DateTime dataAtual = DateTime.Now;
//Formato de uma mensagem e exibição
       string mensagemCompleta = "Olá" + nome;  
       DateTime dataAtual = DateTime.Now;

3. Chaves { } devem estar no mesmo nível que o código fora as chaves. (Para linguagem C#)

Bom Ruim
 if(string.IsNullOrEmpty(nome))
 {
      Console.Write("Mensagem");
      Console.WriteLine("\n");
      Console.ReadLine();
 }
if(string.IsNullOrEmpty(nome)) {
     Console.Write("Mensagem");
     Console.WriteLine("\n");
     Console.ReadLine();
}

4. Use uma linha em branco para separar grupos lógicos de códigos:

Bom
Ruim
 bool Aviso ( string nome )
{
	string mensagemCompleta = "Ola " + nome;
	DateTime dataAtual = DateTime.Now;

	string mensagem = mensagemCompleta + ", a hora é : " 
+ dataAtual.ToShortTimeString();
	MessageBox.Show ( mensagem);

	if ( ... )
	{
		// faça algo
		// ...
		return false;
	}
	return true;
}
bool Aviso ( string nome )
{
   string mensagemCompleta = "Ola " + nome;
   DateTime dataAtual = DateTime.Now;
   string mensagem = mensagemCompleta + ", a hora é : " 
+ dataAtual.ToShortTimeString();
   MessageBox.Show ( mensagem);
   if ( ... )
   {
      // faça algo
      // ...
      return false;
    }
   return true;
}

5. Use um único espaço antes e depois de cada operador e colchetes.

Bom
Ruim
if ( exibeValor == true )
{
	for ( int i = 0; i < 10; i++ )
	{
		//
	}
}
if (exibeValor==true )
{
	for (int i=0;i<10;i++ )
	{
		//
	}
}

6. Use o recurso #region para agrupar partes relacionadas de código.

Boas práticas de programação

1. Evite escrever métodos muito longos. Um método normalmente não deve ter mais 30 linhas de código. Se um método tem muitas linhas de código você deve considerar fazer uma refatoração.

2. Nome do método deve dizer o que ele faz. Se o nome do método é evidente, não há nenhuma necessidade de documentação que explique o que o método faz.

Bom
Ruim
void CalculaValorImposto( int salario )
{
   // calcula imposto
}
//Este método calcula o valor do imposto
void Calcular( int salario )
{
   // calcula imposto
}

3. Um método deve fazer apenas "uma única tarefa". Não combine mais de uma tarefa em um único método, mesmo que elas sejam simples. Um método deve ter uma única responsabilidade. (Princípio SRP)

Bom
Ruim
void CalculaValorImposto ( int salario )
{
    // calcula imposto
    double  imposto  = salario * 0.25;
}
void Calcular ( int salario )
{
   // calcula imposto
   double  imposto  = salario * 0.25;
   // envia um email informando o cliente
   EnviaEmail(); 
}

4. Sempre preste atenção para valores inesperados. Por exemplo, se você estiver usando um parâmetro com dois valores possíveis, nunca assuma que se um valor não for escolhido então a única possibilidade é o outro valor.

Bom
Ruim
If ( tipoMembro == Registrado )
{
	//Registrado
}
else if ( tipoMembro == Convidado )
{
	//Convidado
}
else
{
        // Tipo inesperado lança uma exceção 
        // (Se um novo tipo for introduzido podemos 
        //  encontrar o problema com facilidade)
        throw new Exception (“Tipo de membro inesperado “ + tipoMembro.ToString() + “’.”)
}
If ( tipoMembro == Registrado )
{
	//Registrado
}
else
{
          //Convidado
}
Se um novo tipo for incluido
vai ocorrer o codigo esta tratando
apenas dois tipos e não ocorre erro
algum dificultando sua localização.

5. Não defina números fixos no seu código. Use constantes em seu lugar. Declare constantes no início do arquivo e as utilize em seu código:

Bom
Ruim
const int meses = 12;
const int semanas = 52;
const int dias = 365;

const double diasPorSemana = (double) dias / (double) semanas;
const double diasPorMes = (double) dias / (double) meses;

const double diasPorSemana = (double)  365 / 52;
const double diasPorMes = (double) 365 / 12;

6. Converta strings para minúsculas ou maiúsculas antes de fazer comparações. Isto irá assegurar que a string sendo comparada irá corresponder, mesmo se ela for escrita de forma diferente. Ex: macoratti e Macoratti:

Bom
Ruim
if (nome.ToLower () == "macoratti")
{
   // ...
}
if (nome == "macoratti")
{
   // ...
}

7. Use String.Empty ao invés de ""

Bom
Ruim
if (nome == String.Empty 
{
   // ...
}
if (nome == "")
{
   // ...
}

8. Evite usar de variáveis membros. Declare variáveis locais sempre que necessário e passe-as a outros métodos em vez de compartilhar uma variável de membro entre os métodos. Se você compartilhar uma variável de membro entre os métodos, será difícil rastrear quando e qual método foi alterado.

9. Use o recurso enum sempre que necessário. Não use números ou strings para indicar valores discretos.

Bom
Ruim
      enum TipoEmail
        {
            Html,
            PlainText,
            Attachment
        }

        void EnviaEmail(string mensagem, TipoEmail tipoDeEmail)
        {
            switch (tipoDeEmail)
            {
                case TipoEmail.Html:
                    // manda email
                    break;
                case TipoEmail.PlainText:
                    // manda email
                    break;
                case TipoEmail.Attachment:
                    // manda email
                    break;
                default:
                    // manda email
                    break;
            }
        }
void EnviaEmail(string mensagem, TipoEmail tipoDeEmail)
        {
            switch (tipoDeEmail)
            {
                case "Html":
                    // manda email
                    break;
                case "PlainText":
                    // manda email
                    break;
                case "Attachment":
                    // manda email
                    break;
                default:
                    // manda email
                    break;
            }
        }

10. Não crie variáveis membro como públicas ou protegidas. Mantenha-as como privadas e exponha as propriedades public/protected.

11. Não use código para acionar um evento de um botão para executar a mesma ação que você escreveu no evento click do botão. Em vez disso, chamar o mesmo método que é o evento clique do botão chama.

12. Nunca defina um caminho ou nome da unidade fixo no código. Obtenha o caminho do aplicativo via código e use o caminho relativo.

13. Nunca assuma que o seu código será executado a partir da unidade "C:". Alguns usuários podem executá-lo a partir da rede ou a partir de um "Z:".

14. No inicio da aplicação, faça algum tipo de "verificação" e garanta que todos os arquivos e dependências necessárias estão disponíveis nos locais previstos. Verifique se há conexão com o banco de dados. Envie uma mensagem amigável para o usuário em caso de problemas.

15. Se o arquivo de configuração necessário não for encontrado, a aplicação deve ser capaz de criar um arquivo com valores padrão.

16. Se um valor errado for encontrado no arquivo de configuração, a aplicação deve lançar um erro ou dar uma mensagem e também deve informar ao usuário quais são os valores corretos.

17. Mensagens de erro devem ajudar o usuário a resolver o problema. Nunca envie mensagens de erro como "Erro na aplicação", "Há um erro", etc. Prefira usar mensagens específicas como "Falha ao atualizar o banco de dados. Verifique se o ID de usuário e senha estão corretos."

18. Mostre mensagens curtas e amigáveis ao usuário. Mas registre o erro real (log de auditoria) com todas as informações possíveis. Isso vai ajudar muito no diagnóstico de problemas.

19. Não tenha mais de uma classe em um único arquivo. (Você pode criar diversas classes em um mesmo arquivo, mas isso dificulta a localização de erros.)

20. Evitar criar arquivos muito grandes. Se um único arquivo tem mais que 1000 linhas de código ele é um bom candidato a refatoração.

21. Sempre capture somente a exceção específica e não a genérica.

22. Quando você relançar uma exceção use a instrução throw sem especificar a exceção original. Dessa forma a pilha original é preservada.

catch
{
     // trate a exceção
     throw;	
}

23. Não escreva muito blocos try-catch muito grandes. Se necessário, escreva um try-catch separado para cada tarefa que você executa e coloque apenas a parte específica do código dentro do try-catch. Isso irá ajudá-lo a descobrir qual parte do código gera a exceção e você pode dar a mensagem de erro específica para o usuário.

24. Escreva suas próprias classes de exceção personalizada se necessário na sua aplicação. Não derive as suas exceções personalizadas da classe base SystemException, prefira herdar de de ApplicationException.

Arquitetura

1. Sempre use arquitetura em camadas.

2. Nunca acessar banco de dados a partir das páginas da interface do usuário. Sempre tenha uma camada de acesso a dados que executa todas as tarefas relacionadas com banco de dados. Isso irá ajudá-lo a apoiar ou migrar para outro banco de dados com mais facilidade.

3. Use try-catch em sua camada de dados para capturar todas as exceções de banco de dados. Este manipulador de exceção deve registrar todas as exceções do banco de dados. Os dados gravados devem incluir o nome do comando que está sendo executado, nome do procedimento armazenado, parâmetros, string de conexão usada, etc. Depois de gravar a exceção, ela pode ser lançada para que uma outra camada da aplicação possa tomar as medidas adequadas.

4. Separe sua aplicação em várias assemblies. Agrupe todas as classes de utilitários independentes em uma biblioteca de classe separada. Todos os seus arquivos relacionados com banco de dados podem estar em uma outra biblioteca de classes.

ASP .NET

1. Não use as variáveis ??de sessão em todo o código. Use variáveis ??de sessão apenas dentro das classes e exponha os métodos para acessar o valor armazenado nas variáveis ??de sessão. Uma classe pode acessar a sessão usando System.Web.HttpContext.Current.Session

2. Não guarde objetos grandes em sessão. Armazenar objetos grandes em sessão pode consumir muita memória do servidor, dependendo do número de usuários.

3. Sempre use folhas de estilo para controlar a aparência das páginas. Nunca especifique o nome e tamanho da fonte, em qualquer uma das páginas. Use a classe de estilo apropriada. Isso irá ajudá-lo a mudar a interface do usuário do seu aplicativo facilmente no futuro. Além disso, se você quiser apoiar a personalização da interface do usuário para cada cliente, é apenas uma questão de desenvolver uma outra folha de estilo para eles.

Comentários

1. Não escreva comentários para cada linha de código e cada variável declarada.

2. Use // ou /// para comentários. Evite usar / * ... * /

3. Escrever comentários sempre que necessário. Mas um bom código legível vai exigir muito menos comentários. Se todas as variáveis e nomes de métodos forem significativos, isso torna o código muito legível e não precisa de muitos comentários.

4. Não escreva comentários se o código for fácil de entender sem comentário. A desvantagem de ter muitos comentários é que, se você alterar o código e se esqueça de alterar o comentário, isso vai causar a mais confusão.

5. Se você tem que usar um pouco de lógica complexa ou pouco usual, por qualquer motivo, trate de documentá-lo muito bem com os comentários suficientes.

6. Se você inicializar uma variável numérica com um número especial diferente de 0, -1 etc., documento a razão para a escolha desse valor.

7. Realize a verificação ortográfica nos comentários e também certifique-se de que gramática e pontuação correta estão sendo usados.

Espero que as sugestões e dicas o ajudem a escrever um código mais robusto, confiável e sustentável.

Joã 3:16 Porque Deus amou o mundo de tal maneira que deu o seu Filho unigênito, para que todo aquele que nele crê não pereça, mas tenha a vida eterna.

Joã 3:17 Porque Deus enviou o seu Filho ao mundo, não para que julgasse o mundo, mas para que o mundo fosse salvo por ele.

Joã 3:18 Quem crê nele não é julgado; mas quem não crê, já está julgado; porquanto não crê no nome do unigênito Filho de Deus.

Referências:


José Carlos Macoratti