Upload
cyrilgandon
View
608
Download
1
Embed Size (px)
DESCRIPTION
Citation preview
Qualité logicielleCyril Gandon
Introduction
Vous savez coder : lire et écrire un fichier, afficher un résultat à l’écran,
lancer plusieurs Threads, manipuler des listes, etc.
Etes vous attentifs à la qualité de votre code ?
Un logiciel ne se résume pas à ses fonctionnalités
Son potentiel de croissance est au moins aussi important que ses
fonctionnalités
Un code de qualité est tourné vers le futur : maintenable, modulable
Objectifs
Etre capable de reconnaitre le manque de qualité d’un code
Etre capable d’appliquer les méthodes de refactoring pour augmenter la
qualité
Comprendre les enjeux de la qualité logicielle
Connaitre les outils applicables pour écrire un code de qualité
Sommaire
1. Mesure de la qualité
2. Le Refactoring
3. Les principes S.O.L.I.D.
4. Initiation à la programmation fonctionnelle
5. Analyse statique
6. Programmation par contrat
1. Mesure de la qualité
La théorie : Norme ISO 9126 Software
engineering — Product quality
Functionality : The existence of a set of functions and their specified properties. The functions are those that satisfy stated or implied needs.
Suitability, Accuracy, Interoperability, Security, Functionality Compliance
Reliability : The capability of software to maintain its level of performance under stated conditions for a stated period of time.
Maturity, Fault Tolerance, Recoverability, Reliability Compliance
Usability : The effort needed for use, and on the individual assessment of such use, by a stated or implied set of users.
Understandability, Learnability, Operability, Attractiveness, Usability Compliance
Efficiency : The relationship between the level of performance of the software and the amount of resources used, under stated conditions.
Time Behaviour, Resource Utilization, Efficiency Compliance
Maintainability : The effort needed to make specified modifications.
Analyzability, Changeability, Stability, Testability, Maintainability Compliance
Portability : The ability of software to be transferred from one environment to another.
Adaptability, Installability, Co-Existence, Replaceability, Portability Compliance
Source : http://en.wikipedia.org/wiki/ISO/IEC_9126
En pratique
Fonctionnel
Maintenable
KISS : Keep It Simple Stupid
YAGNI : You Ain’t Gonna Need It
DRY : Don’t Repeat Yourself
Performant (Théorie de la complexité)
Robuste (Parallélisable, Portable)
Testable (Via les tests unitaires)
Fonctionnel
Développer les fonctionnalités demandées
Maintenable
Simplicité, simplicité, simplicité
Lisibilité
Pas de code spaghetti
Ne soyez pas un « Astronaute Architecte » = Ne surdésigner pas le model
Ne vous répétez pas, n’écrivez jamais deux fois le même code
Performant
Soyez conscient de la complexité de votre algorithme
O(1) > O(log n) > O(n) > O(n log n) > O(n²)
Robuste
Comment réagi votre logiciel « au bord »
Et si le réseau tombe ?
Et si l’utilisateur rentre de mauvaises données ?
Et si les données reçues sont dans un mauvais format ?
Fail fast => Valider toujours et échouer rapidement
Testable
La couverture des tests unitaires doit approcher les 100%
Réduit les dépendances
Diminue les régressions
2. Le Refactoring
Le refactoring, c’est quoi ?
Définition : la modification du code d’un logiciel dans le but de le rendre
plus simple à comprendre et à modifier, sans changer son comportement
observable
Simplifier le code
Accroitre sa lisibilité
Un code est écrit 1 fois et lu 10 fois
80% du temps est passé à la maintenance, donc la relecture
Gagner en robustesse
Du temps investi aujourd’hui pour une maintenance plus simple demain
Ne modifie pas les fonctionnalités existantes
(Re)Factorisation d’une identité
remarquable
a² + 2ab + b²
a = 2, b = 4
2² + 2 * 2 * 4 + 4² = ?
(a + b)²
a = 2, b = 4
(2 + 4)² = 36
Références
http://refactoring.com
http://sourcemaking.com/refactoring
Quelques principes
1. Renommage
2. Découper les fonctions
3. Réduire les paramètres
4. Réduire le scope des variables
5. Rendre les objets immutables
6. Eviter les négations et supprimer les doubles négation
7. Introduire des variables temporaires pour des expressions complexes
8. Ne pas utiliser la même variable pour autres chose
9. Séparer les niveaux (layers) d’abstraction
10.Loi de Demeter
1. Renommage
Un bon nom doit être dicible par téléphone
Notation PascalCase pour les classes et méthodes, camelCase pour les
variables (en C#)
Doit décrire précisément l’action ou l’état représenté
Suivre les conventions de nommages de son langage (Java != C# != PHP),
ou de son entreprise si elles existent
The name of a method does not reveal its purpose.
Change the name of the method.
2. Découper les fonctions
Diviser pour régner, mieux vaut plusieurs petites fonctions qu’une seule
grosse
Une fonction ne devrait jamais dépasser quelques dizaines de lignes
void printOwing()
{
printBanner(); //print details
System.out.println ("name: " + _name);
System.out.println ("amount " +
getOutstanding());
}
void printOwing()
{
printBanner();
printDetails(getOutstanding());
}
void printDetails (double outstanding)
{
System.out.println ("name: " + _name);
System.out.println ("amount " + outstanding);
}
3. Réduire le scope des variables
Une variable doit être déclarer au plus proche de son utilisation, dans le
scope (contexte) le plus faible
Permet de suivre l’algorithme plus facilement. Le cerveau ne peut pas
retenir plus de 6-7 variables
Implique d’éviter au maximum les variables static qui ont un scope global à
toute l’application
3. Réduire le scope des variables
Scope de iScope de i
public int Foo(int j){
int i = 2;int a;if (j > 3){
a = i * i;}else{
a = 0;}return a;
}
public int Foo(int j){
int a;if (j > 3){
int i = 2;a = i * i;
}else{
a = 0;}return a;
}
4. Réduire le nombre de paramètres
Le nombre de paramètres d’une méthode ne devrait jamais dépasser 3 ou
4
Grouper ensemble les paramètres qui ont un sens commun
You have a group of parameters that naturally go together.
Replace them with an object.
5. Rendre les objets immutables
Un objet sans setters (immutable) est plus simple à manipuler et plus
sécurisant (CF la programmation fonctionnelle)
Peut être passer à des tiers sans effet de bords
Laisser les mutants à la science fiction, pas à la science informatique
5. Rendre les objets immutables
public class MyPoint{
private double _x;
public double X{
get { return _x; }set { _x = value; }
}private double _y;
public double Y{
get { return _y; }set { _y = value; }
}
public MyPoint(double x, double y){
this.X = x;this.Y = y;
}}
public class MyPoint{
private readonly double _x;
public double X{
get { return _x; }}private readonly double _y;
public double Y{
get { return _y; }}
public MyPoint(double x, double y){
this._x = x;this._y = y;
}}
6. Eviter les négations et les doubles
négation
La lecture est toujours plus simple dans le sens positif que dans le sens
négatif
if(!this.NotFound())
double foo;if(this.HasValue == false)
foo = 0;else
foo = 1;
double foo;if(this.HaveValue)
foo = 1;else
foo = 0;
if(this.Found())
7. Introduire des variables temporaires
pour des expressions complexes
Condenser un trop plein d’informations dans une variable nommée
if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
wasInitialized() && resize > 0
)
{
// do something
}
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized)
{
// do something
}
8. Ne pas utiliser la même variable pour
2 concepts différents
Garder en tête la signification d’une variable est difficile, encore plus si elle
change en cours de route
Mieux vaut créer une nouvelle variable
L’idéal est qu’une variable doit être assignée une seule fois (paradigme de
la programmation fonctionnelle)
double temp = 2 * (_height + _width);
System.out.println (temp);
temp = _height * _width;
System.out.println (temp);
final double perimeter = 2 * (_height + _width);
System.out.println (perimeter);
final double area = _height * _width;
System.out.println (area);
9. Séparer les niveaux (layers)
d’abstraction
Identifier et séparer les différentes couches de l’application
Généralement se sont les suivantes
Couche métier (algorithme, logique = BML, Buisness Model Layer)
Couche de présentation (Vue/View)
Couche d’accès aux données (DAL, Data Access Layer)
Voir le Pattern MVC pour un tel découpage (Model Vue Controller)
10. Loi de Demeter
L’antipattern : Feature Envy
var c = a.GetB().GetC();
Augmente le couplage
Définition : Ne parlez qu’à vos amis directs
Vous pouvez jouer avec vous même
Vous pouvez jouer avec vos jouets
Vous pouvez jouer avec les jouets que l’on vous donne
Vous pouvez jouer avec les jouets que vous avez fabriqués
10. Loi de Demeter
var employees = someObject.GetEmployeeList();employees.AddElementWithKey(employee.GetKey(), employee);
someObject.AddToEmployees(employee);
Définition : soit la méthode M de l’objet O. La méthode M à la droit
d’invoquer les méthodes de :
O lui-même
Des attributs / propriétés de O
Des paramètres de M
Des objets instanciés dans M
Conclusion & Problématique
Le refactoring n’ajoute pas de fonctionnalité, ne corrige pas de bugs
Comment vendre du temps d’improductivité à votre entreprise ?
Source : http://dilbert.com/strips/comic/2007-11-26/
Conclusion
Le refactoring est un investissement toujours payant, à vous de savoir le
vendre
Le refactoring est une partie importante des méthodes agiles => Coder
vite, livrer vite, refactoriser, boucler
Fonctionne de pair avec les tests unitaires
3. Les principes S.O.L.I.D.
C’est quoi SOLID ?
Les pratiques SOLID permettent d’avoir des outils pour construire des
logiciels de qualité
Introduites par Robert C. Martin en 2000 dans son article « Design Principles
and Design Patterns »
SOLID est un acronyme de cinq lettres représentant pour chaque lettre 1
principe à respecter
S.O.L.I.D.
Single responsibility principle
Open/closed principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle
Single responsibility principle
Un objet doit avoir une seule responsabilité
Un objet doit avoir une seule raison de changer
Mauvais exemple
Lit un fichier
Parse et valide les données
Non testable unitairement
public class Fruit{
public int Id { get; set; }public string Name { get; set; }
public static IEnumerable<Fruit> FruitsFromFile(string path){
// 0;Apple// 1;Orange// 2;Bananausing (var reader = new StreamReader(path)){
string line;while ((line = reader.ReadLine()) != null){
var words = line.Split(';');int id = Convert.ToInt32(words[0]);string name = words[1];yield return new Fruit() { Id = id, Name = name };
}}
}}
Séparer les responsabilités
// Représente un fruit dans un fichierpublic class FruitFile{
public string Id { get; set; }public string Name { get; set; }
public static IEnumerable<FruitFile> FruitsFromFile(stringpath)
{using (var reader = new StreamReader(path)){
string line;while ((line = reader.ReadLine()) != null){
var words = line.Split(';');yield return new FruitFile()
{ Id = words[0], Name = words[1] };}
}}
}
public class Fruit{
public int Id { get; set; }public string Name { get; set; }
// Valide les données d'une ligne fruitpublic static Fruit Build(FruitFile fruitFile){
int id = Convert.ToInt32(fruitFile.Id);string name = fruitFile.Name;return new Fruit() { Id = id, Name = name };
}
public static IEnumerable<Fruit> FromFile(string path){
return FruitFile.FruitsFromFile(path).Select(fruitFile => Build(fruitFile));
}}
La validation des données devient testable unitairement
Open/closed principle
Un objet doit être ouvert à l’extension mais fermer à la modification
Open/closed principle
Ouvert à l’extension = Possibilité de rajouter des fonctionnalités
Fermé à la modification = Ajout des fonctionnalités sans changer le code
existant
Mécanisme : Héritage, Polymorphisme, Interface (Abstraction)
Mauvais exemple
public class Rectangle{
public double Width { get; set; }public double Height { get; set; }
public double Area { get { return this.Width * this.Height; } }}
public class Circle{
public PointF Center { get; set; }public double Radius { get; set; }
public double Area{
get { return this.Radius * this.Radius * Math.PI; }}
}
public class Shapes{
public double AreaOf(List<object> shapes){
double total = 0;foreach (var shape in shapes){
if (shape is Rectangle){
total += ((Rectangle)shape).Area;}else if (shape is Circle){
total += ((Circle)shape).Area;}
}return total;
}}
Obligation de modifier la fonction si on ajoutait un triangle
Ajout d’un niveau d’abstraction
// Abstract the Shape conceptpublic interface IShape { double Area { get; } }
public class Rectangle : IShape{
public double Width { get; set; }public double Height { get; set; }
public double Area { get { return this.Width * this.Height; } }}
public class Circle : IShape{
public PointF Center { get; set; }public double Radius { get; set; }
public double Area{
get { return this.Radius * this.Radius * Math.PI; }}
}
public class Shapes{
// Can run with abstract list of shapespublic double AreaOf(IEnumerable<IShape> shapes){
double total = 0;foreach (var shape in shapes){
total += shape.Area;}return total;
}}
Le code est extensible pour n’importe quel forme
Liskov substitution principle
Une classe doit pouvoir être remplacée par une instance d'une des ses
sous-classes, sans modifier la validité du programme
Liskov substitution principle
Une sous classe ne doit pas être plus contraignante que sa super classe
Ce qui peut rentrer dans la sous classe doit être moins contraignant que ce
qui peut rentrer dans la super classe
Ce qui peut sortir de la sous classe doit être plus contraignant que ce qui
peut sortir de la super classe
Mauvais exemple
public class Fish { }public class Duck{
public virtual void Give(Fish food){
Console.WriteLine("Eating the fish");}
public virtual int LegCount { get { return 2; } }}
public class RubberDuck : Duck{
public override void Give(Fish food){
throw new InvalidOperationException("Rubber Duckdoesn't need fishes");
}
public override int LegCount { get { return 0; } }}
Le canard en plastique ne sait pas manger un poisson, il est plus
contraignant sur ce qui rentre
Les canards devrait toujours avoir des pattes
Mauvais exemple
Un canard en plastique n’est pas un canard
La modélisation est incorrecte, et viole le principe de Liskov
On ne peut pas supposer un comportement si le principe de Liskov n’est
pas respecté
public class Test{
public void Main(){
var duck = new RubberDuck();var fish = new Fish();this.GiveFood(duck, fish);
}
public void GiveFood(Duck duck, Fish fish){
// we are expecting that is will never fail// unless we violate the Liskov Principleduck.Give(fish);
}}
Interface Segregation Principle
Il vaut mieux avoir plusieurs interfaces spécifiques plutôt qu'une seule
grande interface
Mauvais exemple
// Big interface, so much workpublic interface ICommunicate{
void Send(object data);object Receive();
}
// Phone is ok, can communicate both wayspublic class Phone : ICommunicate{
public void Send(object data){
Console.WriteLine("Composingnumber...");
}
public object Receive(){
return "Dring!";}
}
public class Printer : ICommunicate{
public void Send(object data){
Console.WriteLine("Printing datas...");}
// Printer has to define the Receive methodpublic object Receive(){
// But printer can't receive anythingthrow new InvalidOperationException("Printer
are not receiving.");}
}
Bon exemple
// make it two interfaces instead of onepublic interface ISender { void Send(object data); }public interface IReceiver { void Send(object data); }public class Phone : ISender, IReceiver{
public void Send(object data){
Console.WriteLine("Composing number...");}
public object Receive(){
return "Dring!";}
}
public class Printer : ISender{
public void Send(object data){
Console.WriteLine("Printing datas...");}
}
Dependency Inversion Principle
Les modules de haut niveau ne doivent pas dépendre des modules de bas
niveau. Les deux doivent dépendre des abstractions
Mauvais exemple
// Low level modulepublic class Logger{
public void Log(string text){
Console.WriteLine(text);}
}
// High level module, depend on low level modulepublic class DatabaseReader{
public Logger Logger { get; set; }public void ReadData(){
this.Logger.Log("Reading datas...");}
}
Bon exemple
// Low level modules, depend on abstractionpublic class ConsoleLogger : ILogger{
public void Log(string text){
Console.WriteLine(text);}
}
// Low level modules, depend on abstractionpublic class FileLogger : ILogger{
private readonly string _path;public FileLogger(string path){
this._path = path;}public void Log(string text){
using (var file = newStreamWriter(this._path))
{file.WriteLine(text);
}}
}
// High level module, depend on abstractionpublic class DatabaseReader{
public ILogger Logger { get; set; }public void ReadData(){
this.Logger.Log("Reading datas...");}
}
// Abstractionpublic interface ILogger{
void Log(string text);}
Conclusion
Appliquer ces principes ne garantissent pas un code sans faille, mais au
moins un meilleur code
Il faut savoir pondérer ces bonnes pratiques lorsque les délais sont courts et
les résultats pressants
Les définitions des principes sont simples, mais leurs mises en place
complexe sur des problèmes réels
4. Initiation à la
programmation fonctionnelle
Historique
Programmation procédurale : Fortran, C, Basic…
A base de fonction qui s’appelle les unes les autres
Ensemble d’étape à exécuter pour arriver au résultat
Programmation orientée objet : C++, Java, C#
Définition d’un ensemble d’objet interagissant entre eux
Un objet est définit par ses états (variables) et ses comportements (méthodes)
Grande mode à partir des années 80, adopté en masse par les entreprises dans
les années 90
Historique
Aujourd’hui : le marché est toujours dominé par la programmation orienté
objet, et les langages procéduraux gardent une bonne place
Source : http://www.tiobe.com
Historique de la programmation
fonctionnelle
Programmation fonctionnelle : Lisp (1958), puis Scheme (1975), Haskell
(1987), ou récemment F# (2002) et LINQ (2007)
N’a jamais réussi à pénétrer le marché malgré son ancienneté
Peu enseigné, peu pratiqué, peu utilisé
A quoi ça sert alors ?
Simple à écrire, condensé
Puissant
Pas d’effet de bords par nature
Les principes de la programmation fonctionnelle sont applicables à la
programmation orienté objet
Rend toute fonction parallélisable
Rend possible la mise en cache de manière automatique
Paradigmes
Aucun état n’est modifiable, on parle d’Immutabilité
Un objet est instancié une fois, puis ne peux plus jamais être modifié
Les fonctions sont Pures, pas d’effet de bords = aucune modification d’état
Une fonction pure est une fonction qui ne modifie aucun état, et qui retourne
une seule valeur
La valeur retournée est toujours la même pour les mêmes arguments d’entrée.
Contrexemple : Date date = new Date(); // Impur
Immutabilité
Sur des objets, l’initialisation passe par le constructeur
public class ImmutablePoint{
private readonly double _x;public double X { get { return _x; } }
private readonly double _y;public double Y { get { return _y; } }
public ImmutablePoint(double x, double y){
this._x = x;this._y = y;
}}
Immutabilité
Sur des conteneurs de données, chaque ajout retourne une nouvelle
instance du conteneur
var empty = new ImmutableList<double>();var onlyTwo = empty.Add(2);var twoAndThree = onlyTwo.Add(3);
Avantage de l’immutabilité
Les variables ne pourront jamais être modifié par un code tiers
public void EvilCode(List<int> ints){
ints.Clear();}
var primes = new List<int>() { 2, 3, 5, 7};EvilCode(primes);// ergh, primes is empty!
La programmation multi thread est simplifiée
Pas de Lock, Mutex, et autres joyeusetés
Les accès aux données sont automatiquement en lecture seule
Avantage de l’immutabilité
Exemple : un tas immutable
public interface IStack<T> : IEnumerable<T>{
IStack<T> Push(T value);IStack<T> Pop();T Peek();bool IsEmpty { get; }
}
Exemple : un tas immutablepublic sealed class Stack<T> : IStack<T>{
private sealed class EmptyStack : IStack<T>{
public bool IsEmpty { get { return true; } }public T Peek() { throw new Exception("Empty stack"); }public IStack<T> Push(T value) { return new Stack<T>(value, this); }public IStack<T> Pop() { throw new Exception("Empty stack"); }public IEnumerator<T> GetEnumerator() { yield break; }IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}private static readonly EmptyStack empty = new EmptyStack();public static IStack<T> Empty { get { return empty; } }private readonly T head;private readonly IStack<T> tail;private Stack(T head, IStack<T> tail){
this.head = head;this.tail = tail;
}public bool IsEmpty { get { return false; } }public T Peek() { return head; }public IStack<T> Pop() { return tail; }public IStack<T> Push(T value) { return new Stack<T>(value, this); }public IEnumerator<T> GetEnumerator(){
for (IStack<T> stack = this; !stack.IsEmpty; stack = stack.Pop())yield return stack.Peek();
}IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
Exemple : un tas immutable
IStack<int> s1 = Stack<int>.Empty;IStack<int> s2 = s1.Push(10);IStack<int> s3 = s2.Push(20);IStack<int> s4 = s2.Push(30); // shares its tail with s3.
Source : http://blogs.msdn.com/b/ericlippert/archive/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack.aspx
LINQ : Application au monde réel
LINQ arrive en 2007 avec le Framework .NET 3.5
Ensemble de fonctions inspiré de la programmation fonctionnelle qui agit
sur des types IEnumerable (basiquement, des List)
Un peu de LINQ : filtrer les éléments
d’une liste
Avant
public List<int> MutableEvens(List<int> ints){
var evens = new List<int>();foreach (var item in ints){
if (item % 2 == 0){
// mutation of the listevens.Add(item);
}}return evens;
}
Après
public List<int> ImmutableEvens(List<int> ints){
return ints.Where(i => i % 2 == 0).ToList();}
Aucune mutation, sans effet de bords
Un peu de LINQ : sélectionner une
propriété
Avant Après
public List<int> MutableGetXs(List<Point> points){
var xs = new List<int>();foreach (var point in points){
xs.Add(point.X);}return xs;
}
public List<int> ImmutableGetXs(List<Point> points){
return points.Select(p => p.X).ToList();}
Exercice
Refactoriser le code suivant pour le rendre immutable
public class MyPoint{
public double X { get; set; }public double Y { get; set; }
public static void Translate(List<MyPoint> points, double dx, double dy)
{foreach (var point in points){
point.X += dx;point.Y += dy;
}}
public override bool Equals(object obj){
var point = (MyPoint)obj;return point.X == this.X && point.Y ==
this.Y;}
}
[TestClass]public class MyPointTests{
[TestMethod]public void TestTranslate(){
var points = new List<MyPoint>() { new MyPoint() { X = 1, Y = 2 }, new MyPoint() { X = -1, Y = 0 }
};MyPoint.Translate(points, 1, 2);
var expected = new List<MyPoint>() { new MyPoint() { X = 2, Y = 4 }, new MyPoint() { X = 0, Y = 2 }
};CollectionAssert.AreEqual(expected, points);
}}
Correctionpublic class MyPoint{
private readonly double _x;public double X { get { return _x; } }
private readonly double _y;public double Y { get { return _y; } }
public MyPoint(double x, double y){
this._x = x;this._y = y;
}
public static List<MyPoint> Translate(List<MyPoint> points, double dx, double dy)
{return points.Select(p => new MyPoint(p.X + dx,
p.Y + dy)).ToList();}
public override bool Equals(object obj){
var point = (MyPoint)obj;return point.X == this.X && point.Y == this.Y;
}}
[TestClass]public class MyPointTests{
[TestMethod]public void TestTranslate(){
var points = new List<MyPoint>() {
new MyPoint(1, 2), new MyPoint(-1, 0)
};var actual = MyPoint.Translate(points, 1, 2);
var expected = new List<MyPoint>() {
new MyPoint(2, 4), new MyPoint(0, 2)
};
CollectionAssert.AreEqual(expected, actual);}
}
Conclusion
Les concepts de pureté et d’immutabilité proviennent de la
programmation fonctionnelle
Concepts puissants qui empêchent beaucoup d’erreurs et d’effet de bords
Demande des ajustements dans la façon de coder, et dans l’approche
des problèmes
Ces concepts sont applicables dans tous les langages, objets ou
procéduraux
5. Analyse statiqueL’analyse du code par des systèmes tiers
La revue du code
Via des personnes : camarade, collègues, forums
Extreme Programming (XP) pratique le Pair programming : deux paires d'yeux valent mieux qu'une. Se passe pendant l’écriture, couteuse en ressource humaine
Code review : Se passe à postériori de l’écriture, moins couteuse
http://www.developpez.net/forums/; http://stackoverflow.com/; http://codereview.stackexchange.com/
Via des logiciels
ReSharper, NDepend, Coverity, etc.
http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis
Un exemple
Capture d’écran d’un rapport NDepend
Purity – Immutability – Side-Effects
Capture d’écran d’un rapport NDepend
Les outils d’analyse statique
Analyse du code basé sur un ensemble de règles
Propose des avertissements dans les domaines : convention de nommage,
design, architecture, code mort, etc.
Permet la modification et l’ajout de nouvelles règles
Disponible dans tout les langages, du gratuit au plus cher
Programmation par contrat
Permet de définir des pré conditions, des post conditions et des invariants
pour chaque fonction
Provient du langage Eiffel (1986) qui le supporte nativement
Supporté à base de plugin dans Visual Studio (Code Contracts) de
manière statique
Plusieurs implémentations existe dans les autres langages, analyse
dynamique uniquement
Java : Cofoja, SpringContracts
PHP : Praspel
CF http://en.wikipedia.org/wiki/Design_by_contract
Vérification statique à base de Warning
Vérification dynamique = lancement d’Exception
Générateur de documentation
Code Contracts
Exemple Code Contractspublic class Test{
private object _value2 = "";
public object TestMethod(object value, object value2){
Contract.Requires(value != null); // Static Warning && Runtime Exception en entréeContract.Ensures(Contract.Result<object>() != null); // Static Warning && Runtime Exception en sortie
this._value2 = value2;
return null; // Static Warning}
[ContractInvariantMethod]private void ObjectInvariant(){
Contract.Invariant(this._value2 != null); // Static Warning && Runtime Exception en sortie}
}
static void Main(string[] args){
var test = new Test();test.TestMethod(null, null); // Static Warning
}
Qualité logicielle
Conclusion
Avoir en tête les fonctionnalités demandées, c’est bien
Avoir en tête la qualité et les fonctionnalités, c’est mieux
Des efforts aujourd’hui pour des effets demain
La qualité en informatique est comme la qualité en général, un
investissement à long terme
La qualité demande du temps. « Vite fait bien fait » n’existe pas, fast code
= dirty code
Bibliographie
Code Complete de Steve McConnell
Refactoring: Improving the Design of Existing Code de Martin Fowler
Learn You a Haskell for Great Good!: A Beginner's Guide de Miran Lipovaca
Clean Code: A Handbook of Agile Software Craftsmanship de Robert C.
Martin