Medindo a complexidade do seu código

Postado em 01. mar, 2012 por em Arquitetura, Inovação

Sabemos que um dos grandes vilões na hora de mantermos um sistema legado são os trechos de código difíceis ou complicados de entender. Todo programador já se deparou com (ou mesmo escreveu!) códigos com péssimos nomes de variáveis ou muitas linhas de código em um único método, sem contar o excesso de responsabilidade. Mas, sem dúvida, um dos maiores responsáveis pela dificuldade em entender o que um código faz é o simples if.

Com essa simples instrução, o desenvolvedor faz com que o mesmo método possa responder de duas, três, quatro, N, …, maneiras diferentes, de acordo com certas condições! E pior, o número de caminhos diferentes pode crescer muito rápido!. Considere o trecho de código:

public int contaMaluca(int numero) {
  int resultado = 10;
  if (numero > 10) resultado += numero;
  return resultado;
}

Quantos caminhos diferentes esse método possui? Dois! O primeiro acontece se o if falhar; o segundo acontece se o if for verdadeiro. Agora, olhe o código abaixo, que contém apenas um if a mais:

public int contaMaluca(int numero) {
  int resultado = 10;
  if (numero > 10) resultado += numero;
  if (numero < 30) resultado *= 2;
  return resultado;
}

Repare que temos agora quatro caminhos diferentes! Veja que adicionamos apenas um if a mais, mas a quantidade de caminhos em relação ao código anterior, simplesmente dobrou! Se adicionarmos um if a mais no código acima, parecido com os dois já existentes, veja que o número de caminhos pula pra oito! A figura abaixo exemplifica os possíveis caminhos:

Devemos, quase sempre, manter esse número de caminhos o menor possível. Afinal, quanto menor o número de caminhos, mais fácil é entender o código. Mas o if não é o único vilão! Outras instruções podem aumentar esse “número de complexidade”. Sempre que vemos um for, while, case (do switch), catch, &&, || ou ?, podemos incrementar esse número.

Essa conta é bastante conhecida, é a Complexidade Ciclomática. A ideia é basicamente contar o número de caminhos diferentes que um método pode ter. Entretanto, como calcular o número de caminhos não é tão fácil, um algoritmo muito conhecido é a Complexidade de McCabe, que basicamente conta o número de vezes que um for, while, e todas as instruções mencionadas acima aparecem. Além disso, todo método tem seu contador já inicializado com 1 (afinal, todo método tem pelo menos um caminho). No nosso segundo código, por exemplo, a complexidade de McCabe seria 3.

Não há um número ideal para a complexidade de um método. Mas fato é que uma complexidade ciclomática de 20 ou 30 é demais e esse método deve ser reescrito. Mesmo quebrando-o em dois, é interessante observar a complexidade dos métodos de uma classe em conjunto. Existem diversas ferramentas que calculam esse número para você. Uma das mais populares é o Eclipse Metrics, que é um plugin do Eclipse. Você pode baixá-lo aqui: http://metrics.sourceforge.net/. Ele calcula a complexidade ciclomática de todos os métodos de um projeto!

Faça o teste: instale o plugin e procure pelos métodos cuja complexidade ciclomática seja alta! Compartilhe conosco o maior número que encontrou! E, obviamente, refatore seu código para diminuir esse número.

Mauricio Aniche

Mais sobre o autor

Tags: , , , ,

