Principes SOLID en Java : de la théorie à la pratique
\n\nSOLID est l'acronyme de cinq principes fondamentaux de la programmation orientée objet, formulés par Robert Martin (Oncle Bob). Ces principes aident à créer un code flexible, maintenable et évolutif. Dans cet article, nous analyserons chaque principe avec des exemples en Java, montrerons comment les violer et comment les appliquer correctement.
\n\nPourquoi les principes SOLID sont-ils nécessaires ?
\nSans SOLID, le code se transforme rapidement en « spaghetti ». Avez-vous déjà rencontré une situation où la modification d'une classe en casse trois autres ? Ou une méthode qui en fait trop ? SOLID résout ces problèmes. Chaque principe est une règle qui rend le code :
\n- \n
- Résistant aux changements — les nouvelles exigences ne cassent pas l'ancienne logique. \n
- Compréhensible — chaque classe est responsable d'une seule chose. \n
- Testable — facile à écrire des tests unitaires. \n
1. Single Responsibility Principle (SRP) — Principe de responsabilité unique
\nFormulation : « Une classe ne doit avoir qu'une seule raison de changer ».
\nEn termes simples : une classe ne doit faire qu'une seule chose. Si une classe a plus d'une responsabilité, elle est difficile à maintenir.
\n\nViolation du SRP (mauvais code)
\npublic class OrderService {\n public void createOrder(Order order) {\n // Logique de création de commande\n saveToDatabase(order);\n sendEmailConfirmation(order);\n }\n\n private void saveToDatabase(Order order) {\n // Sauvegarde en BDD\n }\n\n private void sendEmailConfirmation(Order order) {\n // Envoi d'email\n }\n}\nProblème : la classe est responsable à la fois de la logique métier, de l'accès à la BDD et de l'envoi de notifications. Une modification du service email nécessitera de modifier OrderService.
Implémentation correcte du SRP
\npublic class OrderService {\n private final OrderRepository repository;\n private final EmailService emailService;\n\n public OrderService(OrderRepository repo, EmailService email) {\n this.repository = repo;\n this.emailService = email;\n }\n\n public void createOrder(Order order) {\n repository.save(order);\n emailService.sendConfirmation(order);\n }\n}\n\npublic class OrderRepository {\n public void save(Order order) { /* logique BDD */ }\n}\n\npublic class EmailService {\n public void sendConfirmation(Order order) { /* logique email */ }\n}\nMaintenant, chaque classe a une seule raison de changer. OrderService change uniquement lorsque le processus métier de création de commande est modifié.
2. Open/Closed Principle (OCP) — Principe d'ouverture/fermeture
\nFormulation : « Les classes doivent être ouvertes à l'extension, mais fermées à la modification ».
\nCela signifie que vous devez ajouter de nouvelles fonctionnalités via du nouveau code, et non en modifiant le code existant.
\n\nViolation de l'OCP (mauvais code)
\npublic class AreaCalculator {\n public double calculateArea(Object shape) {\n if (shape instanceof Rectangle) {\n Rectangle r = (Rectangle) shape;\n return r.width * r.height;\n } else if (shape instanceof Circle) {\n Circle c = (Circle) shape;\n return Math.PI * c.radius * c.radius;\n }\n throw new IllegalArgumentException("Forme inconnue");\n }\n}\nPour ajouter un triangle, il faut modifier la méthode calculateArea — cela viole l'OCP.
Implémentation correcte de l'OCP
\npublic interface Shape {\n double calculateArea();\n}\n\npublic class Rectangle implements Shape {\n private double width, height;\n public Rectangle(double w, double h) { this.width = w; this.height = h; }\n @Override\n public double calculateArea() { return width * height; }\n}\n\npublic class Circle implements Shape {\n private double radius;\n