Dernière mise à jour:

Le principe Ouvert/Fermé

Cédric Gérard
Cédric Gérard Code

Second principe appartenant à la gamme SOLID, le principe ouvert/fermé (Open/Closed principle) encourage la conception de logiciels extensibles sans nécessité de modifications directs sur le code existant.

Le Principe Ouvert/Fermé est formulé par Bertrand Meyer et stipule : “Les entités logicielles (classes, modules, fonctions, etc.) doivent être ouvertes à l’extension, mais fermées à la modification”. En d’autres termes, cela signifie que vous devez concevoir votre code de manière à ce qu’il puisse être étendu pour répondre à de nouveaux besoins ou fonctionnalités, sans avoir à modifier directement le code existant.

Respecter ce principe est extrêmement puissant puisque les éléments de votre code deviennent interchangeables facilement et sans impact au-delà du composant qu’on ajoute. On peut voir ça comme le fait de remplacer une batterie de voiture par une plus performante, cela se fait sans avoir à démonter le moteur ni le moindre risque pour le reste des éléments… À condition de respecter les spécifications de la batterie.

Prenons un cas concret pour illustrer les avantages de ce principe. Imaginons que nous ayons un système qui représente une machine à café. Cette machine permet de réaliser les cafés suivants :

Classes représentant les cafés

Voici maintenant le code de la machine à café :

Code de la machine à caffé

On remarque que dans cette implémentation de la machine à café, on sera obligé de modifier cette classe à chaque modification concernant les cafés. Il faudra rajouter une clause dans la sélection pour introduire un nouveau café. Il faudra également modifier ce code si la façon de préparer un café change. Imaginons qu’on rajoute une étape à la préparation d’un cappuccino. Il faudra alors aller dans la classe machine à café pour introduire cette nouvelle étape.

Ici, la machine à café, n’est pas fermée aux modifications. C’est le seul moyen d’étendre son comportement. Elle est également couplée à la préparation de chaque café.

Voici maintenant un refactoring qui permet de suivre le principe ouvert/fermé en partant du même problème.

Tout d’abord, on introduit une interface, Coffee, dont l’objectif est de garantir un contrat unique avec la machine à café.

Interface pour les cafés

Maintenant, tout nos cafés vont implémenter cette interface et vont donc répondre au même contrat.

Les cafés étendant l'interface Coffee

Vous remarquerez que seule la méthode qui vient de l’interface est publique. Les autres méthodes ont été passées en privé. En faisant cela, on encapsule les détails concernant chaque café dans leur propre classe.

Voici maintenant l’impact sur notre machine à café :

La nouvelle version de la machine à café

L’introduction de l’interface nous a permis de retirer toute la complexité de la préparation de chaque café de la classe. Maintenant, notre machine à café est protégée des modifications des autres classes. Je vous vois venir, vous allez me dire qu’on a toujours le bloc conditionnel pour la sélection d’un café. Donc, en ajoutant un nouveau type de café, on devra obligatoirement modifier le code de la machine à café.

Je ne vais pas vous mentir, dans un cas celui-là, on aura toujours, quelque part, besoin de faire le lien entre le paramètre de la sélection et le café à préparer. Il aura donc besoin d’un bout de code similaire à celui-là pour le faire.

Il y a néanmoins une amélioration intéressante à proposer pour la classe CoffeeMachine.

La version SRP de la machine à café

Ici, on extrait simplement la sélection du café dans une classe dédiée. On introduit ce sélecteur comme une dépendance de la machine à café. Maintenant, quel que soit le changement qu’on introduit, ajout d’un nouveau style de café ou modification dans la préparation d’un café, il n’y aura plus d’impact sur la classe CoffeeMachine. Cette classe respect bien le principe Open/Close. Elle est ouverte à l’extension, on peut lui passer un autre type de sélecteur ou introduire de nouveau café facilement. En revanche, elle est fermée aux modifications qui viennent de l’extérieur.

Notez au passage que ce refactoring nous a permis de respecter le Single Responsibility Principle dont je parle ici.

Pour finir sur le code voici un exemple de sélecteur de café. Cette classe sera toujours dépendante de l’introduction de nouveau type de café et devra être modifiée en conséquence. Pour ce genre de classe, c’est un compromis qu’on fait. Cela ne pose pas de problème tant que la seule responsabilité de cette classe, c’est de faire le mapping entre un paramètre et la classe qu’elle doit fournir.

Le selecteur de café

Conclusion

Ce qu’il faut avoir en tête pour respecter ce principe, c’est qu’une entité logicielle doit pouvoir être impacté sans que son code n’ait besoin de changer. Pour ça, il faut que son comportement puisse être étendu. Dans le cas de notre machine à café, c’est l’introduction d’une interface commune à tous les cafés et d’un sélecteur qui nous permet d’étendre les capacités de notre machine sans avoir à modifier son propre code.

Ce qui est intéressant dans cet exemple, pourtant simple, c’est que pour rendre le code compatible avec le principe ouvert/fermé nous avons du faire appel à des techniques de programmation qui correspondent aux trois derniers principes de SOLID. Nous verrons cela dans les posts à suivre sur le sujet.

Cédric Gérard

Cédric Gérard

Je suis dans l'informatique depuis tout jeune. D'abord intéressé par le hardward (montage, overcloking), j'ai mis du temps à trouver ma voie. Je suis tombé dans le développement en 2007, je n'ai jamais arrêté depuis..

Aujourd'hui, je suis développeur web avec une plus grande appétence pour le backend. J’accorde beaucoup d’attention à la valeur apportée aux utilisateurs finaux. On ne réalise pas d'application que pour se faire plaisir, après tout.

Je mets aussi un point d'honneur à livrer du code de qualité en m'appuyant sur les bonnes pratiques du développement logiciel et je défends les valeurs du software craftmanship.

L'agilité est également un élément essentiel pour un travail fiable et efficace. Je ne parle pas de méthode, mais de l'état d'esprit prôné par l'agilité.

J'aime partager mes compétences et j'ai une appétence particulière pour l'encadrement des développeurs juniors.

Je suis également en quête de sens, aucune technologie étant une fin en elle-même, j'ai besoin de savoir pourquoi je travaille et qu'elle est la valeur produite.

Articles en relation

Le principe d'injection de dépendance

Le principe de ségrégation des interfaces

Le principe de substitution de Liskov