Os métodos iterativos reduzem a complexidade ciclomática e melhoram a capacidade de suporte?


11

Os métodos iterativos, como os comumente encontrados em linguagens modernas como C #, JavaScript e (esperançosamente) no Java 8, reduzem o impacto da complexidade ciclomática na compreensibilidade e capacidade de suporte do código?

Por exemplo, em C #, podemos ter o seguinte código:

List<String> filteredList = new List<String>();

foreach (String s in originalList){
   if (matches(s)){
      filteredList.add(s);
   }
}

Isso tem uma complexidade ciclomática simples de 2.

Poderíamos facilmente reescrever isso como:

List<String> filteredList = originalList.where(s => matches(s));

Que possui uma complexidade ciclomática simples de 0.

Isso realmente resulta em código mais suportável? Existe alguma pesquisa real sobre esse tópico?


2
Seu título pergunta sobre redução da complexidade ciclomática, mas seu texto assume redução de CC e pergunta sobre manutenção. Isso tem o potencial de ser uma pergunta valiosa. Você pode editar o título para ser mais preciso?
precisa

@KilianFoth Isso é melhor?
243 Ross C.

Eu associaria métodos iterativos a metodologias iterativas de desenvolvimento. Eu acho que um termo melhor seria funções de ordem superior. (E, à parte, os métodos não têm tipo de retorno / nulo, enquanto as funções retornam algo).
Johannes Rudolph

@JohannesRudolph "os métodos não têm tipo de retorno / nulo, enquanto as funções retornam algo" - em qual idioma isso é verdade? Eu nunca ouvi isso; de fato, a única distinção real que já ouvi falar foi sobre métodos associados a uma classe, funções não.
Threed

Respostas:


14

Meu palpite é que você acabou de dividir / mover a complexidade. Diminuiu porque você não conta a implementação .where()no seu CC.

O CC geral realmente não foi movido, o CC do seu próprio código diminuiu, apenas porque agora ele foi movido para o código da estrutura.

Eu diria que é mais sustentável. Quando é um recurso do idioma, use-o. Não é um "ohh, entendo, é um truque inteligente" que você está usando, apenas uma função simples de redução em linha.


11

Tudo o que você está fazendo é destacar uma falha na complexidade ciclomática como métrica, a complexidade do código realmente não mudou. Você tem uma ramificação explícita no primeiro exemplo e precisa entender que há uma ramificação implícita no segundo. A segunda é mais clara e fácil de entender, desde que você entenda a sintaxe e, uma vez que ela usa menos sintaxe básica, isso pode ser um problema.


1
Alguém poderia argumentar que a complexidade ciclomática de seu próprio código é reduzida aqui, uma vez que whereaqui é provavelmente a partir de uma estrutura. Acho que a complexidade ciclomática como métrica é útil mesmo aqui porque, embora dividir uma função em várias não diminua a complexidade geral do sistema (na verdade, muitas vezes a aumenta, mesmo que ligeiramente), ajuda a determinar quando um parte do sistema se torna excessivamente complexa e precisa ser decomposta.
Threed 6/07/2015

6

Para responder à pergunta objetivamente, precisaríamos de algum tipo de métrica para manutenção. A complexidade ciclomática em si não é uma medida de manutenção, mas é um componente de algumas métricas que pretendem medir a manutenção. Por exemplo, a fórmula do Índice de Manutenção é:

MI = 171 - 5.2 * ln(V) - 0.23 * (G) - 16.2 * ln(LOC)

onde Gestá a complexidade ciclomática do código em questão. Portanto, reduzir a complexidade ciclomática de um pedaço de código melhora, por definição, o Índice de Manutenção e outras métricas que usam similarmente a complexidade ciclomática .

É difícil dizer se o tipo de mudança que você propõe faz com que o programa pareça mais sustentável para os programadores; isso provavelmente depende de como eles estão familiarizados (no seu caso) com o wheremétodo.


Atenção: Magic Numbers! (>_<)
Izkata 25/02

