Les méthodes itératives réduisent-elles la complexité cyclomatique et améliorent-elles la prise en charge?

11

Les méthodes itératives telles que celles que l'on trouve couramment dans les langages modernes tels que C #, JavaScript et (espérons-le) dans Java 8 réduisent-elles l'impact de la complexité cyclomatique sur la compréhensibilité et la prise en charge du code?

Par exemple, en C #, nous pourrions avoir le code suivant:

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

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

Cela a une complexité cyclomatique simple de 2.

Nous pourrions facilement réécrire ceci comme:

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

Qui a une complexité cyclomatique simple de 0.

Cela se traduit-il réellement par un code plus supportable? Existe-t-il des recherches réelles sur ce sujet?

Traverser
la source
2
Votre titre pose des questions sur la réduction de la complexité cyclomatique, mais votre texte suppose une réduction CC et pose des questions sur la maintenabilité. Cela a le potentiel d'être une question précieuse, pouvez-vous modifier le titre pour être plus précis?
Kilian Foth,
@KilianFoth Est-ce mieux?
C. Ross
J'associerais des méthodes itératives à un développement itératif de métodologies. Je pense qu'un meilleur terme serait des fonctions d'ordre supérieur. (Et en passant, les méthodes n'ont pas de type de retour / void alors que les fonctions retournent quelque chose).
Johannes Rudolph
@JohannesRudolph "les méthodes n'ont pas de type de retour / void alors que les fonctions retournent quelque chose" - dans quelle langue est-ce vrai? Je n'ai jamais entendu ça; en fait, la seule vraie distinction que j'ai jamais entendue était que les méthodes sont associées à une classe, les fonctions ne le sont pas.
Threed

Réponses:

14

Je suppose que vous venez de diviser / déplacer la complexité. Elle a diminué car vous ne comptez pas l'implémentation de .where()dans votre CC.

Le CC global n'a pas vraiment bougé, le CC de votre propre code a diminué, uniquement parce qu'il est maintenant déplacé vers le code du framework.

Je dirais que c'est plus maintenable. Quand c'est une caractéristique de la langue, utilisez-la. Ce n'est pas un "ohh, je vois, c'est une astuce astucieuse" que vous utilisez, seulement une simple fonction de réduction en ligne.

Clement Herreman
la source
11

Tout ce que vous faites, c'est mettre en évidence une faille dans la complexité cyclomatique en tant que métrique, la complexité du code n'a vraiment pas changé. Vous avez une branche explicite dans le premier exemple et devez comprendre qu'il y a une branche implicite dans le second. La seconde est plus claire et plus facile à comprendre à condition que vous compreniez la syntaxe, et puisqu'elle utilise moins de syntaxe de base qui peut être un problème.

