Complexidade ciclomática ao chamar o mesmo método várias vezes


12

Graças a uma pergunta na Code Review , entrei em um pequeno desacordo (que é essencialmente uma oportunidade de aprender algo) sobre o que exatamente é a Complexidade Ciclomática para o código abaixo.

public static void main(String[] args) {
    try {
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
    }
    catch (NullPointerException e) {
    }
}

private static Random random = new Random();

public static void thro() throws NullPointerException {
    if (random.nextBoolean())
        throw new NullPointerException();
    System.out.println("No crash this time");
}

Ao escrever esse código no Eclipse e usar o plug - in de métricas do Eclipse , ele diz que a Complexidade Ciclomática McCabe para o método principal é 2 e para o thrométodo que diz 2.

No entanto, alguém me diz que a complexidade de chamar throvárias vezes é number of calls * method complexitye, portanto, afirma que a complexidade do método principal é 7 * 2 = 14.

Estamos medindo coisas diferentes? Nós dois podemos estar corretos? Ou qual é a complexidade ciclomática real aqui?


5
O CC da função é dois, pois existem apenas dois caminhos. O CC do programa é maior. Esta é uma facada completa no escuro, mas presumo que o software de análise de código leve cada função como uma caixa preta separada devido à inviabilidade de calcular o CC de um aplicativo complexo inteiro de uma só vez.
Phoshi

@Phoshi Se você escrever isso como uma resposta e (se possível) fornecer links que mostrem que há uma separação dos dois, eu aceitaria com prazer essa resposta.
Simon Forsberg

Se você contar todos os caminhos causados por possíveis exceções nas medições CC, deus ajuda o cara que fez a pergunta sobre refatoração algum código trivial para obter o número sob 10.
mattnz

Respostas:


9

Quando entendi isso corretamente, a complexidade ciclomática de mainé 8 - esse é o número de caminhos linearmente independentes através do código. Você recebe uma exceção em uma das sete linhas, ou nenhuma, mas nunca mais que uma. Cada um desses possíveis "pontos de exceção" corresponde exatamente a um caminho diferente através do código.

Acho que quando McCabe inventou essa métrica, ele não tinha linguagens de programação com o tratamento de exceções em mente.


Mas isso realmente importa qual das linhas que lança a exceção?
Simon Forsberg

5
@ SimonAndréForsberg: sim, é verdade. Pense em "thro" tendo um efeito colateral onde ele incrementa um contador global quando é chamado (que não alteraria os caminhos possíveis pelo código). Os resultados possíveis de que são, em seguida, contador de 0 a 7, de modo que este mostra que o CC é pelo menos 8.
Doc Brown

Você diria que o plug-in de métricas que estou usando está relatando um valor incorreto para o mainmétodo?
Simon Forsberg

@ SimonAndréForsberg: bem, eu não conheço seu plug-in de métricas, mas obviamente 2 não é 8.
Doc Brown

Há um link para o plug-in de métricas na minha pergunta ....
Simon Forsberg 29/11

6

Sendo 'o outro cara', responderei aqui e seja preciso sobre o que digo (o que não era particularmente preciso em outros formulários).

Usando o exemplo de código acima, calculo a complexidade ciclomática como 8 e tenho comentários no código para mostrar como calculo isso. Para descrever os caminhos, considerarei um loop bem-sucedido através de todas as thro()chamadas como o caminho do código 'principal' (ou 'CP = 1'):

public static void main(String[] args) {
  try {
             // This is the 'main' Code Path: CP = 1
    thro();  // this has a branch, can succeed CP=1 or throw CP=2
    thro();  // this has a branch, can succeed CP=1 or throw CP=3
    thro();  // this has a branch, can succeed CP=1 or throw CP=4
    thro();  // this has a branch, can succeed CP=1 or throw CP=5
    thro();  // this has a branch, can succeed CP=1 or throw CP=6
    thro();  // this has a branch, can succeed CP=1 or throw CP=7
    thro();  // this has a branch, can succeed CP=1 or throw CP=8
  }
  catch (NullPointerException e) {
  }
}

Portanto, conto 8 caminhos de código neste método principal, o que, para mim, é uma Complexidade Ciclomática de 8.

Em termos de Java, cada mecanismo para sair de uma função conta com sua complexidade; portanto, um método que possui um estado de sucesso e lança, por exemplo, possivelmente até três exceções, possui quatro caminhos de saída documentados.

A complexidade de um método que chama essa função é:

CC(method) = 1 + sum (methodCallComplexity - 1)

Penso que outras coisas a considerar é que, na minha opinião, a catchcláusula não contribui para a complexidade do método, catché simplesmente o alvo de um throwsramo e, portanto, um bloco de captura que é o alvo de várias throwcontagens 1 vez para cada um throw, e não apenas uma vez para tudo.


Você está contando as possíveis ramificações de OutOfMemoryExceptions também? Quero dizer, pedanticamente, eles podem causar ramificações de código, mas ninguém as conta, pois elas diluem a utilidade da métrica.
Telastyn

Não, não estou ... e você está certo, mas, no contexto deste argumento, conto apenas as exceções que o método é declarado como lançando. Além disso, se um método declara três exceções, mas o código do callinch faz um, catch (Throwable t) {...então eu acho que não importa quantas exceções ele declara lançar.
Rolfl
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.