" Um blog com dicas e tutorias sobre o mundo da programação "

Boas práticas de codificação

Essas boas práticas são focadas em manutenção, legibilidade, testabilidade e compreensão do seu código. Se você já escreve um código de fácil manutenção, legível, testável e compreensível, então essas boas práticas vão te ajudar a escrever um código de mais fácil manutenção, mais legível, mais testável e mais compreensível.

Nota: Deixando claro que você não precisa utilizar todos os passos descritos nesse texto, mas é altamente recomendável que sejam utilizados. Nota 2: Algumas dessas dicas são tiradas do conceito Object Calisthenics, aqui tem um bom artigo sobre esse assunto, e outras são tiradas do meu dia a dia analisando códigos 😄. Nota 3: A linguagem de programação utilizada nesse artigo foi o C#.

1. Apenas um nível de indentação por método

Tendo muitos níveis de indentação no seu código, pode gerar uma baixa legibilidade e baixa manutentabilidade. Muitas vezes, você não consegue entender com facilidade na sua cabeça o código sem compilar, especialmente se você tem várias condições em diferentes níveis, ou um laço dentro de outro laço, como por exemplo:

class Board {
    public String board() {
        StringBuilder buf = new StringBuilder();

        // 0
        for (int i = 0; i < 10; i++) {
            // 1
            for (int j = 0; j < 10; j++) {
                // 2
                buf.append(data[i][j]);
            }
            buf.append("\\n");
        }

        return buf.toString();
    }
}

Para melhorar esse código, você tem que quebrar o seu método em outras partes. Martin Fowler, no seu livro Refactoring, introduz o padrão de Extração de Métodos, que é exatamente o que deve ser feito. Você não vai reduzir o números de linhas do seu código, mas vai melhorar a legibilidade em uma forma muito significante.

class Board {
    public String board() {
        StringBuilder buf = new StringBuilder();

        collectRows(buf);

        return buf.toString();
    }

    private void collectRows(StringBuilder buf) {
        for (int i = 0; i < 10; i++) {
            collectRow(buf, i);
        }
    }

    private void collectRow(StringBuilder buf, int row) {
        for (int i = 0; i < 10; i++) {
            buf.append(data[row][i]);
        }

        buf.append("\\n");
    }
}

Isso vale para If’s também, busque ao máximo possível não ficar utilizando If dentro de If. vamos dar uma olhada no código abaixo

public void insertUser(int id, string user, string email, string cpf) {
	var userClass = new userBd();
	if(!string.isNullOrEmpty(user)){
          if(!string.isNullOrEmpty(email)){
            if(!string.isNullOrEmpty(cpf)){
		userClass.id = id;
		userClass.user = user;
		userClass.email = email;
		userClass.cpf = cpf;
                bd.insert(userClass);
           }
         }
      }
}

Esse é um caso simples e parece bobeira fazer uma refatoração. Mas imagina que se você tivesse códigos entre os If’s e com else no meio do caminho, onde esse método esteja com 50 linhas, até você entender toda a estrutura e a lógica que está sendo feita, já foi um bom tempo gasto analisando o método. Uma forma simples de resolver o código acima (adiantando alguns passos), seria seguinte o passo abaixo

public void insertUser(int id, string user, string email, string cpf) {
	var userClass = new userBd();
	if(string.isNullOrEmpty(user))
	   return;

	if(!string.isNullOrEmpty(email))
	   return;

	if(!string.isNullOrEmpty(cpf))
	   return;
		
	userClass.id = id;
	userClass.user = user;
	userClass.email = email;
	userClass.cpf = cpf;
        bd.insert(userClass);
}

2. Não use a palavra-chave ELSE

A palavra-chave else é muito conhecida do if/else que é incorporada em muitas linguagens de programação. Você se lembra da última condição aninhada que você viu? você realmente gostou? Eu acho que não, então, é exatamente por isso que você deveria evita-la. Vamos retornar ao exemplo anterior, só que agora com o else e alguns código no meio.

