Qualité de code et bonnes pratiques

435 vues

Publié le

Ce cours présente la notion de qualité de code et comment il est possible de l'évaluer grâce à des métriques mesurables. Après avoir présenté plusieurs métriques standards, il se concentrer sur des aspects de qualité de code spécifique à l'orienté objet et présente les cinq concepts de l'orienté objet. La deuxième partie du cours présente plusieurs bonnes pratiques à avoir en programmation orientée objet, sur base d'exemples concrets.

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

  • Soyez le premier à aimer ceci

Aucun téléchargement
Vues
Nombre de vues
435
Sur SlideShare
0
Issues des intégrations
0
Intégrations
7
Actions
Partages
0
Téléchargements
15
Commentaires
0
J’aime
0
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Qualité de code et bonnes pratiques

  1. 1. PO3T Programmation orientée objet Séance 12 Qualité de code et bonnes pratiques Sébastien Combéfis, Quentin Lurkin mercredi 9 décembre 2015
  2. 2. Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Pas d’Utilisation Commerciale – Pas de Modification 4.0 International.
  3. 3. Rappels Variable et méthode de classe Instance de la classe Class Partage d’information entre instances Définition et utilisation de classe interne d’instance/de classe Partage privé de this avec une autre instance Modularité et structuration « horizontale » Organisation de classes en packages Définition et constitution d’un package de classes Visibilité package 3
  4. 4. Objectifs Évaluer la qualité d’un code Définition et critères de qualité d’un code Processus de refactoring Bonnes pratiques en programmation orientée objet Utilisation adéquate des classes et objets Héritage versus composition 4
  5. 5. Qualité de code
  6. 6. Qualité de code (1) 6
  7. 7. Qualité de code (2) Nécessite d’évaluer la qualité d’un code Utilisation de métrique et critères d’évaluation Plusieurs types de qualité Qualité du code (ou structurelle) Manière avec laquelle une fonctionnalité est implémentée Qualité logicielle (ou fonctionnelle) Résultat final de la fonctionnalité Qualité du code peut être mesurée automatiquement Outils existants pour la plupart des langages de programmation 7
  8. 8. Métrique Métrique permet de mesurer un certain aspect du code Fournit une valeur chiffrée pour un critère mesuré Ne pas se forcer à atteindre de bonnes valeurs des métriques Course aux bonnes valeurs et comportement contre-productif Un mauvais rapport doit être perçu comme un signal d’alarme Le lecteur du rapport doit donc aller plus loin dans sa démarche 8
  9. 9. Métriques standards (1) Complexité cyclomatique Nombre de chemins linéaires possibles dans une fonction SLOC (Source Line of Code) Nombre de lignes de code du programme Densité des commentaires (DC) DC = CLOC / SLOC Couverture de code Proportion du code qui est couverte par des tests 9
  10. 10. Métriques standards (2) Code dupliqué Pourcentage de code dupliqué ou très similaire Couplage afferent (Ca) ou efferent (Ce) Nombre de références vers/depuis une classe Instabilité (Ce / (Ce + Ca)) Niveau de résistance au changement (stable = difficile à changer) 10
  11. 11. Couplage La classe Shape est très stable Ca = 2, Ce = 0 et donc une instabilité de 0 Niveau d’abstraction élevé pour des classes très stables Rapport entre types abstraits et autres types Shape Rectangle Square Circle 11
  12. 12. Autres critères (1) Architecture Maintenabilité, évolutivité, performance, pertinence Style et lisibilité Mise en page, indentation, structure, nommage... Documentation technique Pour qu’une personne extérieure puisse rentrer dans le code Fiabilité Nombre de défaillances rencontrées dans un laps de temps donné 12
  13. 13. Autres critères (2) Portabilité Capacité d’un logiciel à fonctionner sur des systèmes différents Sécurité Exigences en matière de sécurité dépendent du type de logiciel Nombre de bugs Nombre de problèmes perturbant l’usage normal du logiciel 13
  14. 14. KISS Keep It Simple, Stupid Il faut éviter de rendre les problèmes plus compliqués Principe phare en ingénierie logicielle “Many great problem solvers were not great coders, but yet they produced great code !” 14
  15. 15. Refactoring (1) Transformation de code qui préserve son comportement “A change made to the internal structure of a software to make it easier to understand and cheaper to modify without changing its observable behaviour” — Martin Fowler Objectifs Rendre plus facile l’ajout de nouveau code Améliorer le design du code existant Mieux comprendre un code Rendre le code moins ennuyeux 15
  16. 16. Refactoring (2) Ne faire du refactoring que sur du code fonctionnel et testé Permet d’améliorer la qualité du code Utilisation de cycles TDD Utiliser des tests unitaires pour se rassurer que rien n’a été altéré Les problèmes de design proviennent de code... ...dupliqué ...pas clair ...compliqué 16
  17. 17. Programmation orientée objet Il faut trouver et identifier des objets du monde réel Plusieurs questions à se poser Identifier l’objet et ses attributs Déterminer ce qui peut être fait avec l’objet Identifier ce que l’objet peut faire à d’autre objets Déterminer ce qui sera visible de l’objet Définir l’interface publique de l’objet 17
  18. 18. Abstraction Créer une bonne abstraction de l’objet représenté S’assurer que les détails d’implémentation soient bien cachés L’abstraction doit former un tout cohérent et consistent Le niveau d’abstraction doit être consistent Abstraction — “Le fait de voir une opération complexe sous une forme simplifiée” 18
  19. 19. Encapsulation Minimiser l’accès aux classes et à leurs membres Ne pas exposer les données de la classe Ne pas exposer des détails d’implémentation Diminuer au maximum le couplage entre classes 1 public class GradeReport 2 { 3 private Grade [] grades; 4 5 public double getGrade (String courseName) { /* ... */ } 6 7 private static class Grade { /* ... */ } 8 } 19
  20. 20. Membres d’une classe Limiter le nombre de méthodes Limiter les appels de méthodes directs et indirects En général : limiter la collaboration avec d’autres classes Law of Demeter (LoD) Une méthode M d’un objet O ne peut invoquer que : 1 ses propres méthodes ; 2 les méthodes de ses paramètres ; 3 les méthodes des objets qu’elle instance ; 4 et les méthodes de ses objets composants. 20
  21. 21. Constructeur Initialiser toutes les variables d’instance Interdire la création d’instances avec un constructeur privé Éviter les shallow copies des paramètres 1 public class BookStore 2 { 3 private Books [] books; 4 5 public BookStore (Books [] books) 6 { 7 this.books = books; 8 } 9 } 21
  22. 22. Pourquoi une classe ? Modéliser un objet du monde réel Modéliser des objets abstraits Réduire ou isoler la complexité Limiter les impacts lors de modifications Cacher les données et détails d’implémentation Faciliter la réutilisation de code 22
  23. 23. Composition ou héritage ? (1) HAS-A Une classe se compose à partir d’autres 7 ± 2 (composants) Surveillez le couplage IS-A Une classe est une spécialisation d’une autre 6 (niveaux d’héritage) Surveillez l’encapsulation 23
  24. 24. Composition ou héritage ? (2) Vehicle Car Wheel is-a has-a 24
  25. 25. Principe de Substitution de Liskov Liskov Substitution Principle (LSP) Si q(x) est une propriété démontrable pour tout objet x de type T, Alors q(y) est vraie pour tout objet y de type S tel que S est un sous-type de T. Principe qui définit ce qu’est un bon sous-type Soit S un sous-type de T Tout objet de type T peut être remplacé par un objet de type S Pas d’altération des propriétés désirables du programme 25
  26. 26. Violation du LSP Rectangle Square getWidth(), setWidth(w), getHeight(), setHeight(h) @post width et height librement modifiables @post width et height doivent être égaux Square ne peut pas être utilisé partout à la place de Rectangle Redéfinition des mutateurs dans Square avec vérification Violation de la postcondition des Rectangle 26
  27. 27. Cinq principes de l’OO 1 SRP — Single Responsibility Principle Une classe = une responsabilité (éviter les god classes) 2 OCP — Open/Closed Principle Entité software open pour l’extension, fermée pour la modification 3 LSP — Liskov Substitution Principle Remplacement d’un objet d’un type par un autre d’un sous-type 4 ISP — Interface Segregation Principle Plusieurs interfaces clients plutôt qu’une seule grosse 5 DIP — Dependency Inversion Principle Module de haut niveau doit dépendre d’abstractions 27
  28. 28. Bonnes pratiques
  29. 29. Exemple 1 Il faut éviter un bloc vide pour l’instruction catch Impossibilité de détecter si une erreur s’est produite 1 public static void main (String [] args) 2 { 3 for (int i = 0; i < 10; i++) 4 { 5 try 6 { 7 Thread.sleep (i * 100); 8 } 9 catch ( InterruptedException exception){} 10 } 11 } 29
  30. 30. Exception Plusieurs types d’erreurs peuvent causer une exception Bug Mauvaise entrée utilisateur Un problème n’étant pas un bug Plusieurs réactions possibles suite à une exception Informer l’utilisateur (recommandé) Logguer le problème Envoyer un e-mail à l’administrateur 30
  31. 31. Exemple 2 Il ne faut pas toujours réinventer la roue Il faut exploiter au maximum les librairies existantes 1 public static void main (String [] args) 2 { 3 String [] tab = {"One", "Two", "Three", "Four", 4 "Five", "Six", "Eleven"}; 5 String s = "["; 6 7 if (tab.length > 0) 8 { 9 s += tab [0]; 10 } 11 12 for (int i = 1; i < tab.length; i++) 13 { 14 s += ", " + tab[i]; 15 } 16 17 System.out.println (s + "]"); 18 } 31
  32. 32. Exploiter la librairie standard Il faut exploiter la librairie standard du language Recèle de classes avec des méthodes utiles Il existe également des librairies spécialisées Boost pour C++, SciPy en Python, JUNG en Java... 1 public static void main (String [] args) 2 { 3 String [] tab = {"One", "Two", "Three", "Four", 4 "Five", "Six", "Eleven"}; 5 6 String s = Arrays.toString (tab); 7 8 System.out.println (s); 9 } 32
  33. 33. Exemple 3 Attention aux calculs avec des nombres en précision finie Il faut utiliser des objets spécialisés pour ces calculs 1 public static List <Double > change (double toPay , double givenMoney) 2 { 3 ArrayList <Double > back = new ArrayList <Double >(); 4 double diff = givenMoney - toPay; 5 int i = coins.length - 1; 6 7 while (diff != 0) 8 { 9 while (i >= 0 && coins[i] <= diff) 10 { 11 back.add (coins[i]); 12 diff = diff - coins[i]; 13 } 14 i = i - 1; 15 } 16 17 return Collections . unmodifiableList (back); 18 } 33
  34. 34. Classe BigDecimal (1) Utiliser java.math.BigDecimal pour représenter de l’argent Les BigDecimal sont des objets immuables Utiliser le style d’arrondi ROUND_HALF_EVEN Utiliser le constructeur BigDecimal (String) Utiliser les types primitifs int ou long ≤ 9 chiffres : int, long ou BigDecimal ≤ 18 chiffres : long ou BigDecimal > 18 chiffres : BigDecimal 34
  35. 35. Classe BigDecimal (2) 1 public static List <BigDecimal > change ( BigDecimal toPay , 2 BigDecimal givenMoney) 3 { 4 ArrayList <BigDecimal > back = new ArrayList <BigDecimal >(); 5 BigDecimal diff = givenMoney.subtract (toPay); 6 int i = coins.length - 1; 7 8 while (diff.compareTo ( BigDecimal .ZERO) != 0) 9 { 10 while (i >= 0 && coins[i]. compareTo (diff) <= 0) 11 { 12 back.add (coins[i]); 13 diff = diff.subtract (coins[i]); 14 } 15 i = i - 1; 16 } 17 18 return Collections . unmodifiableList (back); 19 } 35
  36. 36. Exemple 4 Fermeture et libération correcte des ressources Par exemple lorsqu’on ouvre un fichier, ou un socket... 1 try 2 { 3 List <String > list = Arrays.asList ("1", "2", "3"); 4 BufferedWriter writer = new BufferedWriter (new FileWriter ("file.txt")); 5 6 for (String data : list) 7 { 8 writer.write (data); 9 writer.newLine (); 10 } 11 12 writer.close (); 13 } 14 catch ( IOException exception) 15 { 16 System.err.println ("Erreur : " + exception.getMessage ()); 17 } 36
  37. 37. Utilisation de finally Instruction finally exécutée dans tous les cas Créer une méthode et laisser remonter les erreurs d’E/S 1 public static void print (List <String > list , String path) throws IOException 2 { 3 try 4 { 5 List <String > list = Arrays.asList ("1", "2", "3"); 6 BufferedWriter writer = new BufferedWriter (new FileWriter (path)); 7 8 for (String data : list) 9 { 10 writer.write (data); 11 writer.newLine (); 12 } 13 } 14 finally 15 { 16 writer.close (); 17 } 18 } 37
  38. 38. Exemple 5 Énumérations pour un ensemble de valeurs possibles Pour que le compilateur puisse contrôler les valeurs 1 public class VendingMachine 2 { 3 public static final double ONECENT = 0.01; 4 public static final double TWOCENTS = 0.02; 5 // ... 6 7 public void insert (double coin) 8 { 9 // ... 10 } 11 12 public static void main ( String [] args) 13 { 14 new VendingMachine ().insert (TWOCENTS); 15 } 16 } 38
  39. 39. Énumération 1 public enum Coin 2 { 3 ONECENT (0.01) , 4 TWOCENTS (0.02) , 5 FIVECENTS (0.05); 6 7 private double value; 8 9 private Coin ( double v) 10 { 11 value = v; 12 } 13 } 14 15 public class VendingMachine 16 { 17 public void insert (Coin coin) 18 { 19 // ... 20 } 21 } 39
  40. 40. Exemple 6 Prudence en utilisant l’héritage Rédéfinir adéquatement les méthodes, songer à la composition 1 public class Point 2 { 3 private final int x, y; 4 5 public Point (int x, int y) 6 { 7 this.x = x; 8 this.y = y; 9 } 10 } 11 12 public class ColoredPoint extends Point 13 { 14 private final Color color; 15 16 public ColoredPoint (int x, int y, Color c) 17 { 18 super (x, y); 19 color = c; 20 } 21 } 40
  41. 41. Héritage Il faut utiliser l’héritage avec prudence Attention à la redéfinition de equals, compareTo... Les constructeurs, readObject et clone ne devraient pas appeler des méthodes pouvant être redéfinies Empêcher la redéfinition avec final Considérer la composition à la place de l’héritage 41
  42. 42. Crédits https://www.flickr.com/photos/dreamsjung/12613244714 https://www.flickr.com/photos/smitty/2245445147 https://www.flickr.com/photos/magdav/4937833904 42

×