Open Closed Principle

Open Closed Principle : Écrire un Code Extensible sans Risque

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 :

  1. Violation d’OCP : Chaque fois qu’un nouveau type de client doit être ajouté, il faut modifier la classe DiscountService.
  2. Maintenance difficile : Ajouter un nouveau type de réduction implique d’altérer du code existant, risquant d’introduire des régressions.
  3. 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.