Lors de la création de systèmes logiciels, il est crucial de gérer la complexité de la base de code.
Le chapitre 11 de Clean Code traite de la conception de systèmes modulaires plus faciles à entretenir et à adapter au fil du temps.
Nous pouvons utiliser des exemples JavaScript pour illustrer ces concepts.
À mesure que les systèmes se développent, ils deviennent naturellement plus complexes. Cette complexité peut rendre difficile :
Un système bien conçu doit être facile à modifier, testable et évolutif. Le secret pour y parvenir réside dans la modularité et une séparation minutieuse des préoccupations.
Au cœur de la conception de systèmes propres se trouve le principe de modularité. Vous rendez le système plus gérable en divisant un grand système en modules plus petits et indépendants, chacun ayant une responsabilité claire.
Chaque module doit encapsuler une fonctionnalité spécifique, rendant le système global plus facile à comprendre et à modifier.
Exemple : Organiser un système de panier d'achat
Imaginons un système de panier d'achat en JavaScript. Au lieu de regrouper toute la logique dans un seul fichier, vous pouvez diviser le système en plusieurs modules :
// cart.js export class Cart { constructor() { this.items = []; } addItem(item) { this.items.push(item); } getTotal() { return this.items.reduce((total, item) => total + item.price, 0); } } // item.js export class Item { constructor(name, price) { this.name = name; this.price = price; } } // order.js import { Cart } from './cart.js'; import { Item } from './item.js'; const cart = new Cart(); cart.addItem(new Item('Laptop', 1000)); cart.addItem(new Item('Mouse', 25)); console.log(`Total: $${cart.getTotal()}`);
Les responsabilités sont réparties ici : Cart gère les articles, Item représente un produit et order.js orchestre les interactions.
Cette séparation garantit que chaque module est autonome et plus facile à tester et à modifier indépendamment.
L'un des objectifs de la modularité est l'encapsulation : cacher le fonctionnement interne d'un module au reste du système.
Le code externe ne doit interagir avec un module que via son interface bien définie.
Cela facilite la modification de l'implémentation interne du module sans affecter les autres parties du système.
Exemple : Encapsulation de la logique du panier
Disons que nous voulons changer la façon dont nous calculons le total dans le panier. Peut-être que nous devons maintenant tenir compte de la taxe de vente. Nous pouvons encapsuler cette logique dans la classe Cart :
// cart.js export class Cart { constructor(taxRate) { this.items = []; this.taxRate = taxRate; } addItem(item) { this.items.push(item); } getTotal() { const total = this.items.reduce((sum, item) => sum + item.price, 0); return total + total * this.taxRate; } } // Now, the rest of the system does not need to know about tax calculations.
Les autres parties du système (comme order.js) ne sont pas affectées par les changements dans la façon dont le total est calculé. Cela rend votre système plus flexible et plus facile à entretenir.
Un problème courant dans les grands systèmes est que différentes parties du système s'enchevêtrent.
Lorsqu'un module commence à assumer trop de responsabilités, il devient plus difficile de le modifier ou de le réutiliser dans différents contextes.
Le principe de séparation des préoccupations garantit que chaque module a une responsabilité spécifique.
Exemple : traiter le paiement séparément
Dans l'exemple du panier, le traitement du paiement doit être géré dans un module séparé :
// payment.js export class Payment { static process(cart) { const total = cart.getTotal(); console.log(`Processing payment of $${total}`); // Payment logic goes here } } // order.js import { Cart } from './cart.js'; import { Payment } from './payment.js'; const cart = new Cart(0.07); // 7% tax rate cart.addItem(new Item('Laptop', 1000)); cart.addItem(new Item('Mouse', 25)); Payment.process(cart);
Désormais, la logique de paiement est séparée de la gestion du panier. Cela facilite la modification ultérieure du processus de paiement (par exemple, l'intégration avec un autre fournisseur de paiement) sans affecter le reste du système.
L'un des plus grands avantages de la modularité est que vous pouvez tester chaque module indépendamment.
Dans l'exemple ci-dessus, vous pouvez écrire des tests unitaires pour la classe Cart sans avoir à vous soucier de la façon dont les paiements sont traités.
Exemple : test unitaire du chariot
// cart.test.js import { Cart } from './cart.js'; import { Item } from './item.js'; test('calculates total with tax', () => { const cart = new Cart(0.05); // 5% tax cart.addItem(new Item('Book', 20)); expect(cart.getTotal()).toBe(21); });
Avec une séparation claire des préoccupations, chaque module peut être testé isolément, ce qui facilite le débogage et accélère le développement.
Lorsque les modules dépendent trop les uns des autres, des modifications dans une partie du système peuvent avoir des conséquences inattendues ailleurs.
Pour minimiser cela, visez un couplage lâche entre les modules.
Cela permet à chaque module d'évoluer indépendamment.
Exemple : injection de dépendances
Au lieu de coder en dur les dépendances à l'intérieur d'un module, transmettez-les comme arguments :
// cart.js export class Cart { constructor(taxRateCalculator) { this.items = []; this.taxRateCalculator = taxRateCalculator; } addItem(item) { this.items.push(item); } getTotal() { const total = this.items.reduce((sum, item) => sum + item.price, 0); return total + this.taxRateCalculator(total); } }
Cette approche rend la classe Cart plus flexible et plus facile à tester avec différents calculs de taxes.
Bon codage ! ?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!