36 Respostas para “Medindo a complexidade do seu código”

  1. Geraldo Ferraz

    01. mar, 2012

    Um ponto importante pra ressaltar na frase “Afinal, quanto menor o número de caminhos, mais fácil é entender o código” é que fica bem mais fácil de testar.

  2. Mauricio Aniche

    01. mar, 2012

    Oi Geraldo,

    Com certeza! Imagine um método que tenha complexidade ciclomática 20. Para garantir que esse método realmente funcione, você precisa de, *no mínimo*, 20 testes!!

    É trabalho demais!

    Nesses casos, devemos refatorar, criar métodos menores, classes menores, e testar mais fácil!

    Um abraço!

  3. Daniel Wildt

    01. mar, 2012

    Legal o post. Eu curto muito complexidade ciclomática. Ela é simples de se medir, e pode ser automatizada como uma tarefa no build usando PMD por exemplo.

    O ponto de tudo isto é poder apoiar o desenvolvimento e garantir melhor cobertura de teste e principalmente apoiar refactorings.

    Ainda no PMD, se pode configurar ele de modo bem malvado, fazendo que o código dentro do Eclipse por exemplo mostre erro de compilação caso a complexidade esteja fora dos padrões que a equipe configurou.

  4. Carlos

    01. mar, 2012

    Pra mim isso é igual a falar que o esquema tático x é melhor que o y.

    O melhor é aquele que te da a vitória!

    Mais concordo em parte com a opinião de vocês!

    Mais acho que que a complexidade do código deve variar de acordo com a necessidade de cada um, isso não quer dizer que o código é de difícil entendimento.

    Num mundo real é necessário atender as necessidades do cliente e essas necessidades muitas vezes são complexas.

  5. depp

    01. mar, 2012

    cara, as vezes é bem dificil vc criar um método menor… seria interessante um post tipo estudo de caso mostrando um exemplo da vida real.

  6. Mauricio Aniche

    01. mar, 2012

    Oi Carlos,

    Concordo. Devemos sempre ir atrás do nosso objetivo concreto, que é entregar software. Mas não podemos esquecer que o produto/serviço que vendemos possui uma característica bem interessante e difícil de lidar: as coisas mudam o tempo todo, e nosso produto vive pra sempre!

    Quantas vezes já participamos de projetos cujo trabalho era reescrever um sistema que já existia antes? Veja que isso não faz sentido nenhum do ponto de vista econômico. É dinheiro jogado fora!

    É muito fácil escrever código ruim. E por isso que gosto de métricas. Elas podem não refletir a realidade 100% do tempo, mas são um bom guia. Sempre que encontro um número muito fora da curva, eu estranho!

    Concordo que o mundo real é complexo. Mas é por isso que estudamos e trabalhamos tanto para achar boas práticas, certo? Não estou argumentando que métodos não podem ter ifs. Mas um método que tem 30 ifs pode, com certeza, ser reescrito! A grande mágica da Orientação a Objetos é justamente nos permitir criar classes coesas e pequenas e depois juntá-las de maneira fácil e super flexível !

    Um abraço!

  7. Mauricio Aniche

    01. mar, 2012

    Oi Daniel,

    Ótimo! Dá pra avisar o desenvolvedor na hora que ele bagunçar a métrica! Quanto mais feedback (e mais rápido) melhor!

    Em breve, minha ferramenta de cálculo de métricas sai também! E essa vai ser legal! Aguarde e verá! :P

    Um abraço!

  8. Mauricio Aniche

    01. mar, 2012

    Oi Depp,

    É aí que a mágica da orientação a objetos entra! Falando bem tecnicamente, o uso de polimorfismo nos possibilita a criação de classes menores que trabalham juntas!

    Veja os padrões de projeto, por exemplo! Todos eles, no fim, são isso! Eles separam os compartamentos em classes pequenas e coesas, e no fim, os juntam de alguma forma! Já viu nosso curso de Design Patterns online? Lá damos vários exemplos desses!

    Mas aceito sim o desafio, Depp! Se quiser, me mande um código, nós refatoramos, e blogamos sobre isso! O que acha?

    Um abraço!

  9. Igor Steinmacher

    01. mar, 2012

    Muito bom Aniche! Parabéns!
    Li o artigo sem ver que era seu e estou utilizando com meus alunos de qualidade de software.

    Aguardo ansiosamente sua ferramenta

  10. Douglas Rossignolli (@xdougx)

    01. mar, 2012

    Realmente você ter todos esses caminhos a percorrer como mostra na figura é uma grande dor de cabeça para fazer testes, mas eu acredito que é como ensinado quando você esta la na faculdade ou como escutamos é “Dividir para conquistar” :D

  11. Carlos

    01. mar, 2012

    Oi Mauricio não quis dizer que o objetivo é entregar o software..

    Pelo contrário acho que um programador novato nem consegue desenvolver um código complexo, ele irá desenvolver um código com falhas e erros de lógica, não é culpa dele é falte de experiência.

    Quis dizer é que num mundo real esse tipo de exemplo não é visto e os métodos são muito mais complexos e tem várias condições e dependências.

    Oque quis fizer é que só porque o código é complexo não quer dizer que ele foi mal feito, ou terá baixo desempenho em teste de software.

    Outra coisa o programador que só pensa em entregar o projeto, não é programador, vai fazer outra coisa..

  12. Diego

    01. mar, 2012

    Eu era uma das piores pessoas para se escrever códigos, sempre pensava na maior quantidade de erros possíveis.

    E nem sempre essa é a solução, um código com uma lógica mais inteligente te elimina boa parte dos problemas de código.

    O resto é da cabeça do programador.

  13. depp

    01. mar, 2012

    Qual vc considera a metrica ideal? fiz um teste aqui em um metodo que eu achava que tava com bastante if e ele mediu 7 de complexidade ciclomatica.

  14. Helder da Rocha

    01. mar, 2012

    Uma ferramenta que sempre procuro usar para rapidamente coletar métricas de código (principalmente quando me deparo com aplicações pela primeira vez) é o Sonar (www.sonarsource.org). Ele coleta bastante informação sobre o código, inclusive medidas de complexidade, e organiza os dados coletados de PMD, CheckStyle, FindBugs. Usando com o Hudson, ele permite o acompanhamento da qualidade do projeto ao longo de sua vida. Se for um projeto Maven, é muito fácil usar porque há um plug-in nativo. Se não for, é possível ainda obter métricas simplesmente criando um POM para o projeto indicando onde estão as fontes.

    Métricas são muito importantes pois fornecem feedback detalhado sobre o código que podem guiar refatoramentos e chamar a atenção sobre pontos que podem se tornar críticos. Nem sempre as otimizações devem ser feitas. Tudo depende de quão crítico é o código em questão, do custo/complexidade da otimização e de outros fatores. Mas qualquer solução que ajude a deixar o código mais fácil de entender é sempre boa.

  15. Mauricio Aniche

    01. mar, 2012

    Oi Igor,

    Obrigado!!

  16. Pablo

    01. mar, 2012

    Informação excepcional, utilidade inestimável para quem quer melhorar seu código.

  17. Mauricio Aniche

    01. mar, 2012

    Oi Carlos,

    Concordo! Mas acho que esse é exatamente nosso desafio. Como escrever código de maneira simples?

    Com certeza você terá trechos de código no seu sistema mais complexos que outros. Mas prefiro acreditar que eles serão a exceção, e não a regra.

    Um abraço!

  18. Mauricio Aniche

    01. mar, 2012

    Oi Diego e Douglas,

    Obrigado!

  19. Mauricio Aniche

    01. mar, 2012

    Oi Depp,

    7 quer dizer que esse seu método tem no mínimo 7 caminhos diferentes, certo? Em minha opinião, CC=7 não é um problema, mas já é algo para monitorar, prestar atenção. Eu quebraria esse método grande em pequenos métodos privados, ou até mesmo em classes separadas.

    O prof. Harald Gall, de uma universidade suiça, fez um trabalho uma vez sobre métricas que ele considera de referência. Na opinião dele, quando sua complexidade ciclomática, por linha de código, for maior que 0.24, ele considerado alto. Ou seja, pega a complexidade ciclomática, divida pelo número de linhas, e veja o valor.

    Aqui a tabela dele:
    Baixo = 0.16
    Na Média: 0.20
    Alta: 0.24

    Um abraço!

  20. Mauricio Aniche

    01. mar, 2012

    Oi Helder,

    Concordo. Ferramentas como Hudson, Sonar, e etc, tiram nosso trabalho de calcular a métrica! Recomendadíssimo!

    Um abraço!

  21. Mauricio Aniche

    01. mar, 2012

    Oi Pablo!

    Obrigado!

  22. Elizeu Santos

    01. mar, 2012

    perfeito o artigo, recomendo!

  23. Guilherme

    01. mar, 2012

    Olá Maurício,

    Qual foi a ferramenta gráfica que você utilizou para fazer os caminhos do código ?

    abs.

  24. Mauricio Aniche

    01. mar, 2012

    Oi Guilherme,

    Xiii, você vai se decepcionar! Eu usei o Google Docs para desenhar. Fiz na mão mesmo! ;)

    Não sei se existe alguma ferramenta que desenha o grafo de caminhos possíveis. O que pode ajudar é usar ferramentas como Jude ou ArgoUML e, a partir do código-fonte Java, criar diagramas de sequência. Isso deve dar uma noção da complexidade do método!

    Um abraço!

  25. Guilherme

    01. mar, 2012

    OK, tudo bem :) hehe obrigado!

    Relacionado ao post:
    Uma recomendação para os rubistas de plantão: http://ruby.sadi.st/Flog.html

    Esta ferramenta analisa a complexidade usando a métrica abc. bom, não deixa de ser útil e relacionado ao tema :)

    abs.

  26. Márcio Silva

    01. mar, 2012

    Bom artigo e tb bons comentários.
    1. Meça mesmo – ninguém é obrigado a levar em conta os achismos de ninguém.
    2. Comece simples e se esforce para manter a simplicidade pois a complexidade tende a aumentar sempre.
    3. Código complexo = mágica. Lugar de fazer mágica não é em componente de negócio. Em componentes de frameworks é mais aceitável. Bom design, refactoring são algumas armas.

  27. Luis Eugênio

    01. mar, 2012

    Muito bom Aniche! Bem interessante o post. Um link com mais informações em português (baseado, muito provavelmente no link que você passou do linux journal) e também um exemplo em ruby http://goo.gl/RFRzf.

  28. Mauricio Aniche

    01. mar, 2012

    Oi Guilherme,

    O Flog é bem legal também! Eu só não sei bem como ele incrementa o contador, afinal, em Ruby você tem muito mais coisas na linguagem! :)

    Oi Márcio,

    Concordo em absoluto!!

    Oi Luis Eugênio,

    Ótimo link! Galera, não deixem de ler!

    Abraços!

  29. Gilmar P.S.L.

    06. mar, 2012

    Maurício, parabéns pelo artigo, muito bom.

    Guilherme, já usei essa ferramenta para estudo de caso para métricas.

    http://www.codeswat.com/cswat/index.php?option=com_content&task=view&id=15&Itemid=30

    Ela é gera flowcharts do smétodos:

    Exemplo
    http://www.codeswat.com/cswat/s4j/target2.html

    Porém, a não ser que seja um método de outro mundo, tipo: 5 mil linhas de código.., vc não vai usar muito.

    Prefiro usar sempre as métricas pelo sonar como parte da integração contínua. E avaliá-las periodicamente durante a semana.

  30. Raphael

    13. mar, 2012

    Então, foi daí que vc tirou sua tese à respeito de número de asserts nos testes?

  31. Mauricio Aniche

    13. mar, 2012

    Oi Rapha,

    Não! Mas usei complexidade ciclomática como uma das métricas para validar a minha hipótese!

    Te mando o artigo se quiser! Devo blogar sobre isso em breve também!

    Abraços!

  32. Alexandre Aquiles

    14. mar, 2012

    Excelente artigo, Aniche!

    E vale a dica do Sonar, dada pelo Helder. A ferramenta é excelente.

    ____

    No meu TCC de uma Pós que fiz em 2011, eu tentei estudar a relação entre métricas e refatorações.

    Aprofundei bem menos do que queria mas foi uma pesquisa interessante.

    A idéia inicial era ajudar iniciantes a identificar oportunidades de refatoração no código e depender um pouco menos de um olhar de especialista em design.

    Mas o que acabei percebendo é que as métricas não tem uma correlação direta com as refatorações. O olhar tático de um especialista em design é muito importante.

    Uma referência interessante do assunto é o trabalho de Carneiro:

    CARNEIRO, G. F. Usando Medição de Código Fonte Para Refactoring. 2003. 129 f.
    Dissertação (Mestrado em Redes de Computadores) – Universidade Salvador,
    Salvador, 2003.

    Quem quiser olhar meu TCC, coloquei em:
    http://alexandreaquiles.wordpress.com/2012/03/14/identificando-oportunidades-de-refatoracao-atraves-de-metricas/

  33. Mauricio Aniche

    14. mar, 2012

    Vou ler sim, Alexandre!!

  34. Phenders

    23. mar, 2012

    Bom Podemos dar a seguinte solução

    public int contaMaluca(int numero) {

    int resultado = 10;

    if (numero > 10 && > 30)
    resultado += numero;
    else
    resultado *= 2;

    return resultado;
    }

  35. Mauricio Aniche

    23. mar, 2012

    Oi Phenders,

    A complexidade ciclomática seria a mesma! Afinal o “&&” é contabilizado pelo algoritmo!

    Faz sentido?

    Um abraço!

Trackbacks/Pingbacks

  1. Identificando Oportunidades de Refatoração através de Métricas « Mente de Iniciante - março 14, 2012

    [...] lendo o excelente artigo Medindo a complexidade do seu código no blog da Caelum e resolvi tirar da gaveta o artigo que fiz para a conclusão da Pós que fiz em [...]

Deixar uma Resposta