qualité de code et bonnes pratiques
TRANSCRIPT
PO3T Programmation orientée objet
Séance 12
Qualité de code etbonnes pratiques
Sébastien Combéfis, Quentin Lurkin mercredi 9 décembre 2015
Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative CommonsAttribution – Pas d’Utilisation Commerciale – Pas de Modification 4.0 International.
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 classePartage 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
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
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
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
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
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
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
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
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
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
Refactoring (1)
Transformation de code qui préserve son comportement
“A change made to the internal structure of a software tomake it easier to understand and cheaper to modify withoutchanging 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
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
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
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 complexesous une forme simplifiée”
18
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 GradeReport2 {3 private Grade [] grades ;45 public double getGrade ( String courseName ) { /* ... */ }67 private static class Grade { /* ... */ }8 }
19
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
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 BookStore2 {3 private Books [] books ;45 public BookStore ( Books [] books )6 {7 this . books = books ;8 }9 }
21
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
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 unespécialisation d’une autre
6 (niveaux d’héritage)
Surveillez l’encapsulation
23
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 Stel 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
Violation du LSP
Rectangle
Square
getWidth(), setWidth(w),getHeight(), setHeight(h)
@post width et heightlibrement modifiables
@post width et heightdoivent ê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
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
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 try6 {7 Thread . sleep (i * 100);8 }9 catch ( InterruptedException exception ){}
10 }11 }
29
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
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 = "[";67 if (tab. length > 0)8 {9 s += tab [0];
10 }1112 for (int i = 1; i < tab. length ; i++)13 {14 s += ", " + tab[i];15 }1617 System .out. println (s + "]");18 }
31
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 "};56 String s = Arrays . toString (tab);78 System .out. println (s);9 }
32
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;67 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 }1617 return Collections . unmodifiableList (back);18 }
33
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
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;78 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 }1718 return Collections . unmodifiableList (back);19 }
35
Exemple 4
Fermeture et libération correcte des ressources
Par exemple lorsqu’on ouvre un fichier, ou un socket...
1 try2 {3 List <String > list = Arrays . asList ("1", "2", "3");4 BufferedWriter writer = new BufferedWriter (new FileWriter ("file.txt"));56 for ( String data : list)7 {8 writer . write (data);9 writer . newLine ();
10 }1112 writer . close ();13 }14 catch ( IOException exception )15 {16 System .err. println (" Erreur : " + exception . getMessage ());17 }
36
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 IOException2 {3 try4 {5 List <String > list = Arrays . asList ("1", "2", "3");6 BufferedWriter writer = new BufferedWriter (new FileWriter (path));78 for ( String data : list)9 {
10 writer . write (data);11 writer . newLine ();12 }13 }14 finally15 {16 writer . close ();17 }18 }
37
Exemple 5
Énumérations pour un ensemble de valeurs possibles
Pour que le compilateur puisse contrôler les valeurs
1 public class VendingMachine2 {3 public static final double ONECENT = 0.01;4 public static final double TWOCENTS = 0.02;5 // ...67 public void insert ( double coin)8 {9 // ...
10 }1112 public static void main ( String [] args)13 {14 new VendingMachine (). insert ( TWOCENTS );15 }16 }
38
Énumération
1 public enum Coin2 {3 ONECENT (0.01) ,4 TWOCENTS (0.02) ,5 FIVECENTS (0.05) ;67 private double value ;89 private Coin ( double v)
10 {11 value = v;12 }13 }1415 public class VendingMachine16 {17 public void insert (Coin coin)18 {19 // ...20 }21 }
39
Exemple 6
Prudence en utilisant l’héritage
Rédéfinir adéquatement les méthodes, songer à la composition
1 public class Point2 {3 private final int x, y;45 public Point (int x, int y)6 {7 this .x = x;8 this .y = y;9 }
10 }1112 public class ColoredPoint extends Point13 {14 private final Color color ;1516 public ColoredPoint (int x, int y, Color c)17 {18 super (x, y);19 color = c;20 }21 }
40
Héritage
Il faut utiliser l’héritage avec prudence
Attention à la redéfinition de equals, compareTo...
Les constructeurs, readObject et clone ne devraient pasappeler 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
Crédits
https://www.flickr.com/photos/dreamsjung/12613244714https://www.flickr.com/photos/smitty/2245445147https://www.flickr.com/photos/magdav/4937833904
42