42
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

Qualité de code et bonnes pratiques

Embed Size (px)

Citation preview

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

Qualité de code (1)

6

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

Composition ou héritage ? (2)

Vehicle

Car Wheel

is-a

has-a

24

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

Bonnes pratiques

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