D'abord la définition wikipédia :
http://fr.wikipedia.org/wiki/Principe_ouvert/ferm%C3%A9
"En programmation orientée objet, le principe ouvert/fermé affirme qu'une classe doit être à la fois ouverte et fermée. Ouverte signifie qu'elle a la capacité à être étendue. Fermée signifie qu'elle peut être modifiée par extension, sans modification de son code source. L'idée est qu'une fois qu'une classe a été approuvée via des revues de code, des tests unitaires et d'autres procédures de qualifications, vous ne voulez pas changer la classe mais seulement l'étendre. En pratique, le principe ouvert/fermé fait bon usage de l'abstraction et du polymorphisme."
Suivre l'OCP revient à écrire du code que l'on peut étendre sans avoir à le modifier!
Ça peut sembler incohérent, mais non, il n'y a pas d'erreur dans cette définition.
Contrairement au SRP, l'OCP n'est pas forcement un principe à appliquer à toutes les classes.
Il faut savoir identifier les fonctionnalités amené à souvent évoluer.
A partir de la le principe revient à créer un code qui sera amené à ne plus etre modifié, en faisant une abstraction de se qui évoluera.
Maintenant qu'on à défini l'OCP passons a un exemple, on va proposer une fonctionnalité qui consiste à calculer l'air d'un rectangle.
La classe Rectangle (sans les accesseur pour plus de lisibilité) :
- private double width;
- private double height;
- }
La classe AreaCalculator avec sa methode de calcul d'air de plusieurs rectangles :
- public class AreaCalculator {
- public double area(List<Rectangle> shapes){
- double area = 0;
- area += rectangle.getWidth() * rectangle.getHeight();
- }
- return area;
- }
- }
Nous voila avec une solution qui devrait bien fonctionner. Mais, à peine quelques jours plus tard on vous demande la possibilité de calculer l'air de cercles en plus des rectangles.
Le client est roi et puis ça n'a rien de compliqué, alors on s'y colle en modifiant la classe AreaCalculator et en ajoutant une classe Circle :
- public class Circle {
- private double radius;
- }
- public class AreaCalculator {
- public double area(List<Object> shapes){
- double area = 0;
- area += rectangle.getWidth() * rectangle.getHeight();
- }else{
- Circle circle = (Circle) shape;
- }
- }
- return area;
- }
- }
Voila, vous avez répondu à l'attente du client, il est content... Mais il semble qu'il y a anguille sous roche.
En effet, on peut très justement se demander pourquoi se limiter aux rectangles et aux cercles, c'est d'ailleurs ce que se dit le client, il revient alors vers vous pour vous demander si ajouter le calcul de l'air d'un triangle serait compliqué.
Pour cela vous allez devoir modifier une nième fois la classe AreaCalculator au risque de casser ce qui fonctionne, la classe AreaCalculator n'est donc pas fermé à la modification.
Et pourtant il semble judicieux, étant donné le nombre de modification déjà apporté et les éventuelles modifications à venir, d'appliquer l'OCP sur AreaCalculator.
Pour se faire, nous allons créer un Interface Shape avec une methode area() se chargeant de calculer l'air d'un Shape.
- public interface Shape {
- public abstract double area();
- }
Maintenant on va faire en sorte que les classes Rectangle et Circle implémente l'interface Area :
- private double radius;
- @Override
- public double area() {
- }
- }
- private double width;
- private double height;
- @Override
- public double area() {
- return width * height;
- }
- }
- public class AreaCalculator {
- public double area(List<Shape> shapes){
- double area = 0;
- area += shape.area();
- }
- return area;
- }
- }
AreaCalculator et sa methode de calcul d'air est alors fermé a la modification et ouverte à l’évolution.
Entre autres les desgin pattern Strategy, Template method ou encore Visitor découlent directement de l'OCP.
Super tuto merci :)
RépondreSupprimer