Ce livre constitue une introduction au génie logiciel, adoptant un point de vue essentiellement technique. Il s'agit d'un support de cours à l'École Nationale Supérieure de Cognitique.
Un projet écrit en langage C# et utilisant la technologie WinForms illustre certaines notions d'architecture et de test présentées dans ce livre. Son code source est disponible en ligne.
Ce livre est publié sous la licence Creative Commons BY-NC-SA. Son code source est disponible sur GitHub. N'hésitez pas à contribuer à son amélioration en utilisant les issues pour signaler des erreurs et les pull requests pour proposer des ajouts ou des corrections.
I. Le Génie Logiciel : Une Introduction
Le génie logiciel (software engineering) représente l'application de principes d'ingénierie au domaine de la création de logiciels. Il consiste à identifier et à utiliser des méthodes, des pratiques et des outils permettant de maximiser les chances de réussite d'un projet logiciel. Il s'agit d'une science récente dont l'origine remonte aux années 1970.
À cette époque, l'augmentation de la puissance matérielle a permis de réaliser des logiciels plus complexes, mais souffrant de nouveaux défauts : délais non respectés, coûts de production et d'entretien élevés, manque de fiabilité et de performances.
Le génie logiciel vise à rationaliser et à optimiser le processus de production d'un logiciel. Comme tout projet, la réalisation d'un logiciel est soumise à des exigences contradictoires et difficilement conciliables (triangle coût-délai-qualité). Sans être une solution miracle, le génie logiciel a pour objectif de maximiser la surface du triangle en tenant compte des priorités du client.
Le génie logiciel couvre l'ensemble du cycle de vie d'un logiciel.
II. Architecture Logicielle : L'Art de Construire des Logiciels
Le Petit Robert définit l'architecture comme étant « l'art de construire les édifices ». Ce mot est avant tout lié au domaine du génie civil : on pense à l'architecture d'un monument ou encore d'un pont. Par analogie, l'architecture logicielle peut être définie comme étant « l'art de construire les logiciels ».
Dans le domaine du génie civil, on n'imagine pas se lancer dans la construction d'un bâtiment sans avoir prévu son apparence, étudié ses fondations et son équilibre, choisi les matériaux utilisés, etc. Cette problématique se retrouve dans le domaine informatique. Comme un bâtiment, un logiciel est fait pour durer dans le temps. Il est presque systématique que des projets informatiques aient une durée de vie de plusieurs années. Plus encore qu'un bâtiment, un logiciel va, tout au long de son cycle de vie, connaître de nombreuses modifications qui aboutiront à la livraison de nouvelles versions, majeures ou mineures.
Exemple : le logiciel VLC n'était à l'origine qu'un projet étudiant destiné à diffuser des vidéos sur le campus de l'École Centrale de Paris. Sa première version remonte à l'année 2001.
Dans le domaine du génie civil, les objectifs de l'architecture sont que le bâtiment construit réponde aux besoins qu'il remplit, soit robuste dans le temps et (notion plus subjective) agréable à l'œil. L'architecture logicielle poursuit les mêmes objectifs. Le logiciel créé doit répondre aux besoins et résister aux nombreuses modifications qu'il subira au cours de son cycle de vie. Contrairement à un bâtiment, un logiciel mal pensé ne risque pas de s'effondrer.
Il n'existe pas de vrai consensus concernant le sens des mots « architecture » et « conception » dans le domaine du développement logiciel. Ces deux termes sont souvent employés de manière interchangeable. Il arrive aussi que l'architecture soit appelée « conception préliminaire » et la conception proprement dite « conception détaillée ».
L'architecture logicielle (software architecture) considère le logiciel de manière globale.
- que fait-il ?
- quelles sont les sous-parties qui le composent ?
- interagissent-elles ?
- sous quelle forme sont stockées ses données ?
- etc.
- quel logiciel est utilisé pour stocker les données ?
- comment est organisé le code ?
- etc.
Tout logiciel, au-delà d'un niveau minimal de complexité, est un édifice qui mérite une phase de réflexion initiale pour l'imaginer dans ses grandes lignes. Cette phase correspond à l'activité d'architecture. Selon le niveau de complexité du logiciel, l'activité d'architecture peut être une simple formalité ou bien un travail de longue haleine.
L'activité d'architecture intervient traditionnellement vers le début d'un projet logiciel, dès le moment où les besoins auxquels le logiciel doit répondre sont suffisamment identifiés. Les évolutions d'un projet logiciel peuvent nécessiter de nouvelles phases d'architecture tout au long de sa vie.
De manière très générale, un logiciel sert à automatiser des traitements sur des données.
- gérer les interactions avec l'extérieur, en particulier l'utilisateur : saisie et contrôle de données, affichage.
- effectuer sur les données des opérations (calculs) en rapport avec les règles métier (« business logic »).
- accéder aux informations qu'il manipule et les stocker, notamment entre deux utilisations.
Résultat de l'activité du même nom, l'architecture d'un logiciel décrit sa structure globale, son squelette. Elle décrit les principaux éléments qui composent le logiciel, ainsi que les flux d'échanges entre ces éléments. On peut décrire l'architecture d'un logiciel selon différents points de vue. Entre autres, une vue logique mettra l'accent sur le rôle et les responsabilités de chaque partie du logiciel.
III. Principes Clés du Génie Logiciel
Le principe de séparation des responsabilités (separation of concerns) vise à organiser un logiciel en plusieurs sous-parties, chacune ayant une responsabilité bien définie. Ainsi construite de manière modulaire, l'application sera plus facile à comprendre et à faire évoluer. Au moment où un nouveau besoin se fera sentir, il suffira d'intervenir sur la ou les sous-partie(s) concernée(s). Le reste de l'application sera inchangé : cela limite les tests à effectuer et le risque d'erreur. Le principe de responsabilité unique (single responsibility principle) stipule quant à lui que chaque sous-partie atomique d'un logiciel (exemple : une classe) doit avoir une unique responsabilité (une raison de changer) ou bien être elle-même décomposée en sous-parties. « A class should have only one reason to change » (Robert C.
Un bâtiment s'édifie à partir de morceaux de base, par exemple des briques ou des moellons. Longtemps, l'informatique a gardé un côté artisanal : chaque programmeur recréait la roue dans son coin pour les besoins de son projet. Mais nous sommes passés depuis plusieurs années à une ère industrielle. Des logiciels de plus en plus complexes doivent être réalisés dans des délais de plus en plus courts, tout en maintenant le meilleur niveau de qualité possible. Une réponse à ces exigences contradictoires passe par la réutilisation de briques logicielles de base appelées bibliothèques, modules ou plus généralement composants. En particulier, la mise à disposition de milliers de projets open source via des plates-formes comme GitHub ou des outils comme NuGet, Composer ou npm a permis aux équipes de développement de faire des gains de productivité remarquables en intégrant ces composants lors de la conception de leurs applications. À l'heure actuelle, il n'est pas de logiciel de taille significative qui n'intègre plusieurs dizaines, voire des centaines de composants externes. Déjà testé et éprouvé, un composant logiciel fait simultanément baisser le temps et augmenter la qualité du développement.
La définition du couplage est la suivante : « une entité (sous-partie, composant, classe, méthode) est couplée à une autre si elle dépend d'elle », autrement dit, si elle a besoin d'elle pour fonctionner. Au sein d'une application, un couplage fort tisse entre ses éléments des liens puissants qui la rend plus rigide à toute modification (on parle de « code spaghetti »). À l'inverse, un couplage faible permet une grande souplesse de mise à jour. Un élément peut être modifié (exemple : changement de la signature d'une méthode publique) en limitant ses impacts. Le principe de responsabilité unique permet de limiter le couplage au sein de l'application : chaque sous-partie a un rôle précis et n'a que des interactions limitées avec les autres sous-parties.
Ce principe recommande de placer ensemble des éléments (composants, classes, méthodes) ayant des rôles similaires ou dédiés à une même problématique.
DRY est l'acronyme de Don't Repeat Yourself. Ce principe vise à éviter la redondance au travers de l'ensemble de l'application. Cette redondance est en effet l'un des principaux ennemis du développeur. La redondance peut se présenter à plusieurs endroits d'une application, parfois de manière inévitable (réutilisation d'un existant). Le principe DRY est important, mais ne doit pas être appliqué de manière trop zélée. Vouloir absolument éliminer toute forme de redondance conduit parfois à créer des applications inutilement génériques et complexes.
KISS est un autre acronyme signifiant Keep It Simple, Stupid et que l'on peut traduire par « Ne complique pas les choses ». Il part du constat que la complexité entraîne des surcoûts de développement puis de maintenance, pour des gains parfois discutables.
Ce troisième acronyme signifie You Ain't Gonna Need It. Corollaire du précédent, il consiste à ne pas se baser sur d'hypothétiques évolutions futures pour faire les choix du présent, au risque d'une complexification inutile (principe KISS). Il faut réaliser l'application au plus simple et en fonction des besoins actuels.
IV. Architectures et Patrons de Conception
Au fil du temps et des projets, plusieurs architectures-types se sont dégagées.
Le principal avantage de l'architecture client/serveur tient à la centralisation des données. Stockées à un seul endroit, elles sont plus faciles à sauvegarder et à sécuriser. Le serveur qui les héberge peut être dimensionné pour pouvoir héberger le volume de données nécessaire et répondre aux sollicitations de nombreux clients. Le fonctionnement en mode client/serveur est très souvent utilisé en informatique.
Une architecture en couches organise un logiciel sous forme de couches (layers).
Une architecture orientée services (SOA, Service-Oriented Architecture) décompose un logiciel sous la forme d'un ensemble de services métier utilisant un format d'échange commun, généralement XML ou JSON. Une variante récente, l'architecture microservices, diminue la granularité des services pour leur assurer souplesse et capacité à évoluer, au prix d'une plus grande distribution du système. L'image ci-dessous ( source) illustre la différence entre ces deux approches.
La partie Modèle qui regroupe la logique métier (« business logic ») ainsi que l'accès aux données. La partie Contrôleur qui gère la dynamique de l'application. Ce patron a été imaginé à la fin des années 1970 pour le langage Smalltalk afin de bien séparer le code de l'interface graphique de la logique applicative. Le diagramme ci-dessous (extrait de la documentation du framework PHP Symfony) résume les relations entre les composants d'une architecture web MVC. Attention à ne pas employer le terme de « couche » à propos d'une architecture MVC. Dans une architecture en couches, chaque couche ne peut communiquer qu'avec les couches adjacentes.
La patron Modèle-Vue-Présentation, ou MVP, est un proche cousin du patron MVC surtout utilisé pour construire des interfaces utilisateurs (UI). Dans une architecture MVP, la partie Vue reçoit les évènements provenant de l'utilisateur et délègue leur gestion à la partie Présentation. Dans la variante dite Passive View de cette architecture, la Vue est passive et dépend totalement du contrôleur pour ses mises à jour.
Un patron de conception (design pattern) est une solution standard à un problème de conception. L'ensemble des patrons de conception constitue un catalogue de bonnes pratiques issu de l'expérience de la communauté. Leurs noms forment un vocabulaire commun qui permet d'identifier immédiatement la solution associée. Les patrons de conception ont été popularisés par le livre Design Patterns - Elements of Reusable Object-Oriented Software sorti en 1995 et coécrit par quatre auteurs (le Gang Of Four, ou GoF). Chaque patron décrit un problème à résoudre puis les éléments de sa solution, ainsi que leurs relations. Il existe également un catalogue d'antipatterns. Comme son nom l'indique, un antipattern est un exemple de mauvaise pratique à ne surtout pas suivre. Certains patterns originaux du GoF sont maintenant plutôt considérés comme des antipatterns. C'est par exemple, le cas du pattern Singleton (explications).
V. Qualité du Code Source
Le code source est le cœur d'un projet logiciel. Il est essentiel que tous les membres de l'équipe de développement se coordonnent pour adopter des règles communes dans la production de ce code. L'objectif de ces règles est l'uniformisation de la base de code source du projet.
Une première série de règles concerne le nommage des différents éléments qui composent le code. La convention la plus fréquemment adoptée se nomme camelCase (ou parfois lowerCamelCase).
public void UneMethode(int monParam1, int monParam2) { ... public void UneAutreMethode(string encoreUnParametre) { ...
On peut ajouter à cette convention une règle qui impose d'utiliser le pluriel pour nommer les éléments contenant plusieurs valeurs, comme les tableaux et les listes.
La langue utilisée dans la production du code doit bien entendu être unique sur tout le projet. Le français (idClientSuivant) et l'anglais (nextClientId) ont chacun leurs avantages et leurs inconvénients.
La grande majorité des IDE et des éditeurs de code offrent des fonctionnalités de formatage automatique du code. Sous Visual Studio, la commande de formatage automatique du code est Edition->Avancé->Mettre le document en forme.
L'ajout de commentaires permet de faciliter la lecture et la compréhension d'une portion de code source. Il n'y a pas de règle absolue ni de consensus, en matière de taux de commentaires dans le code source. Dans un premier temps, il vaut mieux se montrer raisonnable et commenter les portions de code complexes ou essentielles : en-têtes de classes, algorithmes importants, portions atypiques, etc. Il faut éviter de paraphraser le code source en le commentant, ce qui alourdit sa lecture et n'est d'aucun intérêt.
VI. Cycle de Vie d'un Projet Logiciel
Nous avons déjà mentionné qu'un projet logiciel d'entreprise a une durée de vie de plusieurs années et subit de nombreuses évolutions au cours de cette période. On rencontre souvent le besoin de livrer de nouvelles versions qui corrigent des bogues ou apportent de nouvelles fonctionnalités. Le code source du logiciel « vit » donc plusieurs années. En entreprise, seule une petite minorité de logiciels so...
TAG: #Spaghetti