Tout comme on fait attention au style d'écriture utilisé lorsque l'on rédige un livre, en plus d'appliquer les règles d'orthographe et de grammaire, il faut également appliquer des règles stylistiques lorsqu'on rédige du code.
Cette conférence présente plusieurs aspects à prendre en compte pour rédiger du code de qualité, allant de la structure globale d'un programme à la mise en page de son code, en passant par les noms des variables.
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.
5. “Always code as if the guy who ends up maintaining
your code will be a violent psychopath who knows
where you live.” — Martin Golding
“Always code as if the guy who ends up maintaining
your code will be a violent psychopath who knows
where you live.” — Martin Golding
6. Cacher l’information
Deux catégories de secrets
Cacher la complexité pour le développeur
Cacher les sources de changement pour faciliter les modifs
Barrière au masquage d’information
Distribution excessive de l’information
Dépendance circulaire
Confondre variable de classe avec variables globales
6
7. Identifier le changement
Importance d’identifier le changement pour l’isoler
1 Identifier les éléments qui pourraient changer
2 Séparer les éléments identifiés dans une classe
3 Isoler les éléments identifiés du reste
Exemples communs qui peuvent changer
Règles business (facturation, taxe...)
Dépendances hardware
Entrée/sortie (format de fichiers produits)
Fonctionnalités non standards des langages
Zones avec des constructions difficiles
Variable statuts
7
8. Couplage
Il faut maintenir un couplage le plus faible possible
Critères de couplage
Minimiser le nombre de connexions vers d’autres modules
Rendre les connexions visibles
Pouvoir changer les connexions par flexibilité
Types de couplage
Simple-data-parameter (passage de paramètre primitif)
Simple-object (un objet instancie un autre objet)
Object-parameter
Semantic
8
9. Code de qualité
Focus sur la programmation orientée objet
Analyse top-down
Depuis la classe jusqu’aux instructions
Classe Méthode Instruction
9
10. Trouver les objets du monde réel
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
10
11. Programmer avec des classes
Abstract Data Type
ADT
Cacher les détails
d’implémentation
Rendre les changements
indépendants du reste
du programme
Le programme est
auto-documenté
Le programme manipule
des entités qui modélisent
le monde réel
11
12. Manipuler les objets du monde
getFuelLevel
isOnGround
startEngine
startAutopilot
13. 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”
13
14. 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
public class GradeReport
{
private Grade[] grades;
public double getGrade (String courseName) { /∗ ... ∗/ }
private static class Grade { /∗ ... ∗/ }
}
14
15. Composition ou héritage ? I
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
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.
15
17. 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.
17
18. Constructeurs
Initialiser toutes les variables d’instance
Interdire la création d’instances avec un constructeur privé
Éviter les shallow copies des paramètres
public class BookStore
{
private Books[] books;
public BookStore (Books[] books)
{
this.books = books;
}
}
18
19. Deep or shallow copies ?
public class Exam
{
private Course course;
private Student[] students;
public Exam (Course course, Student[] students)
{
this.course = course;
this.students = students;
}
}
public class Test
{
public static void main (String[] args)
{
Student[] students = /∗ ... ∗/ ;
Exam exam = new Exam (/∗ ... ∗/, students);
Arrays.fill (students, null);
}
}
19
20. 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
20
21. Avoid God classes
also known as The Blob
Toute puissante et qui sait tout
Passe son temps à appeler des get et set
22. Classes à éviter
Éviter les classes God
Éliminer les classes hors sujet
Classes avec seulement des données
Éviter les classes dont les noms sont des verbes
Classes avec seulement des comportements
22
23. Programmer avec des routines
Réduire la complexité
Introduit des abstractions intermédiaire et compréhensibles
Éviter la duplication de code
Faciliter la réutilisation de code
23
24. Pourquoi rassembler des instructions ?
Cohésion fonctionnelle
Une routine = une opération
Cohésion séquentielle
Séquence d’instructions qui partagent des données
Cohésion communicationnelle
Opérations qui utilisent la même donnée
Cohésion temporelle
Opérations qui doivent avoir lieu en même temps
24
25. Choisir le bon nom
Décrire ce que fait la routine
Éviter les verbes vagues : process, handleOutput...
Ne pas différencier rien que par un numéro : test1, test2...
Ne pas hésiter sur la longueur du nom
Utiliser les opposés à bon escient : get/put, start/stop...
Fonction : décrire la valeur de retour
Procédure : verbe fort + sujet pour décrire l’action
25
26. Longueur d’une routine
Pas de convention globalement acceptée
Doit tenir sur un écran ou une/deux page/s (50–150 lignes)
Convention NASA pour développer un code critique
60 lignes de code maximum par routine
26
27. Paramètres d’une routine
39% des erreurs viennent de l’interface entre routines
Cette interface se fait lors des appels de routines
Bonnes pratiques par rapport aux paramètres
Ordre des paramètres : input/modify/output
Même ordre lorsque routines avec les mêmes paramètres
Utiliser tous les paramètres
Ne pas utiliser les paramètres comme variable de travail
Documenter les hypothèses sur les paramètres
Limiter le nombre de paramètres (environ 7)
27
29. Gestion des erreurs ou assertion ?
Gestion des erreurs
À utiliser pour des situations particulières qui pourraient se
produire
Assertion
À utiliser pour des situations qui ne surviendront jamais
public Book getBook (int bookid)
{
for (int i = 0; i < books.length; i++)
{
if (books[i].getId() == bookid) { return books[i]; }
}
assert false;
return null;
}
29
30. Assertion
Le code doit rester fonctionnel sans les assertions
Vérification des préconditions et postconditions
Peut être vu comme des commentaires exécutables
30
31. Gestion des erreurs
Renvoyer une valeur neutre
Passer la donnée erronée
Renvoyer la même valeur que lors du dernier appel
Afficher un message d’erreur
Mécanisme d’exception pour communiquer des erreurs
Correctness <> Robustness
31
33. Déclaration
Initialisation lors de la déclaration
... dans le constructeur pour les variables d’instance
Déclaration le plus proche de l’utilisation
Utilisez les constantes
33
34. Span et durée de vie
Le « span » mesure l’espacement de l’utilisation de la variable
La durée de vie mesure le nombre d’instructions pour
lesquelles la variable existe
public void test()
{
int a = 0;
int b = 0;
int c = 0;
b = a + 1;
b = b / c;
}
Variable b
Average span : (1 + 0)/2 = 0.5
Live time : 4
(les deux valeurs sont à minimiser)
34
35. Minimiser la portée
Initialiser les variables utilisée dans une boucle juste avant
Ne pas retarder l’initialisation d’une variable
Grouper les instructions liées (avec les mêmes variables)
Séparer les groupes d’instructions liées en routines
Toujours commencer avec la visibilité la plus faible
35
36. Variable
Le nom décrit contenu de la variable et la documente
Longueur optimale entre 9 et 15 caractères
Une variable utilisée pour une seule raison
Éviter les variables avec plusieurs significations selon la valeur
S’assurer que toutes les variables sont utilisées
36
37. Choisir les noms
Noms avec signification proche : input et inputVal
Noms similaires : clientRec et clientRep
Éviter les nombres : total1 et total2
Attention à l’orthographe !
Éviter de mélanger les langues
Éviter des caractères difficiles à lire : char1 et chartl, cOnf et
c0nf, GREAT et 6REAT, tex2html et texZhtml...
37
38. Nom pour types de données spécifiques
Compteur des boucles i, j, k (sauf si utilisée hors boucle)
Sauf pour les longues boucles, où un nom parlant est mieux
Ne pas mettre flag dans le nom des variables de statut
Les variables temporaires sont à éviter (temp...)
Nommer les variables booléennes (done, error, found, ok,
success...) avec nom qui implique true ou false
38
40. Nombre (1)
Éviter les nombres magiques
Hardcoder les valeurs 0 et 1 ne pose pas problème
Anticiper les divisions par zéro
Rendre les conversions de type explicite
Éviter les comparaisons de types différents
40
41. Nombre (2)
Nombre entier
Vérifier les divisions par zéro
Vérifier les dépassements de capacité
Diviser avant de multiplier (1e6 * 1e6 / 1e6)
Nombre flottant
Éviter addition et soustraction de nombres de magnitudes =
Éviter les comparaisons pour l’égalité
Anticiper les erreurs d’arrondi
41
42. Chaine de caractères
Éviter les caractères et chaines magiques
Attention à l’accès par indice avec les méthodes utilisées
Attention au support ou non d’Unicode
Penser à la politique d’internationalisation de l’application
42
43. Autres types
Booléen
Utiliser les booléens pour documenter son programme
En nommant des expressions booléennes pour les conditions
Simplifier les tests complexes avec des booléens
Tableau/Liste
S’assurer que les indices restent dans les bornes
Attention aux indices lorsque plusieurs dimensions
43
45. Code séquentiel
Respecter l’ordre lorsqu’il a de l’importance
Rendre visible les dépendances (noms des routines, arguments)
Grouper les instructions de manière logique
45
46. Instructions conditionnelles
Traiter les cas normaux d’abord (avec le if) ensuite les cas
d’erreur (avec le else)
Faire attention à bien utiliser < ou <=
Simplifier les conditions complexes par un appel de fonction
Ordonner les cas en commençant par le plus fréquent
46
47. Instructions répétitives (1)
Boucles while, for et foreach
Un seul point d’entrée par boucle
Code d’initialisation juste avant la boucle
Boucle infinie avec while(true){}
Éviter les boucles vides
Mise à jour des variables de contrôle en début ou fin du corps
Une boucle = une fonction
47
48. Instructions répétitives (2)
S’assurer que la boucle se termine et rendre la condition de
terminaison claire et limpide
Ne pas faire le singe en changeant les variables de contrôle
pour quitter la boucle
Ne pas dépendre de la valeur des variables de contrôle après la
boucle
Utiliser les break, mais avec modération
48
49. Imbriquer des boucles
Attention aux noms des variables de contrôle
Limiter la portée des variables de contrôle à la boucle
Au grand maximum trois niveaux d’imbrication de boucles
On doit pouvoir lire la boucle de manière globale
49
50. Expressions booléennes
Comparer implicitement des booléens avec true et false
Casser des conditions trop complexes en utilisant des variables
booléennes intermédiaires ou dans une routine
Inverser la logique pour avoir la condition du if positive
Simplifier les expression booléennes avec la loi de De Morgan
! (A || B) ≡ ( !A) && ( !B)
! (A && B) ≡ ( !A) || ( !B)
50
51. Fundamental Theorem of Formatting
“A good visual layout
shows the logical
structure of a program”
“Any fool can write code that a computer can
understand. Good programmers write code that
humans can understand.” — Martin Fowler
“Any fool can write code that a computer can
understand. Good programmers write code that
humans can understand.” — Martin Fowler
52. Bon layout
Représenter la structure logique du code de manière précise et
consistante
Augmenter la lisibilité
Supporter et faciliter les modifications
En utilisant les blancs pour regrouper, et pour indenter et les
parenthèses pour clarifier les expressions
52
53. Quelques règles de layout
Une ligne de code ne devrait pas dépasser 80 caractères
Une déclaration de variable par ligne
Dans une classe : commentaire d’entête, données, méthodes
public, protected puis private
53
54. Les commentaires
Répétition du code
Explication du code
Marqueurs dans le code
Résumé du code (focus sur le pourquoi, et pas sur le comment)
Explication de l’intention du code (niveau problème et pas solution)
Information additionnelle non présente dans le code
“Good code is its own best documentation.” — Steve McConnell
54
55. Do not comment tricky code, rewrite it!Do not comment tricky code, rewrite it!
58. En résumé
Réduire la complexité
Écrire ses programmes pour des humains, pas pour la machine
Consistance et cohérence
Garder les éléments proches de leurs utilisations
Penser et coder au niveau du problème, pas de la solution
58