jeudi 24 mai 2012

LCOM4 : "Lack Of Cohesion" (quatrième variante) ou "Manque de Cohésion"

Un des principes fondamentaux de l'Orienté-Objet est qu'une classe doit avoir une et une seule responsabilité. Les anglo-saxons appellent cela le « Single Responsibility Principle » ou « Principe de Responsabilité Unique » :
IL NE DOIT PAS Y AVOIR PLUS D'UNE RAISON POUR UNE CLASSE DE DEVOIR CHANGER
Ce principe a été énoncé pour la première fois par Tom DeMarco et Meilir Page-Jones. Robert C. Martin en a décrit les aspects dans un excellent article. Ce n'est pas un principe sorti de nul part, édicté comme cela par maniérisme ou pédantisme. Cela provient du fait qu'au niveau d'une classe, on peut voir les propriétés (d'instance ou de classe) comme des variables globales. Et vous connaissez aussi ce vieil adage :
N'UTILISEZ JAMAIS DE VARIABLES GLOBALES
Cela provient du fait qu'une variable globale n'indique pas, par définition dans quel périmètre elle doit être utilisée. Ce qui fait qu'un programmeur non scrupuleux sera tenter de l'utiliser directement dans n'importe quelle circonstance, sans jamais se demander si elle est utilisée/modifiée par ailleurs. Toutes ces dépendances sur ces même variables globales créent rapidement des dépendances implicites entre toutes les parties du code source de l'application.

Ainsi, les variables globales d'une classe finissent toujours par être utilisées par tous les membres de cette classes. Il est donc important de détecter, lors de la programmation, si, dans une même classe, il n'y a pas plusieurs ensembles de méthodes et variables qui ont des vies complètement séparées. C'est-à-dire qu'il faut tracer un graphe des dépendances entre membres de la classe et déterminer le nombre de graphes indépendants les uns des autres.

Ce nombre est dénommé LCOM4, de la technique développée par Hitz et Montazeri dans leur article « Measuring Coupling and Cohesion In Object-Oriented Systems ». L'aide en ligne de l'outil Project Analyzer explique le calcul de ce nombre par un exemple graphique :



L'exemple sur la gauche montre une classe constituée des méthodes A à E et des variables x et y. A appelle B et B accède à x. C et D accèdent à y. D appelle E mais E n'accède à aucune variable.
Cette classe a 2 composants non reliés (ici deux graphes distincts) donc LCOM4 = 2. Il faut diviser la classe en {A, B, x} et {C, D, E, y}.

Sur l'exemple de droite, nous avons changé C pour qu'elle accède à x pour augmenter la « cohésion ». Maintenant la classe n'est constituée que d'un seul composant. (LCOM4 = 1). C'est une classe cohésive.
Étudions un exemple concret. Bizarrement, je n'ai pas trouvé de plugin Eclipse réalisant le calcul de LCOM4. Le plugin « Eclipse Metrics Plugin » calcule d'autres métriques de cohésion (moins intéressantes à mon goût) mais pas LCOM4. Dommage.

A ma connaissance, seul Sonar calcule automatiquement LCOM4. Voici comment faire pour obtenir cette métrique Sonar dans Eclipse. Reprenons l'exemple précédent, et implémentons-le en Java, cela nous donne :
public class Foo {
 
 private int x;
 private int y;
 
 public void methodA() { methodB(); } 
 public void methodB() { x = x + 1; } 
 public void methodC() { y = y + 1; }
 public void methodD() { y = y - 1; methodE(); }
 public void methodE() { }
}
Quand le plugin Sonar est configuré dans Eclipse pour calculer localement les métriques, on peut afficher toutes les métriques à l'écran :




samedi 12 mai 2012

Qu'est-ce que la métrique "Feature Envy" ou "Jalousie de Fonctionnalité" ?

Saviez-vous que le code source d'un programme a une odeur ? C'est souvent quand le code sent mauvais qu'on la remarque (« code smells »). L'analyse du code permet de détecter des mauvaises pratiques de l'orienté-objet ou plus globalement, des paradigmes à disposition dans le langage de programmation.

Aujourd'hui penchons-nous sur cette odeur nauséabonde du code que les anglo-saxons appellent « feature envy », et que je traduirais par « jalousie de fonctionnalité ».

La « jalousie de fonctionnalité » survient quand une méthode est plus intéressée par les fonctionnalités (méthodes et champs) d'autres classes que les siennes propres. La solution la plus simple est de déplacer la méthode dans ces autres classes, tout du moins, une partie de la méthode, celle qui a le plus de « jalousie ».

Le greffon Eclipse « Eclipse Metrics Plugin » calcule cette métrique avec la formule suivante :

Etant donné :

m, la méthode pour laquelle nous souhaitons calculer la « jalousie de fonctionnalité »
Fc, l'ensemble des fonctionnalités utilisées par m et appartenant au type c
cm, la classe dans laquelle m est définie

Alors

JalousieDeFonctionnalité(m) = maxc ≠ cm(|Fc|) - |Fcm|

Prenons un exemple :

Etant donné la classe Foo suivante :

package com.jeromeradix;

public class Foo {
 public void methodA() { } 
 public void methodB() { } 
 public void methodC() { }
}

et la classe Velo suivante :

package com.jeromeradix;

public class Velo {
 public void methodX() { }
 public void methodY() { }
}

Examinons le classe Bar suivante pour établir la métrique JalousieDeFonctionnalité(Bar.method1) :

package com.jeromeradix;

public class Bar {
 public void method1(Foo f, Velo v) {
  f.methodA();
  f.methodA();
  f.methodB();
  v.methodX();
 }
}

Comme method1() appelle deux méthodes différentes de Foo, on obtient |FFoo| = 2.
Comme method1() appelle une seule méthode de Velo, on obtient |FVelo| = 1.

Ainsi maxc ≠ Bar(|Fc|) = 2

La méthode method1() n'utilise pas d'autres méthodes de Bar, donc |FBar| = 0

Ainsi, JalousieDeFonctionnalité(Bar.method1) = maxc ≠ Bar(|Fc|) - |FBar| = 2 – 0 = 2.


Ce que nous confirme l'export HTML de l'Eclipse Metrics Plugin :
Le plugin Eclipse Metrics part sur une valeur max de JalousieDeFonctionalité = 2. Cela veut dire qu'au delà de 2, un message d'avertissement sera affiché dans la fenêtre "Problème" d'Eclipse, message qu'il sera donc intéressant de suivre en reconcevant la méthode si jalouse de ses voisines.