Cours JavaScript et TypeScript · Module 5: JavaScript avancé
Bonnes pratiques
Dans cette leçon3
Cette fois, le Dr Ian Malcolm, accompagné de toute l'équipe de Jurassic Park, t'a préparé un briefing essentiel. Après plusieurs incidents dans le parc, dont la fameuse panne système et l'évasion de dinosaures, l'équipe a élaboré un ensemble de bonnes pratiques de programmation qui pourraient éviter des catastrophes similaires à l'avenir.
Des principes qui auraient pu sauver Jurassic Park
1. Valider toutes les données d'entrée
Problème du parc : Le système ne vérifiait pas correctement les données de température des incubateurs d'œufs de dinosaures, ce qui a entraîné une reproduction incontrôlée des velociraptors.
Solution :
1// Mauvais :
2function setIncubatorTemperature(temperature) {
3 incubator.temperature = temperature;
4 // Et si la température est trop élevée ? Ou une chaîne au lieu d'un nombre ?
5}
6
7// Bon :
8function setIncubatorTemperature(temperature) {
9 // Vérifie que la température est un nombre
10 if (typeof temperature !== 'number') {
11 throw new Error('La température doit être un nombre');
12 }
13
14 // Vérifie que la température est dans la plage sûre
15 if (temperature < 20 || temperature > 40) {
16 throw new Error('Température hors de la plage sûre (20-40°C)');
17 }
18
19 incubator.temperature = temperature;
20}2. Écrire des tests automatisés
Problème du parc : Des modifications du système de sécurité ont introduit des bugs qui n'ont pas été détectés avant la mise en production.
Solution :
1// Test unitaire du système de contrôle des clôtures
2describe('FenceControlSystem', () => {
3 it('devrait activer la clôture quand une menace est détectée', () => {
4 const fenceSystem = new FenceControlSystem();
5 fenceSystem.detectThreat({ level: 'HIGH', sector: 'B2' });
6 expect(fenceSystem.getStatus('B2')).toBe('ACTIVE');
7 });
8
9 it('devrait alerter si la tension de la clôture est trop basse', () => {
10 const fenceSystem = new FenceControlSystem();
11 const alert = fenceSystem.checkVoltage(5000); // en dessous du minimum
12 expect(alert.type).toBe('VOLTAGE_WARNING');
13 });
14});3. Principe de responsabilité unique (SRP)
Problème du parc : Le système de contrôle principal essayait de tout faire - surveiller les dinosaures, contrôler les clôtures, gérer les visiteurs, traiter les paiements. Un seul bug pouvait faire tomber tout le système.
Solution :
1// Mauvais - une seule classe qui en fait trop
2class JurassicParkSystem {
3 monitorDinosaurs() { /* ... */ }
4 controlFences() { /* ... */ }
5 manageVisitors() { /* ... */ }
6 processPayments() { /* ... */ }
7 generateReports() { /* ... */ }
8}
9
10// Bon - des classes séparées et ciblées
11class DinosaurMonitor {
12 trackLocation(id) { /* ... */ }
13 checkHealth(id) { /* ... */ }
14}
15
16class FenceController {
17 activate(sector) { /* ... */ }
18 checkVoltage(sector) { /* ... */ }
19}
20
21class VisitorManager {
22 checkIn(visitorId) { /* ... */ }
23 evacuate(sector) { /* ... */ }
24}4. Toujours gérer les erreurs
Problème du parc : Des erreurs non gérées faisaient planter le système silencieusement, sans activer les protocoles d'urgence.
Solution :
1// Mauvais :
2async function checkDinosaurStatus(id) {
3 const dino = await getDinosaur(id);
4 return dino.status; // Et si getDinosaur lève une erreur ?
5}
6
7// Bon :
8async function checkDinosaurStatus(id) {
9 try {
10 const dino = await getDinosaur(id);
11 return dino.status;
12 } catch (error) {
13 if (error instanceof DinoNotFoundError) {
14 console.warn(`Dinosaure ${id} introuvable - il a peut-être été transféré`);
15 return 'unknown';
16 }
17 // Erreur inconnue - relance pour l'appelant
18 throw new SystemError(`Échec de la vérification de l'état du dinosaure : ${error.message}`);
19 }
20}5. Documenter ton code
Problème du parc : Après le départ de l'ingénieur en chef, plus personne ne savait exactement comment fonctionnait le système de contrôle d'accès.
Solution :
1/**
2 * Vérifie le niveau d'accès d'un employé aux zones du parc.
3 *
4 * @param {Object} employee - Objet contenant les données de l'employé
5 * @param {string} employee.id - Identifiant unique de l'employé
6 * @param {string} employee.firstName - Prénom de l'employé
7 * @param {string} employee.lastName - Nom de l'employé
8 * @param {number} employee.accessLevel - Niveau d'accès de l'employé (1-5)
9 * @param {string} zone - Identifiant de la zone à laquelle l'employé tente d'accéder
10 * @returns {boolean} - Renvoie true si l'employé a l'accès, false sinon
11 *
12 * @example
13 * // Vérifie si le Dr Wu a accès au laboratoire
14 * const drWu = { id: '001', firstName: 'Henry', lastName: 'Wu', accessLevel: 5 };
15 * const access = checkAccess(drWu, 'laboratory');
16 * console.log(access); // true
17 */
18function checkAccess(employee, zone) {
19 // Carte des niveaux d'accès requis pour différentes zones
20 const requiredLevel = {
21 'public': 1,
22 'administration': 2,
23 'herbivore enclosures': 3,
24 'predators': 4,
25 'laboratory': 5
26 };
27
28 // Vérifie si la zone existe
29 if (!requiredLevel.hasOwnProperty(zone)) {
30 console.warn(`Zone inconnue : ${zone}`);
31 return false;
32 }
33
34 // Compare le niveau d'accès de l'employé avec l'exigence de la zone
35 return employee.accessLevel >= requiredLevel[zone];
36}6. Écrire du code lisible
Problème du parc : Le code génétique écrit par le Dr Wu était si alambiqué que personne d'autre ne comprenait exactement comment les dinosaures hybrides étaient créés.
Solution :
1// Mauvais :
2function ms(d,g,s) {
3 const r = [];
4 for(let i=0;i<d.length;i++) {
5 if(d[i].g === g && d[i].t > s) {
6 r.push(d[i]);
7 }
8 }
9 return r;
10}
11
12// Bon :
13/**
14 * Filtre les dinosaures par espèce et niveau de force minimal
15 */
16function filterDinosaursBySpeciesAndStrength(dinosaurs, species, minStrength) {
17 return dinosaurs.filter(dinosaur =>
18 dinosaur.species === species && dinosaur.strength > minStrength
19 );
20}7. Éviter l'état global
Problème du parc : Le système de contrôle climatique utilisait des variables globales pour stocker les réglages, ce qui permettait à un bug dans le module de ventilation d'affecter tout le système climatique.
Solution :
1// Mauvais :
2let globalTemperature = 22;
3let globalHumidity = 70;
4
5function setJungleTemperature() {
6 // Modifie les variables globales
7 globalTemperature = 28;
8 globalHumidity = 90;
9}
10
11function setLabTemperature() {
12 // Modifie aussi les mêmes variables globales !
13 globalTemperature = 20;
14 globalHumidity = 50;
15}
16
17// Que se passe-t-il si les deux fonctions sont appelées presque en même temps ?
18
19// Bon :
20class ClimateController {
21 constructor() {
22 this.zones = new Map();
23 }
24
25 setZoneParameters(zoneId, temperature, humidity) {
26 this.zones.set(zoneId, { temperature, humidity });
27 }
28
29 getZoneParameters(zoneId) {
30 return this.zones.get(zoneId);
31 }
32}
33
34const climateController = new ClimateController();
35
36climateController.setZoneParameters('jungle', 28, 90);
37climateController.setZoneParameters('laboratory', 20, 50);
38
39// Maintenant chaque zone a ses propres réglages indépendants8. DRY (Don't Repeat Yourself)
Problème du parc : Le code de détection des menaces était dupliqué à travers plusieurs systèmes, si bien qu'une correction de bug dans un système n'était pas appliquée aux autres.
Solution :
1// Mauvais :
2function checkThreatAtDoors() {
3 if (sensors.motion > 10 && sensors.sound > 80 && !isDaytime()) {
4 raiseAlarm('Activité suspecte aux portes !');
5 }
6}
7
8function checkThreatAtFence() {
9 // La même logique est répétée !
10 if (sensors.motion > 10 && sensors.sound > 80 && !isDaytime()) {
11 raiseAlarm('Activité suspecte à la clôture !');
12 }
13}
14
15// Bon :
16function detectSuspiciousActivity() {
17 return sensors.motion > 10 && sensors.sound > 80 && !isDaytime();
18}
19
20function checkThreatAtDoors() {
21 if (detectSuspiciousActivity()) {
22 raiseAlarm('Activité suspecte aux portes !');
23 }
24}
25
26function checkThreatAtFence() {
27 if (detectSuspiciousActivity()) {
28 raiseAlarm('Activité suspecte à la clôture !');
29 }
30}9. Utiliser le contrôle de version
Problème du parc : L'équipe IT n'utilisait pas de contrôle de version, donc après l'introduction d'une nouvelle fonctionnalité qui s'est avérée contenir des bugs, il n'y avait pas de moyen simple de revenir à la version précédente qui fonctionnait.
Solution :
1# Initialise un dépôt Git
2git init
3
4# Ajoute les fichiers au dépôt
5git add .
6
7# Premier commit
8git commit -m "Version initiale du système Jurassic Park"
9
10# Crée une nouvelle branche pour développer une nouvelle fonctionnalité
11git checkout -b new-feeding-system
12
13# Travaille sur la fonctionnalité...
14
15# Sauvegarde les changements
16git add modified-files
17git commit -m "Ajout du système de nourrissage automatisé pour les prédateurs"
18
19# Fusionne les changements dans la branche principale après les tests
20git checkout main
21git merge new-feeding-system
22
23# En cas de problème, reviens rapidement à la version précédente
24git revert HEAD10. Appliquer les revues de code
Problème du parc : Dennis Nedry a introduit une porte dérobée dans le système de sécurité que personne n'a remarquée, faute de processus de revue de code.
Approche d'équipe :
- Chaque modification doit être relue par au moins un autre développeur
- Les tests automatisés doivent passer avant de fusionner le code
- Applique une checklist lors des revues :
- Le code respecte-t-il les standards ?
- Les problèmes de sécurité potentiels ont-ils été traités ?
- La documentation a-t-elle été mise à jour ?
- Les tests couvrent-ils le nouveau code ?
Le coût d'ignorer les bonnes pratiques
Comme on le sait grâce à l'histoire de Jurassic Park, ignorer les bonnes pratiques de programmation peut mener à des conséquences catastrophiques :
- Perte de données : Du matériel génétique de dinosaure inestimable perdu à cause de bugs système
- Failles de sécurité : Des pannes système entraînant l'évasion de créatures dangereuses
- Temps d'arrêt : Fermeture du parc à cause de problèmes techniques
- Difficultés de développement : Incapacité à introduire rapidement de nouvelles fonctionnalités
- Problèmes de maintenance : Du code difficile à comprendre et à corriger
Exercice
Imagine que tu es le développeur principal de Jurassic Park. Identifie tous les problèmes potentiels dans le code ci-dessous et réécris-le selon les bonnes pratiques :
1// Système de surveillance des dinosaures de Jurassic Park
2var dinos = [];
3var alarms = 0;
4
5function add(name, type, area, dangerLvl) {
6 dinos.push({n: name, t: type, a: area, d: dangerLvl, h: 100});
7 console.log('ajouté : ' + name);
8}
9
10function check() {
11 for (var i=0; i<dinos.length; i++) {
12 if (dinos[i].h < 50) {
13 console.log('ATTENTION ! Le dinosaure ' + dinos[i].n + ' a une santé faible !');
14 alarms = alarms + 1;
15 }
16
17 if (dinos[i].a != dinos[i].lockArea && dinos[i].d > 3) {
18 console.log('DANGER ! Prédateur hors de la zone !');
19 alarms = alarms + 1;
20 }
21 }
22
23 if (alarms > 0) alert('On a des problèmes...');
24}
25
26// Exemple d'utilisation
27add('Rex', 'T-Rex', 'B5', 5);
28add('Tri', 'Triceratops', 'A2', 2);
29dinos[0].h = 30;
30dinos[0].a = 'X3'; // Hors de la zone assignée !
31check();Souviens-toi, dans le monde de la programmation comme à Jurassic Park - la vie trouve toujours un chemin... pour révéler les solutions mal pensées ! C'est pourquoi tu dois appliquer les bonnes pratiques pour rendre ton code sûr, maintenable et efficace.
Code de cette leçon : main.js
1// Bonnes pratiques avec les modules - Jurassic Park
2console.log("Bonnes pratiques avec les modules\n");
3
4// ===========================================
5// 1. STRUCTURE DE DOSSIERS CLAIRE
6// ===========================================
7
8console.log("=== 1. Structure du projet ===\n");
9console.log(`jurassic-park/
10├── src/
11│ ├── models/
12│ │ ├── Dinosaur.js
13│ │ ├── Enclosure.js
14│ │ └── index.js
15│ ├── services/
16│ │ ├── DinosaurService.js
17│ │ ├── SecurityService.js
18│ │ └── index.js
19│ ├── utils/
20│ │ ├── validators.js
21│ │ ├── formatters.js
22│ │ └── index.js
23│ ├── config/
24│ │ └── constants.js
25│ └── index.js
26└── package.json`);
27
28// ===========================================
29// 2. RESPONSABILITE UNIQUE PAR MODULE
30// ===========================================
31
32console.log("\n=== 2. Single Responsibility ===\n");
33
34// BIEN - chaque module fait une seule chose
35const DinosaurValidator = {
36 validate(data) {
37 return data.name && data.species;
38 }
39};
40
41const DinosaurFormatter = {
42 format(dinosaur) {
43 return `${dinosaur.name} - ${dinosaur.species}`;
44 }
45};
46
47// MAUVAIS - un module en fait trop
48const DinosaurEverything = {
49 validate() {},
50 format() {},
51 save() {},
52 load() {},
53 update() {},
54 delete() {}
55};
56
57console.log("Des modules a responsabilite unique");
58console.log("Evite les 'god objects'");
59
60// ===========================================
61// 3. NOMMAGE
62// ===========================================
63
64console.log("\n=== 3. Conventions de nommage ===\n");
65
66console.log("BIEN :");
67console.log(" DinosaurService.js - classe/service");
68console.log(" constants.js - constantes");
69console.log(" utils.js - fonctions utilitaires");
70console.log(" index.js - re-export");
71
72console.log("\nMAUVAIS :");
73console.log(" dino.js - peu clair");
74console.log(" stuff.js - trop generique");
75console.log(" temp.js - fichiers temporaires");
76
77// ===========================================
78// 4. DEPENDENCY INJECTION
79// ===========================================
80
81console.log("\n=== 4. Dependency Injection ===\n");
82
83// BIEN - les dependances comme parametres
84class ParkManager {
85 constructor(dinosaurService, securityService) {
86 this.dinosaurService = dinosaurService;
87 this.securityService = securityService;
88 }
89}
90
91// MAUVAIS - dependances codees en dur
92class BadParkManager {
93 constructor() {
94 this.dinosaurService = new DinosaurService();
95 this.securityService = new SecurityService();
96 }
97}
98
99console.log("Injecte les dependances");
100console.log("Ne cree pas les dependances a l'interieur");
101
102// ===========================================
103// 5. N'EXPORTE QUE LE NECESSAIRE
104// ===========================================
105
106console.log("\n=== 5. Surface d'API minimale ===\n");
107
108// BIEN - n'exporte que l'API publique
109const DinosaurModule = {
110 // Public
111 createDinosaur(name) {
112 return _createInternal(name);
113 },
114
115 getDinosaur(id) {
116 return _findById(id);
117 }
118};
119
120// Fonctions privees (non exportees)
121function _createInternal(name) {
122 return { name, id: Math.random() };
123}
124
125function _findById(id) {
126 return { id };
127}
128
129console.log("Surface publique minimale");
130console.log("N'exporte pas les helpers internes");
131
132// ===========================================
133// 6. DOCUMENTATION
134// ===========================================
135
136console.log("\n=== 6. Documentation des modules ===\n");
137
138/**
139 * Dinosaur Service
140 * Handles all dinosaur-related operations
141 *
142 * @module DinosaurService
143 * @exports {Object} Service object with public methods
144 *
145 * @example
146 * import DinosaurService from './DinosaurService';
147 * const dino = DinosaurService.create('Rex');
148 */
149const DinosaurService = {
150 /**
151 * Creates a new dinosaur
152 * @param {string} name - Dinosaur name
153 * @returns {Object} Dinosaur object
154 */
155 create(name) {
156 return { name };
157 }
158};
159
160console.log("JSDoc pour les API publiques");
161console.log("README pour les modules plus importants");
162
163console.log("\n=== RECAPITULATIF DES BONNES PRATIQUES ===\n");
164console.log("1. Structure de dossiers claire");
165console.log("2. Responsabilite unique par module");
166console.log("3. Noms de fichiers clairs");
167console.log("4. Dependency Injection");
168console.log("5. Exports minimaux");
169console.log("6. Documentation");
170console.log("7. Des tests pour chaque module");
171console.log("8. Evite les dependances circulaires");
172
173console.log("\nDes modules propres = un meilleur code !");Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Que signifie le principe de responsabilité unique (Single Responsibility Principle) ?
2. Pourquoi la validation des données d'entrée est-elle cruciale en programmation ?
Tâches pratiques dans le jeu
- Tri vertical
Ordonne les étapes de création d'un code fiable, de la première à la dernière :