vignette|Image représentant la puanteur/pourriture, représentation métaphorique du .
En génie logiciel, les ou mauvaises odeurs peuvent être de mauvaises pratiques de conception logicielle qui conduisent à l’apparition de défauts. Ces défauts sont souvent issus de mauvais choix d’implantation ou de conception et conduisent à une complexification du code source et de la maintenance et évolutivité de celui‐ci. À la différence d'un AntiPattern, les code smells ne sont pas forcément des erreurs, c'est-à-dire qu'ils peuvent persister sans perspective d'évolution dans un logiciel. Afin de corriger un code smell, il est nécessaire de procéder à un réusinage du code source, c’est‐à‐dire modifier le code sans en altérer son comportement.
Des facteurs tels que la compréhensibilité du code, la facilité avec laquelle il peut être modifié, la facilité avec laquelle il peut être amélioré pour prendre en charge les changements fonctionnels, la capacité des codes à être réutilisés dans des contextes différents, le degré de testabilité du code et la fiabilité du code sont des facteurs. qui peut être utilisé pour identifier les code smells. Exemples de code smells À la manière des patrons de conceptions, de nombreux exemples de code smells ont été répertoriés et décrits dans la littérature scientifique.
Martin Fowler en a notamment répertorié des dizaines ainsi que la refactorisation à adopter.
Duplicated Code L’anti‐patron Duplicated Code est un exemple de code smell classique. Il s’agit de trouver la même portion de code à plusieurs endroits d’une application.
La duplication de code peut se situer dans une même classe, au sein de méthodes différentes, ou dans des classes différentes.
Feature Envy L’anti‐patron Feature Envy décrit une méthode qui fait de nombreux appels à des méthodes d’autres classes. Le plus souvent ces appels sont faits à des getters (ou accesseurs) par besoin de données.
C’est le signe que la méthode se trouve probablement dans la mauvaise classe.
BLOB Aussi connu sous le nom de God Class ou Winnebago, l’anti‐patron Blob est une classe ayant trop de responsabilités au sein du logiciel.
Cela se manifeste par un trop grand nombre d’attributs et de dépendances aux classes data, ainsi qu’un nombre d’appels de ces méthodes par les classes extérieures très important.
Long Parameter List
L’anti‐patron Long Parameter List est une erreur héritée des débuts de la programmation, avant l’arrivée de l’orienté objet. Chaque fonction nécessitait toute une série de paramètres, qu’il était préférable d’utiliser à la place des variables globales. Mais aujourd’hui et depuis l’arrivée des langages orientés objets, la situation a évolué et le surnombre de paramètres ne fait que nuire à la lisibilité et la compréhension. Cela complique également l’utilisation et la refactorisation du code.
Long Method L’implantation de longues méthodes est un problème récurrent chez les développeurs débutants. Il s’agit de rendre les méthodes inutilement longues et complexes quand il est possible de les décomposer.
Cette décomposition en plus d’améliorer la lisibilité et la compréhension d’un code, permet de simplifier la refactorisation par la suite. On la caractérise souvent par l’utilisation d’un trop grand nombre de paramètres internes, de boucles ou conditions ainsi que de valeurs de retour.
Large Class L’anti‐patron Large Class décrit simplement le cas d’une classe possédant beaucoup de variables. Ce genre de situation induit parfois une utilisation séparée des variables et conduit le plus souvent vers de la duplication de code.
Détection et correction de code smells
Moyens de détection Pour chaque code smell, il existe des moyens de détection plus ou moins mathématiques ainsi que des pratiques permettant de corriger ou même éviter l’erreur. Pour chacun des six code smells précédents, voici la méthodologie de correction ainsi que le moyen de détection à adopter.
Duplicated Code Pour détecter le smell Duplicated Code (code dupliqué littéralement), il faut mesurer le pourcentage de lignes dupliquées dans le logiciel. Cela peut paraître assez simple d’un premier abord, mais un morceau de code peut être dupliqué de différentes manières. La détection du duplicata exact est triviale, mais dans certains cas, il faut aussi être capable de gérer le renommage ou l’alias.
Par exemple en Java : Integer var = 5; String str = String.valueOf(var); est la duplication de code de : Integer nombre = 5; String chaine = String.valueOf(nombre); Si tel est le cas, il faut donc que l’algorithme de détection teste toutes les possibilités de renommage pour être bien certain de ne pas manquer un doublon de code. De plus, il est tout à fait possible que la duplication de code ait été modifiée légèrement. Pour reprendre notre exemple précédent, il faudrait aussi prendre en compte les cas suivants : Integer var = 5; // Du code ne modifiant pas var... String str = String.valueOf(var);
Dans d’autres cas, il se peut que deux algorithmes différents fassent le même travail. Le cas échéant, l’algorithme le plus optimisé est sélectionné et utilisé pour les deux appels. Cette erreur est souvent commise par les débutants à la suite d'abus de copier/coller. En plus d’augmenter sensiblement la taille du code source, elle engendre également des problèmes lors de la maintenance. Pour corriger ce problème, il faut extraire le code dupliqué et le déplacer dans une nouvelle méthode et remplacer les anciens codes par des appels à cette nouvelle méthode. Enfin, il est possible de l’éviter en s’imposant les bonnes pratiques de développement suivantes :
Factoriser son code le plus possible (et dès que possible). Travailler selon la technique du test driven development.
Feature Envy Le smell Feature Envy (littéralement la « jalousie de fonctionnalités ») survient dans une classe quand une méthode est plus intéressée par les membres (attributs et méthodes) d’une autre classe que les siens. Pour corriger ce genre de problème au sein d’une classe, plusieurs solutions : Déplacer une méthode d’une classe dans une autre si elle utilise plus les données de celle‐ci que les siennes. Par exemple, dans la classe Rectangle, la méthode perimetre() peut être totalement déplacée dans la classe AutreClasse. public class Rectangle {
...
public Double perimetre() { return 2 * AutreClasse.largeur + 2 * AutreClasse.longueur;
Déplacer une partie d’une méthode dans une autre si elle accède aux données d’une autre classe plus que les siennes. Même principe avec une méthode accédant aux données de plusieurs classes. Par exemple, dans la classe bateau, la méthode preparer_bateau() utilise plus d’appels de méthodes de la classe SPI que ses propres méthodes. public class Bateau {
private SPI spi; private Reserve reserve;
...
public void preparer_bateau() { this.laver(); this.verifier_coque(); reserve.verifier_provisions(); spi.regler_hauteur(); spi.regler_largeur(); spi.verifier_voile();
La correction adaptée serait donc de créer une méthode dans SPI regroupant les trois appels et d’appeler cette nouvelle méthode dans preparer_bateau().
Pour détecter le code smell Feature Envy, il faut être capable de localiser les méthodes utilisant trop les méthodes d’une classe extérieure. Pour cela, il faut mesurer la force de couplage qu’ont les méthodes d’une classe avec les autres méthodes ou données de classes extérieures. Lanza et Marinescu proposent une formule conditionnelle de détection basée sur des métriques :
Avec : 5, une valeur fixée et expliquée dans leur ouvrage. LAA (Locality of Attribute Accesses) qui représente la proportion de méthodes de la classe utilisant accédant à plus d’attributs de classes extérieures qu’à ses propres attributs. FDP (Foreign Data Providers) qui représente la proportion d’utilisation d’attributs « extérieurs » et utilisés dans très peu d’autres classes. ATFD (Access to Foreign Data) qui représente la proportion de méthodes de la classe qui accèdent aux attributs directement ou via des accesseurs d’autres classes extérieures.
Cette méthode de détection est utilisée dans les outils iPlasma et inFusion. D’autres outils comme JDeodorant, essayent de déplacer les méthodes dans des autres classes. Ainsi pour chaque méthode du programme, un algorithme va former une liste de classes candidates où il est possible de déplacer la méthode. Ensuite, pour chacun de ces candidats, un calcul de distance sera effectué ainsi qu’un calcul sur le nombre d’appels d’accesseurs. À la fin, la classe sélectionnée est celle dont la distance calculée est minimale et le nombre d’appels d’accesseurs maximal. Autre approche avec un outil nommé HIST basé sur l’analyse de commits, une méthode affectée par le Smell Feature Envy a tendance à changer plus fréquemment avec les méthodes des autres classes qu’avec celles de sa propre classe. Enfin, il existe également un plugin Eclipse permettant de détecter cette erreur. Ce greffon nommé Eclipse Metrics. calcule un indice de jalousie maximal et dès que cet indice est dépassé, l’utilisateur est informé via un message d’avertissement.
BLOB Cette erreur est l’impact direct de l’incapacité à concevoir correctement des architectures logicielles complexes dans un langage de programmation procédurale. En plus d’être totalement inélégant, le programme perd en vitesse d’exécution et la classe est difficilement maintenable. Il est possible de l’éviter en s’imposant les bonnes pratiques de développement suivantes : Utiliser le paradigme Diviser pour mieux régner. Créer des classes abstraites ou qui étendent d’autres classes.
Il n’y a pas de correction miracle pour un tel cas, il faut repenser son code afin de le factoriser. La taille de la classe n’est pas liée à cette erreur de conception. D’une manière générale, voici les corrections typiques Retirer les méthodes n’ayant aucun impact sur la classe en question et les replacer dans la classe affectée par l’appel de la méthode.
Ainsi, dans la classe ci‐dessous, la méthode afficher_pilote() devra être déplacée dans la classe Pilote… public class Avion {
private Pilote pilote;
...
public String afficher_pilote() {...}
} Retirer les attributs présents illogiquement dans la classe et les placer dans les classes adaptées.
Ainsi, dans la classe ci‐dessous, l’attribut matricule_pilote devra être déplacée dans la classe Pilote… public class Avion {
private Pilote pilote; private String matricule_pilote;
...
} Retirer les associations dont l’existence n’est pas nécessaire.
Ainsi, dans la classe ci‐dessous, la référence pilote devra être déplacée dans la classe Equipage… public class Avion {
private Equipage equipage; private Pilote pilote;
...
} Pour la détection, Lanza et Marinescu proposent la formule conditionnelle suivante reposant sur trois métriques :
Avec : 47 et 5, des valeurs fixées et expliquées dans leur ouvrage. WMC (Weighted Method Count) qui représente la somme de la complexité statistique de toutes les méthodes dans une classe. TCC (Tight Class Cohesion) qui représente le nombre relatif de méthodes accédant aux attributs. ATFD (Access to Foreign Data) qui représente le nombre de classes externes qui accèdent aux attributs de la classe directement ou via des accesseurs. Cette méthode de détection est utilisée dans les outils iPlasma et inFusion.
DECOR utilise des fiches de règles nommées « Rules Cards ». D’après les caractéristiques de ces « Rules Cards », un BLOB est une classe ayant un LCOM5 (un manque de cohésion de méthodes, Lack of Cohesion Of Methods en anglais) supérieur à 20 et un nombre de méthodes et d’attributs supérieurs à 20. De plus, les classes sont identifiables par des noms suspects comme Process, Control, Manage, System etc...
Pour connaître le LCOM5 d’une classe, on utilise la formule ci‐dessous :
Avec : l, le nombre d’attributs k, le nombre de méthodes a, la somme du nombre d’attributs distincts accédés par chaque méthode dans une classe