Por que a complexidade ciclomática é tão importante para um único método?

10

Estou usando o SonarLint for Eclipse desde recentemente, e isso me ajudou muito. No entanto, levantou-me uma questão sobre a complexidade ciclomática.

O SonarLint considera aceitável um CC de 10 e há alguns casos em que estou além dele, cerca de 5 ou 6 unidades. Essas partes estão relacionadas aos mapeadores em que os valores se baseiam em diferentes variáveis, por exemplo:

  • O campo A se baseia na String sA;
  • O campo B depende da string sB;
  • O campo C conta com String sC;
  • etc ...

Não tenho outra escolha que colocar um ifpara cada campo. Felizmente, essa não é minha escolha, mas um sistema já existente e complexo que não posso mudar sozinho.


O núcleo da minha pergunta é: por que é tão importante não ter um CC muito alto em um único método ? Se você mover algumas de suas condições em um ou mais sub-métodos para reduzir a complexidade, isso não reduzirá o custo de sua função geral, está apenas movendo o problema para outro lugar, eu acho?

(Desculpe por pequenos erros, se houver).


EDITAR

Minha pergunta não se refere à complexidade ciclomática global, mas apenas à complexidade de um método e à divisão de métodos (eu tenho um tempo difícil para explicar o que exatamente quero dizer, desculpe). Eu estou perguntando por que é permitido dividir suas condições em métodos menores se ainda pertencer a um 'super método', que apenas executará todos os sub-métodos, adicionando complexidade ao algoritmo.

O segundo link, no entanto ( sobre o anti-padrão ) é de grande ajuda.

Yassine Badache
fonte
^^^ "seta cabeça" questão é provavelmente a melhor duplicar no sentido de que ele explica como para melhorar o seu código, mas eu escolhi primeiro por causa da explicação detalhada respondendo à parte da sua pergunta sobre a complexidade ciclomática
mosquito
Dividir um método em partes menores não reduz a quantidade total de código executado, mas torna claras as tarefas individuais que estão acontecendo; cada um pode ser entendido muito mais facilmente individualmente do que quando estão todos presos em um todo maior. No mínimo, ele remove muitas variáveis ​​intermediárias de uso único do escopo maior.
Doval
1
Para o seu caso descrito em particular, eu faria algo no seu método principal como "A = extractAFrom (sA);" para cada campo. Você provavelmente pode criar nomes melhores, pois conhece os campos reais e seus usos.
Tin Man

Respostas:

32

A principal coisa aqui: "capacidade cerebral".

Veja bem, uma das principais funções do código é ... ser lida . E o código pode ser fácil de ler e entender; ou difícil.

E ter um CC alto simplesmente implica muitos "níveis" em um método. E isso implica: você, como leitor humano, terá dificuldade em entender esse método.

Quando você lê o código-fonte, seu cérebro automaticamente tenta colocar as coisas em perspectiva: em outras palavras - ele tenta criar alguma forma de "contexto".

E quando você tem um método pequeno (com um bom nome) que consiste apenas em algumas linhas e CC muito baixo; então seu cérebro pode facilmente aceitar esse "bloqueio". Você lê, você entende; FEITO.

Por outro lado, se seu código tiver CC alto, seu cérebro gastará muitos "ciclos" mais para deduzir o que está acontecendo.

Outra maneira de dizer isso: você sempre deve preferir uma rede complexa de coisas simples a uma rede simples de coisas complexas. Porque seu cérebro é melhor para entender pequenas coisas.

GhostCat saúda Monica C.
fonte
9
Então, basicamente, não se trata de questões técnicas , mas de humano ? Isso parece realmente inteligente, não sei como não pensava nisso antes. Obrigado !
Yassine Badache
4
Nesse caso, recomendo sinceramente que você adquira o "Código Limpo" de Robert Martin (você pode encontrar o pdf gratuitamente nas redes). Veja bem, criar código legível é uma das virtudes mais importantes, mas muitas vezes ignoradas, de um bom programador.
GhostCat saúda Monica C. 02/12/16
O @YassineBadache CC também torna difícil testar todos os cantos e recantos (cobertura total).
Tulains Córdova 02/12/19
Este também era o cerne do trabalho de Dijkstra .
Seth Battin
Na minha experiência, os bugs estão quase sempre nos métodos com um CC alto e quando os bugs estão no método de baixo CC são geralmente totalmente óbvios, não há como eles se esconderem após a primeira execução do código. Além disso, acho que quase nunca preciso modificar um método de baixa CC - mais carga retirada do cérebro.
Loren Pechtel 02/12/19
6

CC, como todas as outras regras práticas para odores de código, é uma heurística . Não é um critério à prova de falhas que diz uma verdade absoluta. Se assim fosse, a coisa mais razoável a fazer seria simplesmente tornar esses métodos ilegais no idioma e forçar as pessoas a alcançar seus fins de outra maneira.

