• Aucun résultat trouvé

1.3 Génie Logiciel, refactorisation et optimisation

1.3.2 Génie logiciel

L tude des o es te h i ues de p og a atio pe etta t de te d e e s le eilleu logiciel possible est ainsi devenue une discipline à part entière : le Génie Logiciel (souvent abrégé GL). Le but est d ta li des gles plus ou oi s st i tes qui, lorsqu elles so t appliquées intelligemment, permettent d a lio e la qualité du logiciel créé (McConnell 1993) (Sommerville 2001). Le problème est quelque peu différent dans le cas de l a lio atio d u e appli atio d jà e ista te. Da s e as, l a lio atio de la ualit du ode d elopp da s l appli atio e peut se fai e au d t i e t de la ualit de l appli atio développée. Poussée à l e t e, l id e est u il est i utile de dispose d u ode d e elle te ualit si elui- i e pe et plus de fai e fo tio e l appli atio . Il faut do g ale e t alise des op atio s d a lio atio ponctuelle du code sans introduire de défauts da s l appli atio .

Ainsi, si un code fonctionnel de la PGMS existait avant le début de ma thèse, le choix a été fait de retravailler celui-ci en profondeur afin de faciliter les développements futurs. Le code ayant été développé par accrétion de fonctionnalités durant un laps de temps important, la qualité de l a hite tu e de l appli atio et celle du code développé s e est donc ressentie. Or, comme nous allons le voir par la suite, u o e i po ta t d tudes o t o t l i t t d appli ue de o es thodes de génie logiciel afi de a i ise l efficacité des développements et de minimiser les coûts de production.

Dans un programme orienté objet, une partie importante du travail est relative à la conception des classes. Cette partie traite particulièrement de ce problème. Rendre une classe la plus fo tio elle possi le est pas u e tâ he aussi fa ile ue ela peut sembler au premier abord. Un nombre important de points sont à prendre en compte lors de la conception. Lo je tif g al e he h a a oi u e influence importante sur la façon de o e oi les lasses de l appli atio . En fonction de cet objectif, les classes ne vont pas être conçues de la même façon. Ainsi, si l o he he à a lio e la robustesse d u e application, c'est-à-dire à améliorer sa capacité à continuer de fonctionner correctement en cas de

43 mauvaise utilisation, un plus grand nombre de tests seront réalisés au détriment de la

itesse d e utio du p og a e.

Au-delà des choix stratégiques qui peuvent e iste e t e la atio d u p og a e robuste, fiable, juste, adaptable et/ou s u is …, certaines notions restent majoritairement o u es à l e se le des lasses. Ainsi, une étude de (Woodfield, Dunsmore et Shen 1981) a o t u u p og a e st u tu e utilisa t les t pes de données abstrait (Abstract Data Type) était plus facilement compréhensible par un programmeur extérieur u u p og a e a a t été implémenté au plus proche des problématiques fonctionnelles. U e lasse est d auta t plus o p he si le, et do d auta t plus fa ile à modifier, si elle est o ga is e e u e se le oh e t d att i uts et de thodes. Un accent tout particulier est mis sur la conception de l i te fa e pu li ue d u e classe. L id e d u e telle approche est de conserver une interface la plus homogène possible. Le terme homogène est i i utilis pou u e i te fa e utilisa t le e i eau d a st a tio pou tous ses l e ts. Dans son excellent livre « Code Complete » (McConnell 1993), Steve McConnell donne plusieurs exemples de types de données abstraits ainsi que les opérations correspondantes :

- Pour un type « Lumière », il serait possible de disposer des opérations : allumer et éteindre.

- Pour un autre type : « Pile », les opérations disponibles pourraient être par exemple : i itialise , ajoute u l e t e haut de la pile, eti e l l e t e haut de la pile et li e l l e t e haut de la pile.

Ces deux interfaces sont parfaitement homogènes. Dans le premier cas, on traite unique e t de la ise sous ou ho s te sio d u e la pe. Da s le se o d as, o a ipule les éléments du e pile.