public int insertUser(int id, string user, string email, string cpf) {
    var userClass = new userBd();
    if(!string.isNullOrEmpty(user)){
        if(!string.isNullOrEmpty(email)){
            if(!string.isNullOrEmpty(cpf)){
                string pattern = "^\\\\d{3}\\\\.\\\\d{3}\\\\.\\\\d{3}-\\\\d{2}$";  
                Regex rg = new Regex(pattern);
                if(rg.Matches(cpf))
                {
                    var userInBd = bd.select(id)
                                    .where(x => x.user = user
                                            && x.email = email)
                                    .FirstOrDefault();
                    if(userInBd is not null && userInBd.count <= 0)
                    {
                        userClass.id = id;
                        userClass.user = user;
                        userClass.email = email;
                        userClass.cpf = cpf;
                        bd.insert(id, user, email, cpf);
                        return userClass.Id;
                    }
                    else
                    {
                        thow new exception("Usuário já existe na base");
                    }
                }
                else
                {
                    thow new exception("CPF inválido");
                }
            }
            else
            {
                thow new exception("valor não pode ser nulo");
            }
        }
        else
        {
            thow new exception("valor não pode ser nulo");
        }
    }
    else
    {
        thow new exception("valor não pode ser nulo");
    }
}

Imagina que nesse método, que nem é tão grande ou complexo, fosse solicitado que a mensagem de retorno das variáveis nulas fossem alteradas, ou a validação do usuário fosse alterada, ou então que o insert fosse feito mesmo com o cpf inválido e que a validação fosse feita depois, quanto tempo você gastaria para encontrar, ajustar e testar? Para resolvermos esse problema, vamos aplicar a regra de não ter else e todos as validações/retornos ficam no inicio do código e o restante do método segue a codificação padrão.

public int insertUser(int id, string user, string email, string cpf) {
	if(string.isNullOrEmpty(user))
		thow new exception("valor não pode ser nulo");
	
	if(string.isNullOrEmpty(email))
		thow new exception("valor não pode ser nulo");
	
	if(string.isNullOrEmpty(cpf))
		thow new exception("valor não pode ser nulo");
		
	string pattern = "^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$";  
	Regex rg = new Regex(pattern);
	if(!rg.Matches(cpf))
		thow new exception("CPF inválido");
	
	var userInBd = bd.select(id)
			 .where(x => x.user = user
			        && x.email = email)
			 .FirstOrDefault();
	if(userInBd is null || userInBd.count > 0)
		thow new exception("Usuário já existe na base");
	
	var userClass = new userBd();
	userClass.id = id;
	userClass.user = user;
	userClass.email = email;
	userClass.cpf = cpf;
	bd.insert(id, user, email, cpf);

	return userClass.Id;
}

Agora, ficou bem mais simples de analisar o método e se for solicitado alguma mudança, já fica bem mais fácil de identificar o local, implementar e testar. Mas ainda temos algumas melhorias para aplicar nesse código, vamos dar uma olhada nos próximos passos.

3. Não abrevie

A correta questão é Porque você deseja abreviar?

Você pode responder que é porque você escreve o mesmo nome repetidamente? E eu responderia que esse método é reutilizado várias vezes e se parece com duplicação de código. Portanto, você dirá que o nome do método é muito longo de qualquer maneira. E eu diria que talvez sua classe tenha múltiplas responsabilidades, o que é ruim.

Imagine que você tem que criar um método para verificar se o cnpj ou cpf do cliente fornecedor é válido no site da receita federal antes de ser inserido no banco e você coloca o nome de “CpfCnpjValRF”. Agora, imagine um outro dev que teve que dar uma manutenção no seu código, somente olhando para o nome do método, ele vai conseguir saber o que método faz? ou ele vai ter que abrir o método e estudar a lógica para entender do que se trata? Antes de pensar no nome de uma variável, método ou classe, pense se está claro do que se trata para um outro programador que vai dar manutenção no seu código. No exemplo acima um bom nome seria “VerificaSeCpfOuCnpjEValidoNaReceitaFederal”

Fica a dica, Não abrevie.

4. Use e abuse de regions

No C# temos a opção de agrupar os métodos dentro de regiões, isso facilita muito a vida da pessoa que está lendo uma classe com vários métodos, ela pode fechar partes do código e só deixar aberto aquilo que ela precisa dar manutenção, é uma mão na roda.

Fica a dica, sempre que for dar manutenção em uma classe e precisar adicionar um novo método, verifique se essa classe possui regions e se seu método se encaixe em algum.

#region Métodos de validações
public void ValidaUsuario(string usuario){
	return true;
}
public void ValidaCPF(string cpf){
	return true;
}
#endregion