Há apenas um pequeno problema com o Índice de Manutenção. Os três componentes são o volume de Halstead, a complexidade ciclomática e as linhas de código. Demonstrou-se que tanto o volume de Halstead quanto a complexidade ciclomática estão fortemente correlacionados às linhas de código. Isso significa que uma equação aproximada alternativa para o Índice de Manutenção que depende SOMENTE de Linhas de Código pode ser derivada que seria quase tão precisa quanto a original, com uma dificuldade computacional consideravelmente menor.
John R. Strohm

@ JohnR.Strohm Acho que você obteria um resultado semelhante, mesmo que você apenas use LOC como métrica de manutenção. A alteração do OP reduz o LOC de 1 para 4, e outras alterações que reduzem a complexidade ciclomática também têm probabilidade de reduzir o LOC. De qualquer forma, tudo se resume a como você define e mede a manutenção.
Caleb

1
@ Caleb: Concordo. O argumento que tento enfatizar é que as pessoas recorrem com frequência a essas métricas e combinações de métricas realmente complicadas, sem perceber que quase todas elas demonstraram estar fortemente correlacionadas no código real com simples linhas de código (LOC) e, portanto, não têm mais valor preditivo ou descritivo do que LOC.
John R. Strohm

3

Como foi demonstrado que a complexidade ciclomática (CC) está fortemente correlacionada com o tamanho do código "tanto que se pode dizer que o CC não tem absolutamente nenhum poder explicativo próprio". o que você realmente está perguntando é se "métodos iterativos, como os comuns em linguagens modernas, como C #, JavaScript e (esperançosamente) no Java 8, reduzem o impacto do tamanho do código na compreensibilidade e capacidade de suporte do código".

Nesse ponto, seria de esperar que a resposta fosse óbvia. Sabe-se há décadas que códigos mais curtos geralmente são mais fáceis de entender, manter e dar suporte.


2

Se você está falando sobre as estatísticas brutas da Complexidade Ciclomática, com certeza. Você apenas reduziu de 2 para 0. Se você está indo em números, puro ganho, até o fim.

De uma perspectiva prática (leia-se: humana), eu diria que você realmente aumentou a complexidade em 2. Um ponto disso vem do fato de que agora outro programador deve trazer ou obter conhecimento da sintaxe fluente do LINQ para entender isso. código.

Outro ponto de maior dificuldade vem do entendimento das Expressões Lambda; Embora um Lambda seja bastante direto nesse caso , há algumas mudanças de paradigma que precisam ser feitas para apreciá-los completamente.

Nesse caso, o uso a.where(x => matches(x, arg))não é terrível e, com toda a honestidade, é uma ótima maneira de convencer alguns colegas de trabalho a ver e trabalhar com expressões LINQ e Lambda pela primeira vez (na verdade, apresentei um tutorial sobre LINQ / Lambdas a alguns ex-colegas de trabalho que usam este e outros conjuntos de códigos, com grande efeito.) No entanto, o uso desses requer algum conhecimento.

Eu recomendo cautela, porque vi casos em que o refator LINQ é realmente significativamente pior de ler do que o que esse foreachloop se torna.


1

Subjetivamente, depende do público desenvolvedor, se eles entenderem expressões lambda, então;

List<String> filteredList = originalList.where(s => matches(s));

é mais rápido de entender e talvez um pouco mais fácil. Eu ficaria mais preocupado em usar s e correspondências (). Nem é auto-descritivo, algo como;

List<String> filteredList = 
    originalList.where(stringToBeTested => matchesNameTest(stringToBeTested));

Ou

List<String> filteredList = 
        originalList.where(originalListString => matchesNameTest(originalListString));

fornece ao desenvolvedor informações mais significativas e é mais fácil de analisar, sem ter que mergulhar na função correspondências () para determinar qual correspondência está sendo executada.

A manutenção não se refere apenas à capacidade de compreender o código, mas principalmente à velocidade e precisão com que se pode compreender o código.


2
Oi Ace. É um exemplo, intencionalmente desprovido de conteúdo semântico para destacar a estrutura.
245 Ross C.
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.