Ryathal
la source
1
On pourrait faire valoir que la complexité cyclomatique de son propre code est réduite ici, car whereil s'agit probablement d'un cadre. Je pense que la complexité cyclomatique en tant que métrique est utile même ici car, tout en décomposant une fonction en plusieurs ne diminue pas la complexité globale du système (en fait, elle l'augmente souvent, ne serait-ce que légèrement), elle vous aide à déterminer quand une une partie du système devient trop complexe et doit être décomposée.
2015 à 19h32
6

Afin de répondre objectivement à la question, nous aurions besoin d'une sorte de métrique pour la maintenabilité. La complexité cyclomatique elle - même est pas une mesure de maintenabilité, mais il est une composante de certains paramètres qui sont censés mesurer maintenabilité. Par exemple, la formule de l' indice de maintenabilité est la suivante:

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

Gest la complexité cyclomatique du code en question. Par conséquent, la réduction de la complexité cyclomatique d'un morceau de code améliore par définition l'indice de maintenabilité du code et d'autres mesures qui utilisent de manière similaire la complexité cyclomatique .

Il est difficile de dire si le genre de changement que vous proposez rend le programme semble plus maintenable aux programmeurs; cela dépend probablement de leur familiarité (dans votre cas) avec la whereméthode.

Caleb
la source
Attention: numéros magiques! (>_<)
Izkata
Il y a juste un petit problème avec l'indice de maintenabilité. Ses trois composants sont le volume Halstead, la complexité cyclomatique et les lignes de code. Il a été démontré que le volume de Halstead et la complexité cyclomatique sont très fortement corrélés aux lignes de code. Cela signifie qu'une autre équation approximative pour l'indice de maintenabilité qui dépendait UNIQUEMENT des lignes de code pourrait être dérivée, qui serait presque aussi précise que l'original, avec beaucoup moins de difficultés de calcul.
John R. Strohm
@ JohnR.Strohm Je pense que vous obtiendriez un résultat similaire même si vous utilisez simplement LOC comme métrique de maintenabilité. Le changement de l'OP réduit le LOC de 1 à 4, et d'autres changements qui réduisent la complexité cyclomatique sont également susceptibles de réduire le LOC également. Quoi qu'il en soit, cela se résume vraiment à la façon dont vous définissez et mesurez la maintenabilité.
Caleb
1
@Caleb: D'accord. Le point que j'essaye de faire est que les gens optent trop souvent pour ces métriques et combinaisons de métriques vraiment compliquées, ne réalisant pas que presque toutes ont été fortement corrélées sur du vrai code avec de simples anciennes lignes de code (LOC) , et n'ont donc pas plus de valeur prédictive ou descriptive que LOC.
John R. Strohm
3

Parce qu'il a été démontré que la complexité cyclomatique (CC) est très fortement corrélée à la taille du code, "à tel point que l'on peut dire que CC n'a absolument aucun pouvoir explicatif en soi". ce que vous demandez vraiment, c'est si "les méthodes itératives que l'on trouve couramment dans les langages modernes tels que C #, JavaScript et (espérons-le) dans Java 8 réduisent l'impact de la taille du code sur la compréhensibilité et la prise en charge du code."

À ce stade, on espère que la réponse sera évidente. Il est connu depuis des décennies qu'un code plus court est généralement plus facile à comprendre, à gérer et à prendre en charge.

John R. Strohm
la source
2

Si vous parlez de la statistique brute de la complexité cyclomatique, bien sûr. Vous venez de le faire passer de 2 à 0. Si vous utilisez des chiffres, du gain pur, tout le chemin.

D'un point de vue pratique (lire: humain) cependant, je dirais que vous avez en fait augmenté la complexité de 2. Un point vient du fait que maintenant un autre programmeur doit apporter ou acquérir une connaissance de la syntaxe fluide LINQ pour comprendre cela. code.

Un autre point de difficulté accrue vient de la compréhension des expressions Lambda; bien qu'une Lambda soit assez simple dans ce cas , il y a des changements de paradigme qui doivent être faits pour les apprécier pleinement.

Ce cas, l'utilisation a.where(x => matches(x, arg))n'est pas terrible, et en toute honnêteté, c'est un excellent moyen pour un collègue de voir et de travailler avec les expressions LINQ et Lambda pour la première fois (j'ai en fait présenté un tutoriel sur LINQ / Lambdas à certains anciens collègues utilisant ce et d'autres ensembles de code, pour un grand effet.) Cependant, leur utilisation nécessite certaines connaissances.

Je recommande la prudence, car j'ai vu des cas où le refactoriseur LINQ est en réalité bien pire à lire que ce que foreachdevient cette boucle.

Andrew Gray
la source
1

Subjectivement, cela dépend du public des développeurs, s'ils comprennent les expressions lambda, alors;

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

est plus rapide à comprendre et peut-être légèrement plus facile. Je serais plus préoccupé par l'utilisation de s et de matches (). Ni l'un ni l'autre n'est auto-descriptif, quelque chose comme;

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

Ou

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

donne au développeur des informations plus significatives et est plus facile à analyser, sans avoir à plonger dans la fonction matches () pour déterminer quelle correspondance est effectuée.

La maintenabilité ne concerne pas seulement la capacité à comprendre le code, mais surtout la vitesse et la précision avec lesquelles on peut comprendre le code.

AceCTO
la source
2
Salut Ace. C'est un exemple, intentionnellement dépourvu de contenu sémantique pour mettre en évidence la structure.
C. Ross