À l i e se, u e i te fa e o -ho og e pou ait, da s le as d u e lasse ep se ta t u panier de produits pour de la vente en ligne, traiter à la fois de l ajout et de la suppression de produits ajoute u p oduit, supp i e u p oduit… ais aussi du stockage dans une ase de do es d u tel pa ie (génération de la requête de création du tuple dans la base de do es… . I i, deu a st a tio s différentes se mélangent : la gestio des p oduits d u panier et le stockage des instances de panier. Pour ne pas compliquer la classe inutilement

44 et ai si di i ue les is ues d e eu s, la atio d u e se o de lasse, ne se chargeant que des manipulations liées aux bases de données, pourrait être une bonne initiative. Cette classe pourrait par ailleurs se retrouver fortement couplée à la première.

Au-delà d a oi u e i te fa e ho og e, il est utile de o se e des lasses si ples. Lo s ue l o pa le de classes simples, plusieurs points peuvent être abordés. Ici, nous nous focaliserons sur les points pour lesquels des statistiques ont prouvé, depuis déjà de

nombreuses années, un a a tage e te e de di i utio du o e d e eu s g es et

donc en terme de temps de conception (Basili, Briand et Melo 1996).

L i te fa e d u e lasse gag e tout d a o d à t e li it e e o e de thodes. Durant leurs travaux sur le sujet, (Basili, Briand et Melo 1996) ont fait développer une solution informatique à un même problème par plusieurs groupes de personnes. Les solutions utilisant dans leurs classes un plus grand nombre de méthodes ont aussi été celles pour lesquelles le o e d e eu s lors de la conception et lors de la livraison de la solution était le plus grand. Toujours dans son livre « Code Complete » (McConnell 1993), Steve McConnell propose le nombre de sept méthodes (plus ou moins deux) par classe. Ce nombre de sept plus ou oi s deu est ti de l a ti le du ps hologue Geo ge Mille (Miller 1956) dans

le uel il e pli ue ue e o e o espo d au o e d l e ts diff e ts u u e

personne peut utiliser lo s u elle exécute une tâche sa s a oi à fai e d effo ts suppl e tai es pou se appele de l utilisatio de ha u . Bien entendu, selon les pe so es et selo l e t ai e e t de elles-ci à la tâche u elles sont en train d effe tue , ce nombre peut être différent. Il e este pas oi s u il s agit là d u e o e ase lo s u il est essai e de fl hi au o e de thodes u u e lasse doit p opose , au nombre de paramètres maximal de ces méthodes ou à la profondeur maximale d h itage d u e classe (ce qui est souvent très important comme nous le verrons pas la suite), etc.

Mai te a t u il de ait t e a uis ue la li itatio du o e de thodes d u e lasse induit un plus faible risque d e eu s, il est i po ta t de ie oter u il e s agit pas là du seul l e t a a t u i pa t statisti ue i po ta t su le o e d e eu s ui peu e t apparaître lo s de l itu e d u p og a e. La o eptio des thodes est gale e t t s i po ta te. L tude de (Basili, Briand et Melo 1996) montre ainsi que le nombre d e eu s est statisti ue e t plus i po ta t lo s ue le o e de fo tio s et de thodes

45 appelées pa l e se le des thodes d u e lasse aug e te. De la e faço , plus le couplage o e d utilisations de thodes et d i sta es d aut es lasses pa les thodes d u e lasse entre classes est grand, plus les chances de rencontrer des erreurs augmenteront.

U e o e ho og it de l i te fa e de la lasse pe et sou e t de li ite le p e ie poi t. E effet, o se e d fa ile e t o pte ue da s l e e ple o ho og e du panier présenté précédemment, toutes les méthodes traitant du stockage dans la base de données vont utiliser un jeu de méthodes et de fonctions proches les unes des autres mais très éloignées des autres méthodes qui gèrent les éléments du panier. La conception de deux classes résoudrait une nouvelle fois ce problème : chaque classe utiliserait un jeu de thodes t s diff e t pa appo t à l aut e lasse ais au sei de ha u e des classes, les méthodes seraient beaucoup réutilisées ou du moins très homogènes.

Le couplage est u e sou e d e eu a l aug e tatio des i te a tio s e t e les lasses rend ces dernières beaucoup plus difficiles à appréhender dans leur ensemble pour les développeurs. Il faut se appele ue le o e d l e ts u u e pe so e peut a ipule à un instant donné sans effort supplémentaire, et donc sans augmenter le risque de faire des erreurs, est limité. Ici encore, le découpage en classes plus simples est encore une bonne solution. L id e est d aug e te la oh sio de la lasse. Si u e grande partie de la classe est couplée à des instances de la classe SQLManager, e est pas u p o l e a la lasse e question doit sans doute gérer les connexions aux bases de données. Le problème apparait que lo s u u e lasse est oupl e à plusieurs lasses a a t au u ou peu de appo t e t e elles. Il serait alors judicieux de penser à découper la classe en plusieurs classes qui traiteraient chacune une fonctionnalité. Ce problème de cohésion a également été mis en valeur lo s de l i pl e tatio d u e thode. Si u e thode t aite de plusieu s problèmes différents, la p o a ilit d appa itio d e eu s est plus i po ta te. Il est alors essai e d t e ho og e pou l e se le des thodes ui o stitue t l i te fa e de la classe o e ous l a o s u p de e t mais tout autant da s l i pl e tatio de

ha u e d e t e elles.

U e aut e sou e i po ta te d e eu s lors de la o eptio d u p og a e orienté objet est à he he du ôt de l h itage. Il s agit d u concept très puissant pour modéliser

46 certains problèmes et comme tout concept, une utilisation erronée mène souvent à des erreurs. La première chose à intégrer est la règle suivante : l h itage public correspond à la elatio de g alisatio / sp ialisatio ue l o ep se te pa u e elatio o tologi ue de type « est un ». Cette règle est suffisamment importante pour que Meyers parle dans l u e des se tio s de so li e « Effective C++ » (Meyers 1992) de « la règle la plus importante lors de la conception de programmes orientés objet en C++ ». Dès 1987, Liskov énonce le principe de substitution qui porte son nom et ui d fi it ue i po te uelle instance « instanceFille » d u e lasse « Fille » ayant pour classe mère une classe de base « Mere » doit pou oi t e pass e e pa a t e de i po te uelle fo tio ou méthode acceptant une instance « instanceMere » de la classe mère (Liskov 1987). Cette règle est reformulée par Hunt et Thomas (Hunt et Thomas 2000) dans leur livre « The pragmatic programmer - from journeyman to master » o e le fait u u e lasse fille doit pou oi t e utilisa le à t a e s l i te fa e de la lasse e sa s ue l utilisateu ait besoin de o ait e la diff e e. Ai si, lo s u u e lasse e peut t e o sid e o e u e classe fille du poi t de ue de es d fi itio s, il s agit sou e t d u e au aise o eptio de la hi a hie d h itage qui nécessite un nouveau découpage.

Afi d illust e e p o l e, l e e ple d u e lasse oiseau poss da t u e thode voler() est souvent utilisée (voir Figure 1-13. Si l o souhaite od lise diff e tes a es d oiseaux, il est possi le d utilise l h itage pou i pl e te la lasse Fau o ou la lasse Pigeon. Mais l i t g atio de la lasse Autruche pose alors problème : une autruche est bien entendu un oiseau mais celle-ci ne peut pas voler. Il est donc anormal de pouvoir utiliser une i sta e de la lasse Aut u he à t a e s l i te fa e de sa classe mère Oiseau : le principe de substitution de Liskov est transgressé. Une solution consiste alors à créer deux nouvelles classes : OiseauVolant et OiseauNonVolant. La méthode voler() ne sera alors plus présente dans la classe Oiseau mais seulement dans sa sous classe OiseauVolant (voir Figure 1-14). Cette solution, ui est pas la seule possi le, est pas e e pte de d fauts car elle augmente le nombre de classes (cinq classes au lieu de quatre) ainsi que la profondeur d h itage (trois niveaux d h itage au lieu de deu ; e ui est pas sa s pose p o l e comme nous le verrons par la suite.

47

+voler()

Oiseau

Pigeon

Faucon Autruche

Figure 1-13 : Diagramme de classe original des différents oiseaux.

+voler() OiseauVolant Pigeon Faucon Autruche OiseauNonVolant Oiseau

Figure 1-14 : Diagramme de classe des différents oiseaux différenciant les oiseaux volants et non volants.

Lorsque le principe de substitution de Liskov est transgressé, il peut gale e t s agi d u e erreur de conception. Le concept ue l o souhaite i pl e te est alo s pas « est un » mais souvent « a un » ou « est implémenté en termes de » que Meyers préconise d i pl e ter à l aide de la composition ou de l ag gatio da s u e aut e se tio de so livre.

Mais au-delà du p o l e de o eptio de l h itage e tant que « est un », d aut es problèmes peuvent apparaître. L utilisatio de l h itage a pou d sa a tage d aug e ter la o ple it d u e lasse : la o aissa e de l i pl e tatio de la lasse fille est plus suffisa te pou u d eloppeu ui souhaite ait o ait e l i te fa e complète de cette classe ou bien la façon dont sont implémentées toutes les méthodes. Beaucoup de ces informations vont ainsi appartenir à la classe mère. Et ceci est un problème majeur car cela a a oi u g os i pa t su l e apsulatio de la lasse, qui peut être brisée. Ainsi, il sera sou e t essai e de o ait e l i pl e tatio d u e ou plusieu s thodes de la lasse

48 e pou ga a ti u u e fo tio alit de la lasse fille est i pl e t e correctement. Dans son livre « Effective Java » (Bloch 2001), Joshua Bloch propose l e e ple sui a t pou illustrer ce problème :

public static class InstrumentedHashSet<E> extends HashSet<E> {

public int addCount = 0;

@Override

public boolean add(E a) {

addCount += 1;

return super.add(a);

};

@Override

public boolean addAll(Collection<? extends E> c) {

addCount += c.size();

return super.addAll(c);

} }

Le but est ici de créer une nouvelle classe représentant un hash set qui fonctionnerait exactement comme la classe HashSet p se te da s l API Ja a ais ui pe ettrait en plus de o pte le o e d ajouts réalisés au set. Pou o pte les ajouts, l auteu a ai si d id d i e te le o pteu d ajout addCount d u e unité dans la méthode add qui prend en paramètre un objet à ajouter et également de mettre à jour le o e d l e ts ajoutés dans la méthode addAll qui prend en paramètre u e olle tio d o jets à ajoute . Le p o l e i te ie t lo s ue l o sait ue la thode addAll de HashSet réutilise en fait la méthode add sur chacun des objets de la collection : l utilisatio de addAll compte alors deux fois chaque ajout. Par le fait u il soit nécessaire, pour implémenter correctement cette sous-classe, de connaître l i pl e tatio de la lasse mère HashSet (qui, en vertu de l e apsulation, ne devrait pas avoir à être connue), il est possible dans ce cas de dire que l h itage ise l e apsulatio .

C est sans doute pou es aiso s ue l tude de (Basili, Briand et Melo 1996) montre également ue l aug e tatio de la p ofo deu de l h itage a une forte influence sur

l aug e tatio du o e d e eu s da s u p og a e.

Il se ait possi le de s pa d e eau oup plus sur la conception des routines (fonctions et méthodes), de discuter du choix du nom des classes, des routines et des variables,

49 d i t odui e les diff e tes thodes pe etta t la d te tio d e eu s le plus efficacement possible (revue du code par un collègue, tests unitaires, tests fo tio els… et eau oup d aut es points. Le choix a été fait de s a te à la o eptio des lasses, ui est u u e a he pa i ie d aut es du g ie logi iel (Sommerville 2001), a il s agissait du problème de conception le plus important constaté dans la reprise du premier prototype de la PGMS.

Documents relatifs