Dans cet article, nous allons explorer en profondeur le Open Closed Principle (OCP), un des cinq principes SOLID définis par Robert C. Martin. Nous verrons pourquoi ce principe est crucial pour écrire un code propre, maintenable et extensible, et nous l’illustrerons avec des exemples concrets en Java, en mettant en lumière les erreurs courantes et les bonnes pratiques.
🚀 Comprendre l’Open Closed Principle
Le principe Open/Closed stipule que les entités logicielles (classes, modules, fonctions, etc.) doivent être ouvertes à l’extension mais fermées à la modification.
🔹 Ouvert à l’extension : On doit pouvoir ajouter du comportement sans modifier le code existant.
🔹 Fermé à la modification : Une fois une classe stable, elle ne doit pas être modifiée directement pour éviter d’introduire des bugs.
Ce principe vise à éviter l’effet domino où chaque modification d’une classe impacte plusieurs parties du code, entraînant une cascade de modifications et de tests.
❌ Mauvaise Approche : Violation du Open/Closed Principle
Prenons un exemple classique : un système de calcul de remise pour différents types de clients.
💣 Code qui ne respecte pas OCP
class DiscountService {
public double calculateDiscount(String customerType, double amount) {
if (customerType.equals("REGULAR")) {
return amount * 0.05; // 5% de réduction
} else if (customerType.equals("PREMIUM")) {
return amount * 0.1; // 10% de réduction
} else if (customerType.equals("VIP")) {
return amount * 0.2; // 20% de réduction
}
return 0;
}
}
📌 Problèmes de cette implémentation :
- Violation d’OCP : Chaque fois qu’un nouveau type de client doit être ajouté, il faut modifier la classe
DiscountService
. - Maintenance difficile : Ajouter un nouveau type de réduction implique d’altérer du code existant, risquant d’introduire des régressions.
- Violation du Single Responsibility Principle (SRP) :
DiscountService
gère plusieurs responsabilités (logique métier et gestion des types de clients).
✅ Bonne Approche : Respect du Open/Closed Principle
La solution consiste à introduire l’abstraction pour éviter de modifier DiscountService
. Utilisons le polymorphisme et l’interface pour permettre l’extension sans modification.
🎯 Refactoring avec OCP
interface DiscountStrategy {
double applyDiscount(double amount);
}
🔹 Implémentations concrètes
class RegularDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.05; // 5% de réduction
}
}
class PremiumDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.1; // 10% de réduction
}
}
class VIPDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.2; // 20% de réduction
}
}
🔹 Service respectant OCP
class DiscountService {
public double calculateDiscount(DiscountStrategy strategy, double amount) {
return strategy.applyDiscount(amount);
}
}
🔹 Utilisation
public class Main {
public static void main(String[] args) {
DiscountService discountService = new DiscountService();
double amount = 100.0;
DiscountStrategy regular = new RegularDiscount();
DiscountStrategy premium = new PremiumDiscount();
DiscountStrategy vip = new VIPDiscount();
System.out.println("Réduction Regular : " + discountService.calculateDiscount(regular, amount));
System.out.println("Réduction Premium : " + discountService.calculateDiscount(premium, amount));
System.out.println("Réduction VIP : " + discountService.calculateDiscount(vip, amount));
}
}
📌 Avantages de cette approche :
✅ Respecte OCP : Plus besoin de modifier DiscountService
pour ajouter une nouvelle stratégie.
✅ Extensible : On peut facilement ajouter une NewYearDiscount
, BlackFridayDiscount
, etc., en créant une nouvelle classe implémentant DiscountStrategy
.
✅ Code plus propre et lisible : La logique de calcul est séparée et chaque type de réduction a sa propre classe.
🛠️ Open/Closed en Action : Ajouter une Nouvelle Stratégie
Imaginons qu’on veuille ajouter une réduction spéciale pour les soldes de Noël.
🔹 Sans OCP : Il faudrait modifier DiscountService
, tester toutes les autres réductions et s’assurer de ne pas avoir cassé d’autres fonctionnalités.
🔹 Avec OCP : On ajoute simplement une nouvelle classe ChristmasDiscount
:
class ChristmasDiscount implements DiscountStrategy {
public double applyDiscount(double amount) {
return amount * 0.15; // 15% de réduction
}
}
Aucune modification du code existant n’est nécessaire ! 🚀
🌟 Conclusion
Le principe Open/Closed permet de :
✔ Minimiser l’impact des changements
✔ Rendre le code plus modulaire et évolutif
✔ Faciliter la maintenance et les tests
En appliquant ce principe avec des interfaces, l’héritage et le polymorphisme, on s’assure que notre code est ouvert à l’extension sans toucher au code existant.