L’évolution des logiciels est reconnue comme un processus naturel pour les systèmes d’une longue durée de vie (Lehman, 1980). Les raisons de l’évolution sont principalement liées aux changements dus à l’ajout ou l’amélioration des fonctionnalités, la correction des bogues, la refactorisation du code source, l’adaptation à un nouvel environnement, etc. Bien que ces changements soient nécessaires pour garder la pertinence des logiciels, l’évolution peut mener à la dégradation de la conception, et rend le système plus difficile à maintenir et à tester. En conséquence, les logiciels peuvent devenir ingérables et éventuellement inutilisables (Van Gurp et Bosch, 2002).
En génie logiciel, les abstractions sont des mécanismes importants dans la conception permettant de gérer les parties complexes des systèmes et de faciliter le développement modulaire. L’idée fondamentale de ce concept revient au principe de masquage des informations proposé par Parnas (1972) qui consiste en la création des modules permettant de dissimuler les décisions de conception qui sont difficiles ou présentent des possibilités de changement. Une abstraction peut prendre la forme d’une interface entre les détails des implémentations d’un module souvent exposés aux changements et les autres composants d’un système. Le concepteur décide les fonctionnalités qui sont visibles à l’extérieur et qui forment les abstractions des variations. L’avantage de ce mécanisme est de faciliter le développement parallèle en équipe. Les composants logiciels peuvent évoluer de façon indépendante. De plus, il favorise le faible couplage entre les modules en minimisant les dépendances risquées.
Les langages modernes de la programmation orientée objet prennent en charge l’abstraction à travers plusieurs techniques de développement. En Java, les interfaces sont des concepts importants permettant de cacher les implémentations par rapport aux composants externes appelés clients. Une interface déclare un ensemble de contrats de service qui définissent la façon d’interaction entre les modules logiciels. La protection de variations (Larman, 2001) ou de détails ne peut être achevée que si les clients dépendent seulement de l’interface. La minimisation des couplages avec les artefacts instables cachés derrière l’interface favorise l’isolation des parties complexes du système, la facilité de l’extension des fonctionnalités et la stabilité des clients en diminuant l’impact des changements inévitables des implémentations.
Les chercheurs et les experts ont proposé une variété de techniques de développement promouvant une meilleure utilisation de ce mécanisme. La programmation à l’interface et non aux implémentations proposée par Gamma et al. (1994) constitue un concept fondamental dans l’utilisation des interfaces. L’idée derrière cela est que les clients dépendent des interfaces et n’invoquent pas les variations isolées. Ce type de couplage est considéré comme stable parce que les clients connaissent uniquement les abstractions. Les clients sont privés de la création des objets dérivés de l’interface. Cette responsabilité peut être affectée à un patron créationnel tel que les fabriques dans GoF (Gamma et al., 1994).
Cependant, les défauts de conception dans les interfaces peuvent mener à une augmentation des coûts de développement, et à la dégradation de la qualité du code (Abdeen et al., 2013a). Le couplage des clients avec les implémentations est jugé coûteux (Izurieta et Bieman, 2013) et implique que ces derniers ne sont pas protégés aux changements qui se produisent au niveau des variations. Ce type de dépendance est instable, car il peut introduire des changements dans le système et augmente le nombre du couplage risqué. Dans la pratique, les développeurs des classes clients invoquent directement les implémentations des interfaces sans considérer les risques liés à l’ajout de telles dépendances sur la stabilité des conceptions. Puisque le contrôle d’accès strict au niveau de classe logicielle n’est pas couramment utilisé par les développeurs, le principe de masquage des détails est souvent non respecté (Rufiange et Fuhrman, 2014). En conséquence les clients ayant des couplages vers les implémentations peuvent être exposés aux changements et aux défauts de conception.
Dans la littérature, plusieurs définitions ont été accordées à la stabilité des logiciels. Selon Hassan (2007) et Alshayeb et al. (2011), ces définitions se répartissent en trois vues générales. La première approche définit la stabilité comme la résistance aux changements apportés au système. Le logiciel est stable lorsque deux versions consécutives sont quasiment similaires. Cette dernière a été proposée par Martin (1997) et Soong (1977).
La deuxième définition adoptée par Yau et Collofello (1980), Elish et Rine (2003) et Fayad et Altman (2001), indique qu’un système est stable lorsque ce dernier est capable de résister à la propagation des changements dans le cas d’ajout de nouveaux artefacts ou l’amélioration du code existant. Une classe est dite stable lorsqu’elle subit des modifications sans affecter le reste du système. Selon McConnell (2004), l’accommodation des changements dans une conception a pour objectif d’isoler les composants instables afin de limiter l’effet de leur changement. Dans le même contexte, la norme ISO-1926 (2001) décrit la stabilité des logiciels comme la sensibilité à apporter des changements sur un système et qui est un impact négatif dû à un autre changement. La troisième définition considère un système comme stable lorsqu’il n’y a aucun ajout de nouveaux composants ou modification du code existant, ou lorsqu’il y a seulement l’ajout de nouvelles fonctionnalités et sans faire des changements (Grosser et al. 2002;Jazayeri 2002).
D’autre part, les chercheurs ont proposé différentes approches basées sur des propriétés mesurables afin de faciliter l’analyse de la stabilité des conceptions et de réduire les efforts consacrés à la maintenance. Kelly (2006) a proposé une définition de la stabilité basée sur les mesures des caractéristiques (par exemple, le nombre de lignes du code source d’un module) de la conception. L’auteur a considéré qu’une caractéristique est stable lorsque les valeurs des métriques associées à cette dernière sont presque similaires entre les versions des logiciels .
Métriques de mesure de la stabilité
Les métriques logicielles sont des mesures quantitatives permettant d’analyser des propriétés spécifiques telles que le couplage, la cohésion, etc. Il existe dans la littérature différentes métriques suggérées pour mesurer la stabilité des architectures et des composants logiciels. Yau et Collofello (1980) ont proposé des mesures pour la stabilité de la conception et du code source des composants logiciels en ce qui concerne l’impact de leur modification sur le reste du système. Les métriques proposées ont considéré la complexité des modules logiciels selon leur degré de dépendances, la probabilité de leur modification et la portée des variables utilisées. Dans un même contexte, Arvanitou et al. (2015) ont proposé la métrique REM (Ripple Effect Measure) qui consiste à évaluer la prédisposition d’une classe logicielle aux changements propagés par d’autres classes. Martin (1997) a proposé des métriques de calcul de la stabilité liée aux architectures des logiciels. L’auteur a considéré les dépendances entre les paquetages dans la programmation orientée objet. Les mesures constituent les couplages afférents et efférents entre les modules, et le degré d’instabilité selon les deux types de couplage. Bansiya (2000) a proposé une série de mesures permettant d’évaluer la stabilité architecturale des cadriciels dont la programmation est orientée objet. L’auteur a considéré les caractéristiques structurelles liées au niveau des classes logicielles telles que le nombre des classes dans le système, le nombre des hiérarchies des classes, la moyenne de la profondeur de l’héritage dans la conception, etc. Tonu et al. (2006) ont présenté une approche basée sur des métriques afin d’évaluer la stabilité architecturale et dans quelle mesure une architecture logicielle peut supporter les changements. Les auteurs ont adopté une analyse rétrospective et prédictive dans leur expérimentation. Ils ont identifié que la complexité, la cohésion et le couplage sont des facteurs de contribution dans la stabilité des architectures .
Différentes métriques ont été aussi suggérées afin de mesurer le degré de la stabilité des classes logicielles dans leur évolution. Li et al. (2000) ont proposé une mesure de l’instabilité d’une classe entre deux versions d’un système. La métrique concerne le pourcentage du changement effectué dans le code source des classes. Grosser et al. (2003) ont pris en considération la stabilité d’une classe selon le changement au niveau des méthodes de l’interface qu’elle implémente. Rapu et al. (2004) ont proposé de mesurer la stabilité des classes selon les méthodes ajoutées ou supprimées. Les auteurs ont considéré une classe stable lorsque le nombre de méthodes ne change pas entre les versions d’un logiciel. Alshayeb et al. (2011) ont identifié une liste de propriétés des classes qui peuvent affecter leur stabilité. Ils ont considéré les changements dans les variables et leurs modificateurs, les signatures des méthodes, les noms des interfaces et des classes héritées.
Les mesures quantitatives sont des techniques d’évaluation définies afin de calculer le degré de la stabilité des artefacts logiciels et de déterminer si un composant sera exposé aux changements dans un sens prédictif. En outre, les chercheurs ont proposé des mécanismes et des principes de conception promouvant plus de stabilité des structures logicielles et la réduction de l’impact des changements dans l’évolution des systèmes.
INTRODUCTION |