Principes SOLID en Java : de la théorie à la pratique
SOLID est l'acronyme de cinq principes fondamentaux de la programmation orientée objet, formulés par Robert Martin (Oncle Bob). Ces principes aident les développeurs à créer un code flexible, maintenable et facilement extensible. Dans cet article, nous analyserons chaque principe SOLID avec des exemples en Java, de la théorie simple à la pratique réelle.
1. Single Responsibility Principle (SRP) — Principe de responsabilité unique
Théorie : Une classe ne doit avoir qu'une seule raison de changer. Autrement dit, chaque classe doit être responsable d'exactement une fonctionnalité.
Violation du SRP :
public class Employee { public void calculatePay() { /* calcul du salaire */ } public void saveToDatabase() { /* sauvegarde en BDD */ } public void generateReport() { /* génération du rapport */ }}Correction :
public class Employee { /* seulement les données */ }
public class PayCalculator { public void calculatePay(Employee emp) { /* ... */ }}
public class EmployeeRepository { public void save(Employee emp) { /* ... */ }}
public class ReportGenerator { public void generate(Employee emp) { /* ... */ }}Maintenant, chaque classe a une responsabilité claire et unique, ce qui simplifie les tests et la maintenance.
2. Open-Closed Principle (OCP) — Principe d'ouverture/fermeture
Théorie : Les entités logicielles (classes, modules, fonctions) doivent être ouvertes à l'extension, mais fermées à la modification.
Violation du OCP :
public class ShapeDrawer { public void draw(String shapeType) { if ("circle".equals(shapeType)) { // dessiner un cercle } else if ("rectangle".equals(shapeType)) { // dessiner un rectangle } }}Correction avec le polymorphisme :
public interface Shape { void draw();}
public class Circle implements Shape { @Override public void draw() { /* dessiner un cercle */ }}
public class Rectangle implements Shape { @Override public void draw() { /* dessiner un rectangle */ }}
public class ShapeDrawer { public void draw(Shape shape) { shape.draw(); }}Maintenant, pour ajouter une nouvelle forme (par exemple, Triangle), il n'est pas nécessaire de modifier le code existant — il suffit de créer une nouvelle classe implémentant l'interface Shape.
3. Liskov Substitution Principle (LSP) — Principe de substitution de Barbara Liskov
Théorie : Les objets d'un programme doivent être remplaçables par des instances de leurs sous-types sans altérer la correction du programme. Si S est un sous-type de T, alors les objets de type T peuvent être remplacés par des objets de type S sans perturber le fonctionnement.
Violation classique du LSP :
public class Rectangle { protected int width, height; public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } public int getArea() { return width * height; }}
public class Square extends Rectangle { @Override public void setWidth(int w) { super.setWidth(w); super.setHeight(w); } @Override public void setHeight(int h) { super.setWidth(h); super.setHeight(h); }}Un client travaillant avec Rectangle s'attend à ce que la modification de la largeur n'affecte pas la hauteur. Le carré viole cette attente. Solution : Utiliser une interface commune ou des classes séparées sans héritage.
4. Interface Segregation Principle (ISP) — Principe de ségrégation des interfaces
Théorie : Les clients ne doivent pas dépendre de méthodes qu'ils n'utilisent pas. Il est préférable d'avoir plusieurs interfaces spécialisées plutôt qu'une seule interface "grasse".
Violation du ISP :