Clean Code é uma forma de desenvolvimento de sistemas com a aplicação de práticas voltadas a facilitar a escrita e a leitura de um código para ser o mais limpo e compreensível possível. Quando se aplica esta filosofia na criação de códigos, fica muito mais fácil realizar testes, correções e manutenção do código-fonte, prolongando a vida do software.
Vale ressaltar que a expressão do código limpo ganhou popularidade em 2008 com a publicação do livro “Clean Code: A Handbook of Agile Software Craftsmanship” de Robert Cecil Martin ou Uncle Bob como também é conhecido.
Presente no cenário de desenvolvimento desde 1970, Uncle Bob notou que a dificuldade em fazer a manutenção de códigos é um dos principais desafios na área de programação. Isso porque à medida que surgem novas demandas de atualizações ou funções, lidar com código mal elaborado torna este processo cada vez mais complexo.
O que é considerado um Bad Code (Código Sujo)?
Bad Code ou Código Sujo são códigos de programação considerados difíceis de entender, expandir ou fazer manutenção. Escritos de maneira desorganizada, não seguem boas práticas de desenvolvimento e engenharia de software, resultando em um código de baixa qualidade.]
Dificilmente um programador não se deparou com um Bad Code e com a dor de cabeça que eles podem gerar, uma vez que códigos desorganizados criam um efeito dominó, onde pequenas alterações podem desencadear problemas em todo o sistema, transformando tarefas simples em problemas significativos.
Quando um código é mal estruturado, surgem desafios como dificuldade para corrigir erros, necessidade de refatorações extensas e aumento do tempo de desenvolvimento. Por isso, é essencial adotar práticas de Clean Code como nomes descritivos, funções com responsabilidades únicas e modularidade.
Um código bem organizado não só facilita a manutenção, mas também acelera o progresso e reduz riscos. Investir em boas práticas desde o início é a melhor forma de garantir que o sistema evolua de maneira eficiente e sustentável.
Para que serve o Clean Code?
É uma prática essencial no desenvolvimento de software que visa simplificar a criação e a manutenção de códigos. Considerando que atualizações e novas funcionalidades são inevitáveis, ter um código bem estruturado e organizado é fundamental para garantir a eficiência e a longevidade de um sistema.
No entanto, muitos programadores negligenciam a importância do Clean Code, acreditando que se o código está funcionando, não há motivos para preocupação. Isso pode levar a problemas futuros, já que a manutenção e a revisão de códigos são etapas indispensáveis no ciclo de vida de qualquer projeto.
Outra questão, é que qualquer código pode se tornar ultrapassado e cair em desuso. Um código desatualizado ou que precise de algum remendo fica difícil de manter, tornando mais fácil começar um novo sistema do que continuar a operar uma versão ruim.
Adotando as boas práticas do Clean Code evitam-se problemas, promovendo a clareza, a legibilidade e a organização do código. Isso não só facilita a implementação de novas funcionalidades, mas também reduz custos e tempo de desenvolvimento, já que um código bem escrito é mais fácil de entender e modificar.
Por que o Clean Code é importante?
O Clean Code propõe o desenvolvimento de uma codificação legível e de fácil manutenção, a fim de qualquer pessoa que precise ler o código, compreenda com tranquilidade e faça alterações e correções com praticidade.
Afinal de contas, é impossível não haver a necessidade de mudanças em um sistema. Seja por modificações nos objetivos de negócio da empresa, como por novas necessidades dos usuários, é essencial ter um software que se adequa facilmente a novos cenários.
Quais são os 7 princípios do Clean Code?
Os princípios do Clean Code são baseados nas ideias de Robert C. Martin (Uncle Bob). Eles incluem boa nomenclatura, escrita simples, aplicação de funções mais curtas e diretas, cuidado com inserção excessiva de comentários durante a codificação, tratamento dos erros e a realização de testes.
Vejamos mais detalhes seguir.
1. Nomes fáceis e significativos
Um código deve ser fácil de ler e entender. Transmitir a ideia com objetividade e clareza, faz toda diferença na hora de interpretar o código.
Portanto, use nomes significativos para variáveis, funções e classes, evitando abreviações obscuras ou nomes genéricos.
2. Código Simples
Ao programar deixe o código o mais limpo e simples possível para ficar o mais acessível para futuros programadores. Agora, quando for o caso onde você está continuando uma programação que não foi você que iniciou, faça como os escoteiros e “deixe o lugar mais limpo do que quando você o encontrou.”
3. Funções simples e curtas
Classes e funções devem ser objetivas e bem definidas. Isso porque os códigos funcionam de maneira narrativa, então é preciso que os programadores, como autores do desenvolvimento, se atentem para o modo como a história será contada.
4. DRY (Don’t Repeat Yourself)
Para manter uma boa legibilidade do software, se apegue ao princípio do “não repita a si mesmo” descrito no livro The Pragmatic Programmer, evitando o uso de diversos “ifs” em sequência, aplicando uma representação única em cada parte do conhecimento.
Evite a repetição de códigos extraindo funcionalidades duplicadas em funções ou módulos reutilizáveis.
5. Cuidado com comentários
À medida que a programação do código é alterada, os comentários passam a não condizer com a realidade, ficando por vezes esquecidos.
Assim, tenha cuidado com a inserção excessiva de comentários. Certifique-se em fazê-los apenas quando forem realmente necessários.
6. Tratamento de erros
Erros podem interromper o bom funcionamento de um software e afetar diretamente a rotina de uma empresa. É importante realizar o tratamento de erros, realizando a correção dos códigos para impedir este tipo de situação.
7. Testes limpos
A cada nova funcionalidade ou alteração, é essencial realizar testes para garantir o correto funcionamento do sistema. Até porque, testar o sistema é uma etapa importante e contínua no desenvolvimento de software.
Um teste bem feito, ou limpo, deve ser:
- Rápido: Devido sua execução repetitiva, é fundamental que os testes tenham um processamento ágil para não atrasar o ciclo de desenvolvimento;
- Independente: Cada teste deve ser autossuficiente e não depender de outros testes para não impactar o sistema como um todo, evitando efeitos em cascata.
- Repetível: Repetir testes faz parte do desenvolvimento, por isso os códigos devem permitir a repetição de testes várias vezes em ambientes distintos;
- Passível de validação: Um teste eficaz deve fornecer resultados claros e objetivos, retornando respostas booleanas (true ou false) para indicar sucesso ou falha;
- Pontual: Os testes devem ser escritos de forma precisa. Isso ajuda a evitar complexidades desnecessárias e garante que o código atenda aos requisitos desde o início.
Como fazer um código limpo?
Escrever um código limpo exige a adoção de boa filosofia de desenvolvimento. Entre elas estão algumas regras gerais e específicas ligadas aos comentários, estruturação do código, nomes, funções e métodos.
Confira a seguir algumas dicas de regras e boas práticas para fazer um clean code.
Regras gerais
As regras gerais para o desenvolvimento de código limpo abordam direcionamentos como: seguir convenções, manter o código simples e estar atento às origens dos problemas. Confira as regras com mais detalhes a seguir.
Siga as convenções
Quando se entra em um novo projeto com convenções estabelecidas, procure segui-las à risca. Por exemplo, se são utilizadas constantes em letras maiúsculas ou enumeradores com prefixo “O”, não importa qual é a regra, o importante é aderir aos padrões previamente definidos.
Mantenha o código simples (KISS)
Siga o princípio KISS: Keep It Stupid Simple (Mantenha isso estupidamente simples)!
Não complique as coisas, mantenha tudo o mais simples possível para que as resoluções sejam feitas de forma prática.
Procure a causa raiz do problema
Não se atenha a superficialidades. Procure se aprofundar nos problemas para realmente corrigi-los e evitar retrabalhos.
Regras de design
Quando pensamos no design do código é importante manter os dados de configuração em alto nível, utilizar processamentos em threads separados e se atentar para algumas indicações. Confira.
Mantenha dados de configuração em alto nível
Toda aplicação possui configurações, como as famosas ConnectionStrings. É importante manter essas configurações ou o processo de interpretação delas no nível mais alto que conseguir.
Evite:
public class MeuServico
{
public void Conectar()
{
string conexao = “Server=meuServidor;Database=minhaBase;User Id=usuario;Password=senha;”;
var banco = new Banco(conexao);
}
}
Recomendado:
public class Configuracao
{
public string ConnectionString { get; set; }
}
public class MeuServico
{
private readonly Configuracao _config;
public MeuServico(Configuracao config)
{
_config = config;
}
public void Conectar()
{
var banco = new Banco(_config.ConnectionString);
}
}
Isso permite que a configuração seja gerenciada centralmente e facilmente alterada sem precisar modificar o código principal.
Mult-thread
Utilize processamento em threads separados sempre que possível. O C# oferece suporte a multi-threads e paralelismo há algum tempo, e recursos como Async/Await facilitam essa implementação. Por exemplo:
Sem async/await:
[HttpGet(“produtos”)]
public IActionResult ListarProdutos([FromServices] IProdutoRepository repository)
{
ViewBag.Produtos = repository.GetProdutos();
return View();
}
Com async/await:
[HttpGet(“produtos”)]
public async Task<IActionResult> ListarProdutos([FromServices] IProdutoRepository repository)
{
ViewBag.Produtos = await repository.GetProdutosAsync();
return View();
}
Neste exemplo, a operação de “buscar produtos” foi transformada em assíncrona, utilizando async/await para melhorar a eficiência e a responsividade da aplicação.
Separe os códigos mult-thread
Uma das bases para um código limpo é manter o que é assíncrono separado do que é síncrono para que um trecho do código não force o outro. Utilizando o mesmo exemplo do tópico anterior fica assim:
Método síncrono:
[HttpGet(“produtos”)]
public IActionResult ListarProdutos([FromServices] IProdutoRepository repository)
{
ViewBag.Produtos = repository.GetProdutos();
return View();
}
Método assíncrono:
[HttpGet(“produtos/async”)]
public async Task<IActionResult> ListarProdutosAsync([FromServices] IProdutoRepository repository)
{
ViewBag.Produtos = await repository.GetProdutosAsync();
return View(“ListarProdutos”); // Reutiliza a mesma view do método síncrono
}
Dessa forma, o método síncrono “ListarProdutos” e o método assíncrono “ListarProdutosAsync” são mantidos separados, permitindo que cada um seja utilizado conforme a necessidade, sem forçar a sincronicidade ou assincronicidade em toda a cadeia de chamadas. Isso também facilita a manutenção e a clareza do código.
Utilize Async como sufixo
Sempre utilize o sufixo async para identificar um método assíncrono. Por exemplo:
public async Task<IEnumerable<Produto>> BuscarProdutosAsync(int categoriaId)
{
// Simula uma operação assíncrona, como uma consulta ao banco de dados
return await _produtoRepository.BuscarPorCategoriaAsync(categoriaId);
Evite configurações desnecessárias
Procure não deixar configurações que ainda não tiverem seus objetivos determinados por alguém. Lembre-se sempre de deixar o código o mais limpo possível e descomplicado.
Evite:
public class Servico
{
private string _parametro = “”;
public Servico()
{
_parametro = “ValorPadrao”; // Definição sem necessidade
}
}
Recomendado:
public class Servico
{
private readonly string _parametro;
public Servico(string parametro)
{
_parametro = parametro ?? throw new ArgumentNullException(nameof(parametro));
}
}
Utilize injeção de dependência
Para deixar o código mais limpo e desamarrado, use injeção de dependência na programação.
Evite:
public class UsuarioService
{
private UsuarioRepository _repository;
public UsuarioService()
{
_repository = new UsuarioRepository(); // Alto acoplamento
}
}
Recomendado:
public class UsuarioService
{
private readonly IUsuarioRepository _repository;
public UsuarioService(IUsuarioRepository repository)
{
_repository = repository;
}
}
Lei de Demeter
A Lei de Demeter (LoD), também conhecida como Princípio do Menor Conhecimento, estabelece diretrizes para promover um design de software mais modular e desacoplado. Seus principais pontos são:
- Cada componente deve ter conhecimento apenas sobre unidades diretamente relacionadas a ele;
- Um componente deve interagir apenas com seus “amigos” próximos, ou seja, objetos com os quais ele tem uma relação direta;
- Não se deve interagir com “estranhos” (objetos indiretos ou distantes). A comunicação deve ocorrer apenas com amigos imediatos.
Vejamos um exemplo:
Classes básicas:
public class Pedido
{
public Pagamento Pagamento { get; set; }
}
public class Pagamento
{
public decimal Valor { get; set; }
public void Processar()
{
// Lógica para processar o pagamento
}
}
Mau exemplo (violando a Lei de Demeter):
public class ProcessadorPedido
{
public void FinalizarPedido()
{
var pedido = new Pedido();
pedido.Pagamento.Processar(); // <– Acesso direto a um objeto distante
}
}
Bom exemplo (respeitando a Lei de Demeter):
public class Pedido
{
public Pagamento Pagamento { get; set; }
public void Finalizar()
{
Pagamento?.Processar(); // Encapsula a lógica dentro da classe Pedido
}
}
public class ProcessadorPedido
{
public void FinalizarPedido()
{
var pedido = new Pedido();
pedido.Finalizar(); // Interage apenas com o objeto imediato (Pedido)
}
Regras sobre entendimento de código
Entender os códigos é essencial para manter a programação bem escrita e com fácil manutenção. Ser consistente, utilizar variáveis concisas, evitar dependências lógicas, entre outras regras vão te ajudar nisso. Veja abaixo.
Seja consistente
A consistência na aplicação do código é fundamental. Caso queira seguir um padrão, mantenha-o em todo o projeto. Isso facilita a leitura e a manutenção do código.
Evite:
int dias; // Nome genérico
int quantidadeDiasNoAno = 365;
Recomendado:
int diasNoAno = 365;
int diasNoMes = 30;
Utilize variáveis concisas
Variáveis devem ser autoexplicativas, evitando a necessidade de comentários adicionais. Escolha nomes que descrevam claramente o propósito da variável. Por exemplo:
// Total do que?
decimal total = 0;
// Total do carrinho de compras
decimal shoppingCartTotal = 0;
Obsessão primitiva
Não foque apenas em tipos primitivos (Built-in). Utilize objetos de valor (Value Objects) para representar conceitos mais complexos e melhorar a clareza do código.
Evite:
public class Pedido
{
public string EnderecoEntrega { get; set; }
}
Recomendado:
public class Endereco
{
public string Rua { get; set; }
public string Cidade { get; set; }
public string Estado { get; set; }
}
public class Pedido
{
public Endereco EnderecoEntrega { get; set; }
}
Evite dependências lógicas
Métodos não devem depender de condições específicas de sua classe para funcionar corretamente. Isso pode levar a comportamentos inesperados e dificultar a manutenção.
Evite:
public class Usuario
{
public bool Logado;
public void AcessarSistema()
{
if (Logado)
{
Console.WriteLine(“Acesso permitido”);
}
}
}
Recomendado:
public class Usuario
{
public bool Logado { get; private set; }
public void Login()
{
Logado = true;
}
}
Evite condicionais negativas
Condicionais negativas podem dificultar a leitura do código. Prefira condições positivas para melhorar a clareza.
Evite:
if(!IsSubscriber) { … }
Utilize:
if(IsSubscriber) { … }
Regras de nomes
Escolha nomes descritivos e significativos para garantir a clareza e a manutenção do código. Nomes bem escolhidos reduzem a necessidade de explicações adicionais e facilitam a compreensão do que o código faz.
Confira algumas dicas para esta que é uma das práticas mais importantes.
Escolha nomes descritivos
Nomes de classes, variáveis e métodos devem ser claros e expressivos. Se você precisa explicar o que um nome representa, significa que você pode melhorá-lo.
Evite:
var y = 100;
// Altura do que? Em qual unidade?
int height = 10;
// Muito mais expressivo
int heightInCentimeters = 10;
Faça distinções significantes
Nomes devem ser distintos o suficiente para que seu significado seja claro e não se confunda com outros nomes no código.
Evite:
var valor = 1500M;
// Tem um significado maior
var valorEmDolares = 1500M;
Utilize nomes com fácil busca e pronúncia
Evite nomes com pronúncia difícil ou criar convenções que não sejam intuitivas. Nomes claros e fáceis de buscar ajudam na manutenção do código.
Evite:
var xptoVar = “valor”;
Recomendado:
var nomeCliente = “João”;
Evite o excesso de strings
O uso repetido de strings pode levar a erros difíceis de detectar. Utilize constantes para não se deparar com problemas de comparação e não desperdiçar tempo buscando um bug.
Evite:
if (status == “ATIVO”) { … }
Recomendado:
public class Status
{
public const string Ativo = “ATIVO”;
}
if (status == Status.Ativo) { … }
Não use prefixo ou caracteres especiais
Evite prefixos que indicam o tipo da variável, método ou classe. Além disso, nunca utilize espaços ou caracteres especiais em nomes.
Evite:
public class clsCustomer { … }
Evite:
string strNome = “José”;
Evite:
var situação – “Pendente”;
Regras para funções ou métodos
Regras como expressividade, coerência e códigos não comentados, são importantes para garantir que suas funções sejam eficientes e fáceis de entender.
Pequenas e com objetivo único
Métodos devem ser pequenos e focados em uma única tarefa. Isso facilita a reutilização e a compreensão do código.
Evite:
public void ProcessarPedido()
{
ValidarCliente();
CalcularFrete();
GerarFatura();
}
Recomendado:
public void ValidarCliente() { … }
public void CalcularFrete() { … }
public void GerarFatura() { … }
Utilize nomes descritivos
Nomes de métodos devem ser claros e descritivos, refletindo exatamente o que o método faz, sem usar caracteres especiais.
Evite:
// O que esse método faz?
public void Calcular() { … }
Utilize:
// Calcula o valor do frete
public void CalcularFrete() { … }
Utilize poucos parâmetros
Tente não exigir muitas coisas de um mesmo objeto. Utilize objetos ou parâmetros opcionais para simplificar a assinatura do método como o Optional Parameters do C#.
Evite:
public void CriarPedido(string produto, int quantidade, double preco, string cliente)
Recomendado:
public void CriarPedido(Pedido pedido)
Tenha cuidado com efeitos colaterais
Se uma função alterar valores de outra classe sem ser a dela, isto vai gerar um efeito colateral.
Evite:
public void AplicarDesconto(Pedido pedido)
{
pedido.Valor -= 10;
}
Recomendado:
public class Pedido
{
public decimal Valor { get; private set; }
public void AplicarDesconto(decimal desconto)
{
Valor -= desconto;
}
}
Não tome decisões desnecessárias
Evite o uso de “flags” para tomar decisões dentro de métodos. Divida a lógica em métodos separados ou outras classes.
Evite:
public void Processar(bool isPagamentoCredito)
{
if (isPagamentoCredito)
ProcessarCartao();
else
ProcessarBoleto();
}
Recomendado:
public void ProcessarCartao() { … }
public void ProcessarBoleto() { … }
Regras de comentários
Comentários podem ser úteis, mas seu uso deve ser criterioso para evitar redundâncias e poluição no código. Não ser redundante, não fechar comentários, colocar intenção e esclarecimento, são parte das regras que guiam o uso adequado de comentários. Veja a seguir.
Um código bom é expressivo
Em teoria, quando é preciso comentar uma parte do código, é porque ele não está claro o suficiente.
// Função que inicia o sistema
public void IniciarSistema() { … }
Não seja redundante
Se um comentário não faz sentido para o contexto ou cenário, evite colocá-lo.
// Verifica se o usuário está logado
if (usuario.Logado) { … }
Não feche os comentários
Fechar comentários com barras ou outros caracteres é desnecessário e polui o código.
// Este é um comentário // <- Fechamento desnecessário
public void ProcessarDados() { … }
Evite códigos comentados
Códigos comentados podem ser confusos e dificultam a leitura, para deixar o código limpo e organizado, remova estes trechos. Caso você precise recuperar códigos antigos, utilize versionadores de código.
Intenção
Comentários podem ser úteis para explicar a intenção por trás de um método, classe ou variáveis.
// Retorna os usuários inativos para o processo de limpeza mensal
public IList<Usuario> ObterUsuariosInativos()
{
…
}
Esclarecimento
Outro bom uso de comentários é para fornecer esclarecimentos sobre decisões específicas no código.
public void FinalizarCompra()
{
// Verifica se o estoque foi atualizado antes de finalizar
if (estoque.Atualizado)
{
ConcluirPedido();
}
}
Consequências
Comentários podem servir como aviso sobre possíveis consequências de trechos de código, especialmente em operações críticas. Nesses casos, é recomendado o uso de comentários em XML.
/// <summary>
/// ATENÇÃO: Este método remove o usuário e todos os dados associados
/// </summary>
public void RemoverUsuario()
{
…
}
Estrutura do código
Entre as práticas recomendadas para estruturar um código estão: a separação de conceitos de forma vertical, agrupar funcionalidades similares, declarar funções de cima para baixo, manter poucas linhas, entre outros. Veja abaixo.
Separe conceitos verticalmente
Uma estrutura de pastas bem organizada ajuda a separar contextos e funcionalidades, evitando confusão e facilitando a navegação no projeto.
Exemplo:
/Controllers
/Services
/Repositories
/Models
Declare variáveis próximas de seu uso
Declare variáveis próximas de onde serão utilizadas, evitando criar variáveis no início de uma classe ou método.
Evite:
var total = 0;
public void CriarCliente() { … }
public void CriarPedido() { … }
public void AtualizarCliente() { … }
public void CalcularTotal()
{
total = 500; // <- Só é utilizada aqui
}
Exemplo recomendado:
public void CriarCliente() { … }
public void CriarPedido() { … }
public void AtualizarCliente() { … }
var total = 0;
public void CalcularTotal()
{
total = 500;
}
Agrupe funcionalidades semelhantes
Funções que pertencem ao mesmo grupo ou contexto devem ser mantidas próximas umas das outras.
Evite:
public void CriarCliente() { … }
public void VerificarEstoque() { … }
public void CriarPedido() { … }
public void AtualizarCliente() { … }
public void CalcularTotal() { … }
Exemplo recomendado:
public void CriarCliente() { … }
public void AtualizarCliente() { … }
public void VerificarEstoque() { … }
public void CriarPedido() { … }
public void CalcularTotal() { … }
Declare funções de cima para baixo
Organize as funções em uma ordem lógica, preferencialmente de cima para baixo, seguindo uma hierarquia de complexidade ou uso. Por exemplo:
public void CriarCliente(string nome) { … }
public void CriarCliente(string nome, int idade) { … }
public void CriarCliente(string nome, int idade, Endereco endereco) { … }
public void CriarCliente(string nome, int idade, Endereco endereco, bool ativo) { … }
Mantenha poucas e curtas linhas
Funções com muitas linhas ou linhas muito longas são difíceis de ler e manter. Priorize funções curtas e objetivas.
public void CriarCliente(string nome)
{
var cliente = new Cliente(nome);
_repositorio.Clientes.Add(cliente);
_repositorio.SaveChanges();
}
Não use alinhamento horizontal
Alinhar variáveis, constantes ou propriedades horizontalmente não é necessário e pode dificultar a leitura.
private Long tempoLimiteProcessamento;
protected Pedido pedido;
private Contexto contexto;
this.contexto = contexto;
entrada = stream.getInputStream()
tempoLimiteProcessamento = 1000;
Use os espaços em branco corretamente
Utilize espaços em branco para separar ou agrupar itens relacionados, melhorando a legibilidade do código.
Exemplo recomendado:
private void MeuMetodo(string parametro) {
variavel++;
int outraVariavel = algumArray.Length();
total += algumMetodo();
outraClasse.OutroMetodo(variavel, total);
outroMetodo(total);
}
Não quebre a identação
A identação é fundamental para a legibilidade do código. Um código sem identação adequada não deve ser enviado para o projeto.
Evite:
public class MinhaClasse{
var valor=12;
Console.WriteLine(valor);
}
Recomendado:
public class MinhaClasse
{
var valor = 12;
Console.WriteLine(valor);
}
Objetos e estruturas
As práticas recomendadas para o desenvolvimento de objetos e estruturas incluem o uso de estrutura de dados, esconder estruturas internas e não utilizar dados e objetos juntos. Confira.
Esconda estruturas internas
Manter a estrutura interna de um objeto oculta é uma boa prática para garantir o encapsulamento e evitar que dados sejam manipulados de forma inadequada. Isso pode ser feito tornando propriedades privadas e expondo apenas métodos para manipulação.
Evite:
public class Usuario
{
public string Senha;
}
Recomendado:
public class Usuario
{
private string _senha;
public void DefinirSenha(string senha)
{
_senha = Criptografar(senha);
}
}
Opte por estrutura de dados
A estrutura de dados diz respeito a como os dados são organizados, variando entre classes e structs.
Geralmente os structs são mais leves e adequados para dados simples, enquanto classes oferecem mais recursos como herança, polimorfismo, entre outros.
Exemplo com struct:
public struct PhoneNumber
{
public PhoneNumber(string number)
{
// Validação do número
}
public string Number { get; private set; }
}
public class Customer
{
public PhoneNumber ContactNumber { get; private set; }
}
Exemplo com classe:
public class PhoneNumber
{
public PhoneNumber(string number)
{
// Validação do número
}
public string Number { get; private set; }
}
public class InternationalPhoneNumber : PhoneNumber
{
public InternationalPhoneNumber(string number)
: base(number)
{
// Validação adicional para números internacionais
}
}
public class Customer
{
public PhoneNumber ContactNumber { get; private set; }
Instanciar poucas variáveis
Evite criar variáveis desnecessárias dentro de métodos ou objetos. Priorize o uso de propriedades para reduzir a complexidade.
Evite:
public void Processar()
{
int resultado;
resultado = Calcular();
}
Recomendado:
public void Processar()
{
int resultado = Calcular();
}
Classe base não deve saber sobre suas derivadas
Uma classe base não deve ter conhecimento sobre suas classes derivadas. Isso viola o princípio de encapsulamento e pode levar a um acoplamento desnecessário.
Exemplo:
// N/A (Não há cenário válido para uma classe base saber sobre suas derivadas)
Mais métodos, menos tomadas de decisão
Prefira criar mais métodos do que aumentar a quantidade de tomadas de decisão dentro do código. Por exemplo:
Evite:
public class Order
{
public void ProcessPayment(PaymentMethod method)
{
if(method == PaymentMethod.CreditCard)
// Processar cartão de crédito
else
// Processar boleto
}
}
Exemplo recomendado:
public class Order
{
public void ProcessCreditCardPayment()
{
// Processar cartão de crédito
}
public void ProcessBankSlipPayment()
{
// Processar boleto
}
}
Evite métodos estáticos
Métodos e classes estáticos podem ser difíceis de gerenciar e testar, além de serem compartilhados entre a aplicação integralmente. Prefira instâncias de classes para manter o controle sobre o estado.
Evite:
public static class Logger
{
public static void Log(string message)
{
// Registrar mensagem
}
}
Exemplo recomendado:
public class Logger
{
public void Log(string message)
{
// Registrar mensagem
}
}
Dicas para testes
Confira agora algumas dicas práticas para escrever testes eficientes e de fácil manutenção.
Um assert por teste
É recomendável utilizar apenas um assert por teste. Ter mais de um assert pode causar confusão e dificultar a escrita do teste.
Evite:
[TestMethod]
public void ShouldReturnTrue
{
Assert.AreEqual(true);
Assert.AreEqual(1);
}
Exemplo recomendado:
[TestMethod]
public void ShouldReturnTrue
{
Assert.AreEqual(true);
}
Legível
Os testes devem ser tratados como parte fundamental do código, e não como algo secundário. Eles precisam ser organizados e bem escritos, assim como o restante do software.
Rápido
Um dos principais objetivos de um teste é cobrir uma pequena parte do código. No entanto, é comum estender essa ideia para a maior parte do código possível, resultando em uma grande quantidade de testes de unidade.
Esses testes são executados antes da publicação das aplicações, garantindo que nada com bugs seja enviado para produção. Em cenários críticos, o tempo de deploy é crucial, e testes lentos podem impactar negativamente esse processo.
Independentes
Os testes não devem depender de entidades externas ou de outros testes. O uso de Injeção de Dependência (DI) e Princípio da Inversão de Dependência (DIP) é fundamental para garantir essa autonomia.
Repetível
Devemos ser capazes de repetir o mesmo teste com parâmetros diferentes, garantindo que ele funcione em diversos cenários.
Dicas sobre Code Smells
Quando o Clean Code não é bem aplicado, alguns indicativos aparecem sugerindo problemas na qualidade do código. Conhecido como Code Smells, estes indícios são:
- Rigidez: Quando o software é difícil de modificar, qualquer alteração, mesmo que pequena, desencadeia uma série de outras mudanças;
- Fragilidade: Mesmo uma pequena mudança quebra o software em diversos locais;
- Imobilidade: Dificuldade em reutilizar partes do código em outros projetos por estar acoplado;
- Complexidade desnecessária: Uso excessivo de padrões e arquiteturas que tornam o código mais burocrático do que funcional.
- Repetição desnecessária: O mesmo código é repetido em vários lugares, indicando falta de modularização;
- Opacidade: O código é difícil de entender, prejudicando a manutenção e a clareza.
Simplifique sua gestão de documentos fiscais com a Focus NFe
Somos um ecossistema de soluções para a emissão e gestão de documentos fiscais. Nossos recursos permitem que empresas dos mais diversos portes e segmentos ganhem mais tempo para focar no que importa.
Sua empresa possui desenvolvedores, sistema interno e quer otimizar a emissão de notas? Teste gratuitamente e conheça nosso conjunto de APIs para emissão de documentos fiscais!
Ou converse já com a nossa equipe e saiba como podemos ajudar o seu negócio!