Qualité logicielle
Cyril 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.




Reliability : The capability of software to maintain its level of performance under stated conditions for a stated period of
time.




Time Behaviour, Resource Utilization, Efficiency Compliance

Maintainability : The effort needed to make specified modifications.




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.




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.




Suitability, Accuracy, Interoperability, Security, Functionality Compliance

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 + b)²

 a = 2, b = 4

 a = 2, b = 4

 2² + 2 * 2 * 4 + 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

public int Foo(int j)
{
int i = 2;
int a;
if (j > 3)
{
a = i * i;
}
else
{
a = 0;
}
return a;
}

Scope de i

public int Foo(int j)
{
int a;
if (j > 3)
{
int i = 2;
a = i * i;
}
else
{
a = 0;
}
return a;
}

Scope de i
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 class MyPoint
{
private readonly double _x;

public double X
{
get { return _x; }
set { _x = value; }
}
private double _y;

public double X
{
get { return _x; }
}
private readonly double _y;
public double Y
{
get { return _y; }
}

public double Y
{
get { return _y; }
set { _y = value; }
}
public MyPoint(double x, double y)
{
this.X = x;
this.Y = 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
double foo;
if(this.HasValue == false)
foo = 0;
else
foo = 1;

double foo;
if(this.HaveValue)
foo = 1;
else
foo = 0;

if(!this.NotFound())

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
 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

var employees = someObject.GetEmployeeList();
employees.AddElementWithKey(employee.GetKey(), employee);

someObject.AddToEmployees(employee);
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

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;Banana
using (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 };
}
}
}

 Non testable unitairement

}
Séparer les responsabilités
// Représente un fruit dans un fichier
public class FruitFile
{
public string Id { get; set; }
public string Name { get; set; }
public static IEnumerable<FruitFile> FruitsFromFile(string
path)
{
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 fruit
public 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 concept
public 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 class Shapes
{
// Can run with abstract list of shapes
public double AreaOf(IEnumerable<IShape> shapes)
{
double total = 0;
foreach (var shape in shapes)
{
total += shape.Area;
}
return total;
}
}

public double Area
{
get { return this.Radius * this.Radius * Math.PI; }
}
}

 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 class RubberDuck : Duck
{
public override void Give(Fish food)
{
throw new InvalidOperationException("Rubber Duck
doesn't need fishes");
}

public virtual int LegCount { get { return 2; } }
}

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
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 Principle
duck.Give(fish);
}
}

 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é
Interface Segregation Principle
 Il vaut mieux avoir plusieurs interfaces spécifiques plutôt qu'une seule
grande interface
Mauvais exemple
// Big interface, so much work
public interface ICommunicate
{
void Send(object data);
object Receive();
}

public class Printer : ICommunicate
{
public void Send(object data)
{
Console.WriteLine("Printing datas...");
}

// Phone is ok, can communicate both ways
public class Phone : ICommunicate
{
public void Send(object data)
{
Console.WriteLine("Composing
number...");
}

// Printer has to define the Receive method
public object Receive()
{
// But printer can't receive anything
throw new InvalidOperationException("Printer
are not receiving.");
}
}

public object Receive()
{
return "Dring!";
}
}
Bon exemple
// make it two interfaces instead of one
public 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 module
public class Logger
{
public void Log(string text)
{
Console.WriteLine(text);
}
}
// High level module, depend on low level module
public class DatabaseReader
{
public Logger Logger { get; set; }
public void ReadData()
{
this.Logger.Log("Reading datas...");
}
}
Bon exemple
// Abstraction
public interface ILogger
{
void Log(string text);
}

// Low level modules, depend on abstraction
public class ConsoleLogger : ILogger
{
public void Log(string text)
{
Console.WriteLine(text);
}
}
// Low level modules, depend on abstraction
public class FileLogger : ILogger
{
private readonly string _path;
public FileLogger(string path)
{
this._path = path;
}
public void Log(string text)
{
using (var file = new
StreamWriter(this._path))
{
file.WriteLine(text);
}
}
}

// High level module, depend on abstraction
public class DatabaseReader
{
public ILogger Logger { get; set; }
public void ReadData()
{
this.Logger.Log("Reading datas...");
}
}
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!
Avantage de l’immutabilité
 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
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 immutable
public 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>
IStack<int>
IStack<int>
IStack<int>

s1
s2
s3
s4

=
=
=
=

Stack<int>.Empty;
s1.Push(10);
s2.Push(20);
s2.Push(30); // shares its tail with s3.

Source : http://blogs.msdn.com/b/ericlippert/archive/2007/12/04/immutability-in-c-parttwo-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

