Le principe ouvert/fermé stipule que les entités logicielles (classes, modules, fonctions, etc.) doivent être ouvertes à l’extension, mais fermées à la modification.
Voyons comment appliquer ce principe en JavaScript.
Le problème : Code difficile à étendre
Considérons une classe Calculator qui effectue différentes opérations :
class Calculator {
calculate(num1, num2, operation) {
if (operation === 'add') {
return num1 + num2;
} else if (operation === 'subtract') {
return num1 - num2;
}
// Ajouter une nouvelle opération nécessiterait de modifier cette classe
}
}
const calc = new Calculator();
console.log(calc.calculate(10, 5, 'add')); // 15
console.log(calc.calculate(10, 5, 'subtract')); // 5
Cette implémentation viole le principe ouvert/fermé car pour ajouter une nouvelle opération, nous devrions modifier la classe Calculator.
La solution : Utiliser le polymorphisme et la composition
Appliquons le principe ouvert/fermé en utilisant une stratégie de design pattern :
class Operation {
perform(num1, num2) {
throw new Error("La méthode perform doit être implémentée");
}
}
class Addition extends Operation {
perform(num1, num2) {
return num1 + num2;
}
}
class Subtraction extends Operation {
perform(num1, num2) {
return num1 - num2;
}
}
class Calculator {
constructor() {
this.operations = new Map();
}
registerOperation(name, operation) {
this.operations.set(name, operation);
}
calculate(num1, num2, operationName) {
const operation = this.operations.get(operationName);
if (!operation) {
throw new Error(`Opération non supportée: ${operationName}`);
}
return operation.perform(num1, num2);
}
}
// Utilisation
const calc = new Calculator();
calc.registerOperation('add', new Addition());
calc.registerOperation('subtract', new Subtraction());
console.log(calc.calculate(10, 5, 'add')); // 15
console.log(calc.calculate(10, 5, 'subtract')); // 5
// Ajouter une nouvelle opération sans modifier Calculator
class Multiplication extends Operation {
perform(num1, num2) {
return num1 * num2;
}
}
calc.registerOperation('multiply', new Multiplication());
console.log(calc.calculate(10, 5, 'multiply')); // 50
Avantages de cette approche
- Extensibilité : Nous pouvons ajouter de nouvelles opérations sans modifier la classe Calculator.
- Maintainabilité : Chaque opération est encapsulée dans sa propre classe, facilitant les tests et les modifications.
- Flexibilité : Les opérations peuvent être ajoutées ou retirées dynamiquement.
En appliquant le principe ouvert/fermé, nous avons créé un système plus flexible et plus facile à maintenir. Notre Calculator est maintenant ouverte à l’extension (nous pouvons ajouter de nouvelles opérations) mais fermée à la modification (nous n’avons pas besoin de changer le code existant pour ajouter ces nouvelles opérations).