Qualité logicielle

Post on 28-Nov-2014

607 views 1 download

description

 

Transcript of Qualité logicielle

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