 Après

public List<int> MutableEvens(List<int> ints)
{
var evens = new List<int>();
foreach (var item in ints)
{
if (item % 2 == 0)
{
// mutation of the list
evens.Add(item);
}
}
return evens;
}

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);
}
}
Correction
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;
}

[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)
};

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();
}

CollectionAssert.AreEqual(expected, actual);
}

public override bool Equals(object obj)
{
var point = (MyPoint)obj;
return point.X == this.X && point.Y == this.Y;
}
}

}
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 statique
L’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
Code Contracts
 Vérification statique à base de Warning
 Vérification dynamique = lancement d’Exception

 Générateur de documentation
Exemple Code Contracts
public class Test
{
private object _value2 = "";
public object TestMethod(object value, object value2)
{
Contract.Requires(value != null); // Static Warning && Runtime Exception en entrée
Contract.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

Qualité logicielle

  • 1.
  • 2.
    Introduction  Vous savezcoder : 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
  • 3.
    Objectifs  Etre capablede 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é
  • 4.
    Sommaire 1. Mesure de laqualité 2. Le Refactoring 3. Les principes S.O.L.I.D. 4. Initiation à la programmation fonctionnelle 5. Analyse statique 6. Programmation par contrat
  • 5.
    1. Mesure dela qualité
  • 6.
    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.   Reliability : The capability of software to maintain its level of performance under stated conditions for a stated period of time.   Time Behaviour, Resource Utilization, Efficiency Compliance Maintainability : The effort needed to make specified modifications.   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.   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.   Suitability, Accuracy, Interoperability, Security, Functionality Compliance 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
  • 7.
    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)
  • 8.
    Fonctionnel  Développer lesfonctionnalités demandées
  • 9.
    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
  • 10.
    Performant  Soyez conscientde la complexité de votre algorithme  O(1) > O(log n) > O(n) > O(n log n) > O(n²)
  • 11.
    Robuste  Comment réagivotre 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
  • 12.
    Testable  La couverturedes tests unitaires doit approcher les 100%  Réduit les dépendances  Diminue les régressions
  • 13.
  • 14.
    Le refactoring, c’estquoi ?  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
  • 15.
    (Re)Factorisation d’une identité remarquable a²+ 2ab + b² (a + b)²  a = 2, b = 4  a = 2, b = 4  2² + 2 * 2 * 4 + 4² = ?  (2 + 4)² = 36
  • 16.
  • 17.
    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
  • 18.
    1. Renommage  Unbon 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.
  • 19.
    2. Découper lesfonctions  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); }
  • 20.
    3. Réduire lescope 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
  • 21.
    3. Réduire lescope des variables public int Foo(int j) { int i = 2; int a; if (j > 3) { a = i * i; } else { a = 0; } return a; } Scope de i public int Foo(int j) { int a; if (j > 3) { int i = 2; a = i * i; } else { a = 0; } return a; } Scope de i
  • 22.
    4. Réduire lenombre 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.
  • 23.
    5. Rendre lesobjets 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
  • 24.
    5. Rendre lesobjets immutables public class MyPoint { private double _x; public class MyPoint { private readonly double _x; public double X { get { return _x; } set { _x = value; } } private double _y; public double X { get { return _x; } } private readonly double _y; public double Y { get { return _y; } } public double Y { get { return _y; } set { _y = value; } } public MyPoint(double x, double y) { this.X = x; this.Y = y; } } public MyPoint(double x, double y) { this._x = x; this._y = y; } }
  • 25.
    6. Eviter lesnégations et les doubles négation  La lecture est toujours plus simple dans le sens positif que dans le sens négatif double foo; if(this.HasValue == false) foo = 0; else foo = 1; double foo; if(this.HaveValue) foo = 1; else foo = 0; if(!this.NotFound()) if(this.Found())
  • 26.
    7. Introduire desvariables 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 }
  • 27.
    8. Ne pasutiliser 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);
  • 28.
    9. Séparer lesniveaux (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)
  • 29.
    10. Loi deDemeter  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
  • 30.
    10. Loi deDemeter  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 var employees = someObject.GetEmployeeList(); employees.AddElementWithKey(employee.GetKey(), employee); someObject.AddToEmployees(employee);
  • 31.
    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/
  • 32.
    Conclusion  Le refactoringest 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
  • 33.
    3. Les principesS.O.L.I.D.
  • 34.
    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
  • 35.
    S.O.L.I.D.  Single responsibilityprinciple  Open/closed principle  Liskov substitution principle  Interface segregation principle  Dependency inversion principle
  • 36.
    Single responsibility principle Un objet doit avoir une seule responsabilité  Un objet doit avoir une seule raison de changer
  • 37.
    Mauvais exemple  Litun fichier  Parse et valide les données 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;Banana using (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 }; } } }  Non testable unitairement }
  • 38.
    Séparer les responsabilités //Représente un fruit dans un fichier public class FruitFile { public string Id { get; set; } public string Name { get; set; } public static IEnumerable<FruitFile> FruitsFromFile(string path) { 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 fruit public 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
  • 39.
    Open/closed principle  Unobjet doit être ouvert à l’extension mais fermer à la modification
  • 40.
    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)
  • 41.
    Mauvais exemple public classRectangle { 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
  • 42.
    Ajout d’un niveaud’abstraction // Abstract the Shape concept public 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 class Shapes { // Can run with abstract list of shapes public double AreaOf(IEnumerable<IShape> shapes) { double total = 0; foreach (var shape in shapes) { total += shape.Area; } return total; } } public double Area { get { return this.Radius * this.Radius * Math.PI; } } }  Le code est extensible pour n’importe quel forme
  • 43.
    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
  • 44.
    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
  • 45.
    Mauvais exemple public classFish { } public class Duck { public virtual void Give(Fish food) { Console.WriteLine("Eating the fish"); } public class RubberDuck : Duck { public override void Give(Fish food) { throw new InvalidOperationException("Rubber Duck doesn't need fishes"); } public virtual int LegCount { get { return 2; } } } 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
  • 46.
    Mauvais exemple public classTest { 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 Principle duck.Give(fish); } }  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é
  • 47.
    Interface Segregation Principle Il vaut mieux avoir plusieurs interfaces spécifiques plutôt qu'une seule grande interface
  • 48.
    Mauvais exemple // Biginterface, so much work public interface ICommunicate { void Send(object data); object Receive(); } public class Printer : ICommunicate { public void Send(object data) { Console.WriteLine("Printing datas..."); } // Phone is ok, can communicate both ways public class Phone : ICommunicate { public void Send(object data) { Console.WriteLine("Composing number..."); } // Printer has to define the Receive method public object Receive() { // But printer can't receive anything throw new InvalidOperationException("Printer are not receiving."); } } public object Receive() { return "Dring!"; } }
  • 49.
    Bon exemple // makeit two interfaces instead of one public 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..."); } }
  • 50.
    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
  • 51.
    Mauvais exemple // Lowlevel module public class Logger { public void Log(string text) { Console.WriteLine(text); } } // High level module, depend on low level module public class DatabaseReader { public Logger Logger { get; set; } public void ReadData() { this.Logger.Log("Reading datas..."); } }
  • 52.
    Bon exemple // Abstraction publicinterface ILogger { void Log(string text); } // Low level modules, depend on abstraction public class ConsoleLogger : ILogger { public void Log(string text) { Console.WriteLine(text); } } // Low level modules, depend on abstraction public class FileLogger : ILogger { private readonly string _path; public FileLogger(string path) { this._path = path; } public void Log(string text) { using (var file = new StreamWriter(this._path)) { file.WriteLine(text); } } } // High level module, depend on abstraction public class DatabaseReader { public ILogger Logger { get; set; } public void ReadData() { this.Logger.Log("Reading datas..."); } }
  • 53.
    Conclusion  Appliquer cesprincipes 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
  • 54.
    4. Initiation àla programmation fonctionnelle
  • 55.
    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
  • 56.
    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
  • 57.
    Historique de laprogrammation 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é
  • 58.
    A quoi çasert 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
  • 59.
    Paradigmes  Aucun étatn’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
  • 60.
    Immutabilité  Sur desobjets, 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; } }
  • 61.
    Immutabilité  Sur desconteneurs 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);
  • 62.
    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!
  • 63.
    Avantage de l’immutabilité 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
  • 64.
    Exemple : untas immutable public interface IStack<T> : IEnumerable<T> { IStack<T> Push(T value); IStack<T> Pop(); T Peek(); bool IsEmpty { get; } }
  • 65.
    Exemple : untas immutable public 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(); } }
  • 66.
    Exemple : untas immutable IStack<int> IStack<int> IStack<int> IStack<int> s1 s2 s3 s4 = = = = Stack<int>.Empty; s1.Push(10); s2.Push(20); s2.Push(30); // shares its tail with s3. Source : http://blogs.msdn.com/b/ericlippert/archive/2007/12/04/immutability-in-c-parttwo-a-simple-immutable-stack.aspx
  • 67.
    LINQ : Applicationau 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)
  • 68.
    Un peu deLINQ : filtrer les éléments d’une liste  Avant  Après public List<int> MutableEvens(List<int> ints) { var evens = new List<int>(); foreach (var item in ints) { if (item % 2 == 0) { // mutation of the list evens.Add(item); } } return evens; } public List<int> ImmutableEvens(List<int> ints) { return ints.Where(i => i % 2 == 0).ToList(); }  Aucune mutation, sans effet de bords
  • 69.
    Un peu deLINQ : 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(); }
  • 70.
    Exercice  Refactoriser lecode 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); } }
  • 71.
    Correction public class MyPoint { privatereadonly 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; } [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) }; 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(); } CollectionAssert.AreEqual(expected, actual); } public override bool Equals(object obj) { var point = (MyPoint)obj; return point.X == this.X && point.Y == this.Y; } } }
  • 72.
    Conclusion  Les conceptsde 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
  • 73.
    5. Analyse statique L’analysedu code par des systèmes tiers
  • 74.
    La revue ducode  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
  • 75.
    Un exemple  Captured’écran d’un rapport NDepend
  • 76.
    Purity – Immutability– Side-Effects  Capture d’écran d’un rapport NDepend
  • 77.
    Les outils d’analysestatique  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
  • 78.
    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
  • 79.
    Code Contracts  Vérificationstatique à base de Warning  Vérification dynamique = lancement d’Exception  Générateur de documentation
  • 80.
    Exemple Code Contracts publicclass Test { private object _value2 = ""; public object TestMethod(object value, object value2) { Contract.Requires(value != null); // Static Warning && Runtime Exception en entrée Contract.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 }
  • 81.
    Qualité logicielle Conclusion  Avoiren 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
  • 82.
    Bibliographie  Code Completede 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