Mas não é assim que os indicadores funcionam. Na maioria das vezes, sua função é alertar as pessoas sobre coisas que elas não estavam cientes. No seu caso, você está ciente de que a lógica é complicada e as soluções alternativas a tornam ainda mais complicada. Portanto, não faz sentido tentar satisfazer a regra básica primitiva, quando seu principal objetivo é emitir avisos para pessoas que não estão cientes de que há um problema.

Kilian Foth
fonte
2
Se "FieldA depende de String sA", como OP afirma, não estou convencido de que mover isso para um CalculateFieldA (String sA) crie um código mais complicado.
Taemyr 02/12/19
2

Resumindo: trata-se de legibilidade e, portanto, de manutenção do seu código.

Se você tem um método longo e complexo com muitos (aninhados) if, fica difícil dizer o que realmente faz. Se você extrair alguns métodos particulares e nomeá-los de maneira significativa, é muito mais fácil.

André Stannek
fonte
Embora essa seja uma boa resposta, é um pouco curta. Seria bom se você pudesse fornecer um exemplo do que está dizendo e ser mais específico em relação à pergunta feita pelo OP: "por que é tão importante não ter um CC muito alto em um único método?".
Machado
2

A complexidade ciclomática de um método está relacionada ao número de casos de teste necessários para um método. Especificamente, uma complexidade ciclomática de 10 significa que 10 é o limite superior para que os casos de teste tenham cobertura total de ramificação para seu método. Também está relacionado ao número de caminhos que devem ser testados, menos os caminhos impossíveis.

Além disso, concordo com as outras respostas para outras considerações - capacidade mental de um desenvolvedor ou um indicador de possíveis problemas ou refatoração ou uma medida de legibilidade e manutenção do código .

Thomas Owens
fonte
0

O CC é apenas uma heurística, e o quão ruim é uma pontuação específica depende de muitas coisas.

Dito isto, você sempre deve ver um CC alto como algo que destaca o código que pode / deve ser refatorado. Você diz que mover a ifinstrução para outro método está ocultando o problema - mas existe um padrão que você pode abstrair em vez de copiar e colar n vezes? Se for uma if-elsecadeia longa , você pode transformá-la em uma declaração de troca, ou talvez usar polimorfismo, ou algo mais? Se houver um aninhamento profundo, algumas de suas cláusulas condicionais podem ser unidas ou indica responsabilidades separadas que devem ser divididas em diferentes classes?

GoatInTheMachine
fonte
0

Eu vejo a complexidade ciclomática como um aviso. Se você pode ler o código e não é muito complexo para entender, então eu não me preocuparia muito com isso, provavelmente há coisas mais importantes com as quais se preocupar, sempre há.

Uma maneira de reduzir o tipo de CC que você menciona é usar o polimorfismo, pois você marcou sua pergunta com a tag Java. Portanto, em vez de os caminhos de código serem digitados em seqüência, você pode usar classes bem nomeadas. Isso pode ajudar, mas às vezes é um exagero e pode tornar seu código ainda mais difícil de entender.

No entanto, pode ser um sinal de código difícil de manter. Ao ler o método, é fácil ver qual caminho de código você seguirá para cada caso? Você poderia ignorar esse método se não conhecesse bem a base de código e estivesse procurando algo relacionado, mas mais adiante no código? Eu sei que algumas pessoas defendem a divisão de métodos em muitos métodos de 1/2 linha com nomes descritivos, mas às vezes acho que é ainda mais difícil de ler do que o código que ele deveria substituir.

Por fim, a manutenção é um problema difícil e cabe a você decidir qual você acha que será mais fácil de ler. O fato de você estar pensando sobre isso significa que você está no caminho certo. Lembre-se de que o mantenedor que precisa tentar decifrar esse código dentro de alguns anos pode ser você. Portanto, torne o mais fácil possível para eles.

Encaitar
fonte
0

Tudo se resume a quanto tempo é gasto olhando (ciclos cerebrais) para o código e compreendendo o que o código está fazendo.

  • Considere um método de 10 linhas - provavelmente leve alguns minutos para compreender o que está fazendo.
  • Considere um método de 100 linhas - provavelmente leva uma hora ou mais para compreender o que está fazendo.
  • Considere um método de 1000 linhas - provavelmente leva um dia ou mais para compreender o que está fazendo.

Métodos maiores também são mais difíceis de testar e mais previsíveis que tipo de comportamento pode ocorrer.

A complexidade ciclomática é uma medida. Nesse caso, valores mais altos são indicadores de problemas em potencial. O código complexo leva mais tempo para compreender o entendimento e provavelmente não é testado tão completamente quanto os métodos menos complexos. Portanto, é importante observar quais áreas do código são complexas com essa medida para fins de refatoração e manutenção.

Outra coisa a considerar é a atualização de código complexo. Ao fazer a análise, um desenvolvedor pode relatar que alterar o código é mais ou menos arriscado, analisando sua complexidade.

Portanto, há muito valor para medir a complexidade que pode ser alavancada e usada para fins de tomada de decisão.

Jon Raynor
fonte
1
Desculpe resposta Ghost Cat muito semelhante, deveria ter atualizado a página.
Jon Raynor