|
| 1 | +# Réutilisabilité et maintenabilité |
| 2 | + |
| 3 | +Dans l’odyssée du développement logiciel, deux qualités font la différence entre un projet éphémère et une œuvre qui traverse le temps : la réutilisabilité et la maintenabilité. Un code réutilisable est un trésor, car il évite de réinventer la roue. Un code maintenable, lui, est une promesse de sérénité pour l’équipe qui devra le faire évoluer, corriger ou adapter. Ces principes, souvent cités, prennent tout leur sens à travers des pratiques concrètes et des exemples vécus. |
| 4 | + |
| 5 | +## DRY (Don’t Repeat Yourself) |
| 6 | + |
| 7 | +La duplication est l’ennemie du progrès. Chaque répétition de code est une source potentielle d’erreur, un piège pour celui qui devra un jour tout modifier. Le principe DRY nous invite à centraliser la logique, à n’avoir qu’une seule source de vérité pour chaque information ou comportement. |
| 8 | + |
| 9 | +**Exemple :** |
| 10 | + |
| 11 | +```java |
| 12 | +// Mauvais exemple : duplication |
| 13 | +public double surfaceCercle(double rayon) { |
| 14 | + return 3.14159 * rayon * rayon; |
| 15 | +} |
| 16 | +public double perimetreCercle(double rayon) { |
| 17 | + return 2 * 3.14159 * rayon; |
| 18 | +} |
| 19 | + |
| 20 | +// Bon exemple : extraire la constante |
| 21 | +public static final double PI = 3.14159; |
| 22 | +public double surfaceCercle(double rayon) { |
| 23 | + return PI * rayon * rayon; |
| 24 | +} |
| 25 | +public double perimetreCercle(double rayon) { |
| 26 | + return 2 * PI * rayon; |
| 27 | +} |
| 28 | +``` |
| 29 | + |
| 30 | +Un changement de formule ou de constante ne doit se faire qu’à un seul endroit, sous peine de voir le code diverger et les bugs proliférer. |
| 31 | + |
| 32 | + |
| 33 | +## Single Source of Truth (SSOT) |
| 34 | + |
| 35 | +Dans un système bien conçu, chaque information n’existe qu’à un seul endroit. Multiplier les copies, c’est s’exposer à l’incohérence et à la confusion. Le principe SSOT est le garant de la cohérence des données. |
| 36 | + |
| 37 | +**Exemple :** |
| 38 | + |
| 39 | +Imaginons une application qui communique avec une API distante. Si l’URL de cette API est dupliquée dans plusieurs classes, une modification future (changement de domaine, de version, etc.) risque d’être oubliée à un endroit, provoquant des bugs difficiles à diagnostiquer. |
| 40 | + |
| 41 | +```java |
| 42 | +// Mauvais exemple : l’URL de l’API est dupliquée |
| 43 | +class UserService { |
| 44 | + private static final String API_URL = "https://api.exemple.com/v1/"; |
| 45 | + // ... |
| 46 | +} |
| 47 | +class ProductService { |
| 48 | + private static final String API_URL = "https://api.exemple.com/v1/"; |
| 49 | + // ... |
| 50 | +} |
| 51 | + |
| 52 | +// Bon exemple : une seule source de vérité |
| 53 | +class Config { |
| 54 | + public static final String API_URL = "https://api.exemple.com/v1/"; |
| 55 | +} |
| 56 | +class UserService { |
| 57 | + // Utilise Config.API_URL |
| 58 | +} |
| 59 | +class ProductService { |
| 60 | + // Utilise Config.API_URL |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +En centralisant l’information, on garantit la cohérence et on simplifie la maintenance. |
| 65 | + |
| 66 | + |
| 67 | +## Separation of Concerns (Séparation des préoccupations) |
| 68 | + |
| 69 | +Un bon logiciel est comme un orchestre : chaque instrument a son rôle, chaque musicien sa partition. En séparant les responsabilités, on obtient un code plus lisible, plus testable, plus évolutif. |
| 70 | + |
| 71 | +**Exemple :** |
| 72 | + |
| 73 | +```java |
| 74 | +// Mauvais exemple : une classe fait tout |
| 75 | +class Application { |
| 76 | + void afficherUI() {} |
| 77 | + void sauvegarderDonnees() {} |
| 78 | + void envoyerEmail() {} |
| 79 | +} |
| 80 | + |
| 81 | +// Bon exemple : séparation des responsabilités |
| 82 | +class UIManager { void afficherUI() {} } |
| 83 | +class DataManager { void sauvegarderDonnees() {} } |
| 84 | +class EmailService { void envoyerEmail() {} } |
| 85 | +``` |
| 86 | + |
| 87 | +Chaque classe, chaque module, doit avoir une mission claire et limitée. |
| 88 | + |
| 89 | + |
| 90 | +## Encapsulation |
| 91 | + |
| 92 | +L’encapsulation, c’est l’art de protéger les secrets de fabrication. En cachant les détails internes d’une classe, on limite les risques d’erreur et on rend le code plus robuste face aux changements. |
| 93 | + |
| 94 | +**Exemple :** |
| 95 | + |
| 96 | +```java |
| 97 | +// Mauvais exemple : attributs publics |
| 98 | +class Compte { |
| 99 | + public double solde; |
| 100 | +} |
| 101 | + |
| 102 | +// Bon exemple : attribut privé et méthodes d’accès |
| 103 | +class Compte { |
| 104 | + private double solde; |
| 105 | + public double getSolde() { return solde; } |
| 106 | + public void deposer(double montant) { solde += montant; } |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +L’utilisateur de la classe n’a accès qu’à ce qui est nécessaire, et rien de plus. |
| 111 | + |
| 112 | + |
| 113 | +## Information Hiding (Masquage d’information) |
| 114 | + |
| 115 | +Proche de l’encapsulation, le masquage d’information vise à limiter l’exposition des détails d’implémentation. Moins on en montre, moins on risque de devoir tout changer si l’intérieur évolue. |
| 116 | + |
| 117 | +**Exemple :** |
| 118 | + |
| 119 | +```java |
| 120 | +// Mauvais exemple : exposer la structure interne |
| 121 | +class Stack { |
| 122 | + public Object[] elements; |
| 123 | +} |
| 124 | + |
| 125 | +// Bon exemple : masquer la structure |
| 126 | +class Stack { |
| 127 | + private Object[] elements; |
| 128 | + public void push(Object o) { /* ... */ } |
| 129 | + public Object pop() { /* ... */ } |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | + |
| 134 | +## Composition Over Inheritance (Préférer la composition à l’héritage) |
| 135 | + |
| 136 | +L’héritage est parfois tentant, mais il peut rendre le code rigide et difficile à faire évoluer. La composition, elle, offre souplesse et modularité. En assemblant des objets, on construit des systèmes plus adaptables. |
| 137 | + |
| 138 | +**Exemple :** |
| 139 | + |
| 140 | +Imaginons que l’on souhaite modéliser des périphériques bureautiques : certaines imprimantes peuvent aussi scanner ou faxer, d’autres non. Si l’on utilise l’héritage, on se retrouve vite avec une hiérarchie complexe et peu flexible. La composition permet d’assembler dynamiquement les fonctionnalités nécessaires. |
| 141 | + |
| 142 | +```java |
| 143 | +// Mauvais exemple : héritage rigide |
| 144 | +class Imprimante { |
| 145 | + void imprimer() {} |
| 146 | +} |
| 147 | +class ImprimanteMultifonction extends Imprimante { |
| 148 | + void scanner() {} |
| 149 | + void faxer() {} |
| 150 | +} |
| 151 | + |
| 152 | +// Bon exemple : composition flexible |
| 153 | +interface Scanner { |
| 154 | + void scanner(); |
| 155 | +} |
| 156 | +interface Fax { |
| 157 | + void faxer(); |
| 158 | +} |
| 159 | +class Imprimante { |
| 160 | + void imprimer() {} |
| 161 | +} |
| 162 | +class ImprimanteMultifonction { |
| 163 | + private Imprimante imprimante; |
| 164 | + private Scanner scanner; |
| 165 | + private Fax fax; |
| 166 | + ImprimanteMultifonction(Imprimante imprimante, Scanner scanner, Fax fax) { |
| 167 | + this.imprimante = imprimante; |
| 168 | + this.scanner = scanner; |
| 169 | + this.fax = fax; |
| 170 | + } |
| 171 | + void imprimer() { imprimante.imprimer(); } |
| 172 | + void scanner() { scanner.scanner(); } |
| 173 | + void faxer() { fax.faxer(); } |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +Avec la composition, on peut créer des objets multifonctions ou spécialisés sans multiplier les sous-classes, et faire évoluer les fonctionnalités indépendamment les unes des autres. |
0 commit comments