Qualité logicielle

1 147 vues

Publié le

Publié dans : Formation
0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
1 147
Sur SlideShare
0
Issues des intégrations
0
Intégrations
411
Actions
Partages
0
Téléchargements
70
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Qualité logicielle

  1. 1. Qualité logicielle Cyril Gandon
  2. 2. 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
  3. 3. 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é
  4. 4. 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
  5. 5. 1. Mesure de la qualité
  6. 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. 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. 8. Fonctionnel  Développer les fonctionnalités demandées
  9. 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. 10. Performant  Soyez conscient de la complexité de votre algorithme  O(1) > O(log n) > O(n) > O(n log n) > O(n²)
  11. 11. 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
  12. 12. Testable  La couverture des tests unitaires doit approcher les 100%  Réduit les dépendances  Diminue les régressions
  13. 13. 2. Le Refactoring
  14. 14. 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
  15. 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. 16. Références  http://refactoring.com  http://sourcemaking.com/refactoring
  17. 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. 18. 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.
  19. 19. 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); }
  20. 20. 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
  21. 21. 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
  22. 22. 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.
  23. 23. 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
  24. 24. 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; } }
  25. 25. 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())
  26. 26. 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 }
  27. 27. 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);
  28. 28. 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)
  29. 29. 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
  30. 30. 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);
  31. 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. 32. 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
  33. 33. 3. Les principes S.O.L.I.D.
  34. 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. 35. S.O.L.I.D.  Single responsibility principle  Open/closed principle  Liskov substitution principle  Interface segregation principle  Dependency inversion principle
  36. 36. Single responsibility principle  Un objet doit avoir une seule responsabilité  Un objet doit avoir une seule raison de changer
  37. 37. 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 }
  38. 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. 39. Open/closed principle  Un objet doit être ouvert à l’extension mais fermer à la modification
  40. 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. 41. 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
  42. 42. 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
  43. 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. 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. 45. 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
  46. 46. 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é
  47. 47. Interface Segregation Principle  Il vaut mieux avoir plusieurs interfaces spécifiques plutôt qu'une seule grande interface
  48. 48. 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!"; } }
  49. 49. 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..."); } }
  50. 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. 51. 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..."); } }
  52. 52. 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..."); } }
  53. 53. 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
  54. 54. 4. Initiation à la programmation fonctionnelle
  55. 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. 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. 57. 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é
  58. 58. 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
  59. 59. 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
  60. 60. 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; } }
  61. 61. 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);
  62. 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. 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. 64. Exemple : un tas immutable public interface IStack<T> : IEnumerable<T> { IStack<T> Push(T value); IStack<T> Pop(); T Peek(); bool IsEmpty { get; } }
  65. 65. 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(); } }
  66. 66. 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
  67. 67. 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)
  68. 68. 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
  69. 69. 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(); }
  70. 70. 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); } }
  71. 71. 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; } } }
  72. 72. 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
  73. 73. 5. Analyse statique L’analyse du code par des systèmes tiers
  74. 74. 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
  75. 75. Un exemple  Capture d’écran d’un rapport NDepend
  76. 76. Purity – Immutability – Side-Effects  Capture d’écran d’un rapport NDepend
  77. 77. 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
  78. 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. 79. Code Contracts  Vérification statique à base de Warning  Vérification dynamique = lancement d’Exception  Générateur de documentation
  80. 80. 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 }
  81. 81. 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
  82. 82. 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

×