#regions Métodos de busca
public void BuscaUsuario(int id){
	return usuario;
}
public void BuscaPessoa(int id){
	return pessoa;
}
#endregion

5. Especialize seus métodos

Quanto maior seu método, mais difícil de uma outra pessoa entender, com isso dificulta a manutenção e também fica mais difícil de ser reaproveitado. Busque sempre criar métodos que tenha uma finalidade única por trás e se mesmo assim ele tiver várias linhas, busque quebrar partes do método em métodos menores, especialize esses trechos. Isso facilita muito o entendimento do método principal, o reaproveitamento de código e consequentemente a manutenção.

Vamos agora pegar o segundo exemplo de código que vimos no passo 2 e aplicar todas as regras nele e ver como fica.

#region Métodos de edição na base
public int insertUser(int id, string user, string email, string cpf) {
	VerificaSeStringENulaOuVazio(user);
	VerificaSeStringENulaOuVazio(email);
	VerificaSeStringENulaOuVazio(cpf);
	VerificaSeCpfEValido(cpf);
	VerificaSeUsuarioJaExiste(user, email);
		
	var userClass = ConstruirClasseUser(id, user, email, cpf);
	bd.insert(userClass);

	return userClass.Id;
}
#endregion

#region Métodos de busca na base
public User BuscaUsuario(string user, string email){
	return bd.select(id)
		 .where(x => x.user = user
			&& x.email = email)
		 .FirstOrDefault();	
}
#endregion

#region Métodos de validação
private void VerificaSeStringENulaOuVazio(string Valor){
	if(string.isNullOrEmpty(Valor))
		thow new exception("valor não pode ser nulo");
}

private void VerificaSeCpfEValido(string cpf){
	string pattern = "^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$";  
	Regex rg = new Regex(pattern);

	if(!rg.Matches(cpf))
		thow new exception("CPF inválido");
}

private void VerificaSeUsuarioJaExiste(string user, string email){
	var userInBd = BuscaUsuario(user, email);

	if(userInBd is null || userInBd.count > 0)
		thow new exception("Usuário já existe na base");
}
#endRegion

#region Construtor de classe
public User ConstruirClasseUser(int id, string user, string email, string cpf){
	var userClass = new userBd();
	userClass.id = id;
	userClass.user = user;
	userClass.email = email;
	userClass.cpf = cpf;

	return userClass;
}
#endregion

Agora, depois dessas dicas ditas aqui e você for dar suporte no código de outra pessoa, só de você bater o olho no método principal, rapidamente você vai conseguir identificar quais validações estão sendo feitas e o que o método entrega no final, sem ter que fica analisando If a If, else a else. Com essas dicas você também deixa sua classe muito mais independente, pois você pode utilizar os métodos de validação em outros métodos ou reaproveitar dentro do próprio método como por exemplo, antes tínhamos um If para cada variável, agora é um método que foi reaproveitado e que se for modifica já é refletido em todos os casos. Agora podemos reaproveitar também o método de “buscaUsuario”, que antes estava no meio do código e que se quiséssemos adicionar uma condição para trazer somente os usuários ativos, teríamos que fazer isso em todas as partes que faziam uma busca de usuário, sendo que agora temos um único lugar para alterar, sendo refletido para todo mundo que usar esse método. O exemplo para a validação do cpf segue a mesma lógica e no construtor do objeto “userBd” também. Agora, se te fosse solicitado alterar a mensagem de uma validação, você saberia exatamente onde alterar somente batendo o olho no método principal “insertUser”, evitando assim muito tempo gasto analisando código. Outro ponto importante, esse código está na forma ideal para implantarmos os testes automatizados, mas vou deixar os detalhes dessa parte para um outro texto.

Conclusão

Esse texto contém apenas algumas dicas, mas que já melhoram e muito a legibilidade de um código. Se você não se sentir confortável com esses passos, está tudo tranquilo. Mas confie em mim quando eu digo que dá para ser utilizado na vida real. Tente iniciando com alguns passos, eu acredito que é só uma questão de prática. Isso vai ajudar muito o entendimento do seu código pelo seu colega de trabalho.

Boa codificação.

YOU MIGHT ALSO LIKE

0 Comments

Leave A Comment

Your email address will not be published.