Download - Code Complete
![Page 1: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/1.jpg)
Code CompleteSébastien Combéfis
Lundi 29 février 2016
![Page 2: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/2.jpg)
Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative CommonsAttribution – Pas d’Utilisation Commerciale – Pas de Modification 4.0 International.
![Page 3: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/3.jpg)
Contexte
W. Strunk et E. B. White
1918B. W. Kernighan et P. J. Plauger
1974
3
![Page 4: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/4.jpg)
Qualité de code
4
![Page 5: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/5.jpg)
“Always code as if the guy who ends up maintainingyour code will be a violent psychopath who knowswhere you live.” — Martin Golding
“Always code as if the guy who ends up maintainingyour code will be a violent psychopath who knowswhere you live.” — Martin Golding
![Page 6: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/6.jpg)
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
![Page 7: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/7.jpg)
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 statuts7
![Page 8: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/8.jpg)
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
Semantic8
![Page 9: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/9.jpg)
Code de qualité
Focus sur la programmation orientée objet
Analyse top-down
Depuis la classe jusqu’aux instructions
Classe Méthode Instruction
9
![Page 10: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/10.jpg)
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
![Page 11: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/11.jpg)
Programmer avec des classes
Abstract Data Type
ADT
Cacher les détailsd’implémentation
Rendre les changementsindépendants du restedu programme
Le programme estauto-documenté
Le programme manipuledes entités qui modélisentle monde réel
11
![Page 12: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/12.jpg)
Manipuler les objets du monde
getFuelLevel
isOnGroundstartEngine
startAutopilot
![Page 13: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/13.jpg)
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”
13
![Page 14: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/14.jpg)
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
![Page 15: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/15.jpg)
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 unespé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 Stel que S est un sous-type de T .
15
![Page 16: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/16.jpg)
Composition ou héritage ? II
Vehicle
Car Wheel
is-a
has-a
16
![Page 17: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/17.jpg)
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
![Page 18: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/18.jpg)
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
![Page 19: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/19.jpg)
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
![Page 20: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/20.jpg)
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
![Page 21: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/21.jpg)
Avoid God classesalso known as The Blob
Toute puissante et qui sait tout
Passe son temps à appeler des get et set
![Page 22: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/22.jpg)
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
![Page 23: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/23.jpg)
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
![Page 24: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/24.jpg)
Pourquoi rassembler des instructions ?
Cohésion fonctionnelleUne routine = une opération
Cohésion séquentielleSéquence d’instructions qui partagent des données
Cohésion communicationnelleOpérations qui utilisent la même donnée
Cohésion temporelleOpérations qui doivent avoir lieu en même temps
24
![Page 25: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/25.jpg)
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
![Page 26: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/26.jpg)
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
![Page 27: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/27.jpg)
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
![Page 28: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/28.jpg)
Protégez-vous !
Données provenant de sources externesValeurs des paramètres des routines
![Page 29: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/29.jpg)
Gestion des erreurs ou assertion ?
Gestion des erreursÀ utiliser pour des situations particulières qui pourraient seproduire
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
![Page 30: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/30.jpg)
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
![Page 31: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/31.jpg)
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 <> Robustness31
![Page 32: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/32.jpg)
One variable = One purposeOne variable = One purpose
![Page 33: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/33.jpg)
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
![Page 34: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/34.jpg)
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 pourlesquelles 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
![Page 35: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/35.jpg)
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
![Page 36: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/36.jpg)
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
![Page 37: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/37.jpg)
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 etc0nf, GREAT et 6REAT, tex2html et texZhtml...
37
![Page 38: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/38.jpg)
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
![Page 39: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/39.jpg)
Data types
![Page 40: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/40.jpg)
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
![Page 41: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/41.jpg)
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 6=
Éviter les comparaisons pour l’égalité
Anticiper les erreurs d’arrondi
41
![Page 42: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/42.jpg)
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
![Page 43: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/43.jpg)
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
![Page 44: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/44.jpg)
Writing programs is like writing books
![Page 45: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/45.jpg)
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
![Page 46: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/46.jpg)
Instructions conditionnelles
Traiter les cas normaux d’abord (avec le if) ensuite les casd’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
![Page 47: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/47.jpg)
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
![Page 48: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/48.jpg)
Instructions répétitives (2)
S’assurer que la boucle se termine et rendre la condition determinaison claire et limpide
Ne pas faire le singe en changeant les variables de contrôlepour quitter la boucle
Ne pas dépendre de la valeur des variables de contrôle après laboucle
Utiliser les break, mais avec modération
48
![Page 49: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/49.jpg)
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
![Page 50: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/50.jpg)
Expressions booléennes
Comparer implicitement des booléens avec true et false
Casser des conditions trop complexes en utilisant des variablesboolé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
![Page 51: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/51.jpg)
Fundamental Theorem of Formatting
“A good visual layoutshows the logical
structure of a program”
“Any fool can write code that a computer canunderstand. Good programmers write code thathumans can understand.” — Martin Fowler
“Any fool can write code that a computer canunderstand. Good programmers write code thathumans can understand.” — Martin Fowler
![Page 52: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/52.jpg)
Bon layout
Représenter la structure logique du code de manière précise etconsistante
Augmenter la lisibilité
Supporter et faciliter les modifications
En utilisant les blancs pour regrouper, et pour indenter et lesparenthèses pour clarifier les expressions
52
![Page 53: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/53.jpg)
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éthodespublic, protected puis private
53
![Page 54: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/54.jpg)
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
![Page 55: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/55.jpg)
Do not comment tricky code, rewrite it!Do not comment tricky code, rewrite it!
![Page 56: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/56.jpg)
56
![Page 57: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/57.jpg)
57
![Page 58: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/58.jpg)
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
![Page 59: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/59.jpg)
À suivre
http://geek-and-poke.com/
http://www.codinghorror.com/blog/
59
![Page 60: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/60.jpg)
Livres de référence I
ISBN 978-0-735-61967-8 ISBN 978-0-132-35088-4 ISBN 978-0-596-51004-6
60
![Page 61: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/61.jpg)
Livres de référence II
ISBN 978-0-201-61622-4 ISBN 978-0-321-35668-0 ISBN 978-0-201-48567-7
61
![Page 62: Code Complete](https://reader034.vdocuments.net/reader034/viewer/2022042516/55a8cc351a28ab06628b47ed/html5/thumbnails/62.jpg)
Créditshttps://www.flickr.com/photos/toolmantim/2887927385https://www.flickr.com/photos/smitty/2245445147http://www.flickr.com/photos/tetezinharomana/7152072635/http://www.flickr.com/photos/jetstarairways/6769120131/http://www.flickr.com/photos/47104521@N08/4332849715/http://www.flickr.com/photos/dunechaser/2936384313/http://www.flickr.com/photos/sillydog/287354869/https://www.flickr.com/photos/small_realm/15375289074http://www.flickr.com/photos/j1x4r/4313734090/http://www.flickr.com/photos/focx/5485671820/http://www.flickr.com/photos/cyberslayer/952153409/http://geek-and-poke.com/geekandpoke/2014/7/17/simply-explainedPhotos des livres depuis Amazon
62