SOLID – Open / Close

Le principe “ouvert / fermé”. C’est le deuxième du principe SOLID. Celui-ci me semble être l’un des moins connus mais pas forcément par “oubli” mais plutôt par flemmardise aiguë. Parce qu’une fois qu’on le comprend / connaît, il n’est pas le plus simple à suivre et il demande une grande rigueur. Bien évidemment, comme beaucoup de bonnes pratiques, de méthodes, etc … Il faut aussi savoir se les approprier et les adapter.

L’open / close nous dit que : un objet ( en OOP ) doit être ouvert à l’extension et fermé à la modification. Vous ne rêvez, si l’on doit prendre ce principe au pied de la lettre, toute class écrite ne doit pas être modifiée mais étendue. C’est dingue ? Même s’il y a un bug ? Evidemment que non, je ne pense pas que l’on puisse se permettre de laisser un bug évident dans une class sous prétexte qu’une fois écrite, elle est gravée dans le marbre. Vous pourriez donc vous demander l’intérêt d’un tel principe ?

L’idée est de fournir au client, aux autres développeurs, des objets que l’on pourrait définir de “certifier”, qui détiennent une forme de contrat, tout en permettant à chacun de venir y greffer une amélioration ( On reviendra sur cette notion de contrat ! C’est une autre notion du principe SOLID sur lequel tout s’articule ). Typiquement, prenons les Transformers ! ( désolé pour les non adeptes ) : Optimus Prime n’a pas d’ailes ! Pourtant, dans Transformers 2, lors du combat final, il est amélioré et détient un module lui permettant de voler. On n’a pas modifié le “coeur” d’Optimus Prime mais on lui a simplement permi, durant un instant, de s’améliorer pour un cas spécifique : poutrer les Decepticons. Comment a-t-il pu récupérer ces ailes ? C’est comme un contrat lui permettant cette possibilité d’accepter et d’ajouter ces ailes ( cf. le I de SOLID qu’on verra plus tard ).

Prenons un exemple concret. Imaginons que nous avons un objet “Order” qui sait gérer la commande d’un client sur votre site ecommerce (et elle ne fait que ça ! on n’oublie pas les autres principes comme la responsabilité unique )

On a donc un objet Order sur lequel on peut lui ajouter des produits et surtout, on va s’intéresser à sa méthode “getAmount” qui retourne le prix total de la commande. Ici, pas d’alertes sur la responsabilité unique de cette class, tout va bien. Il y aurait des choses à redire concernant d’autres principes mais restons focus sur l’O/C.

Maintenant, disons que le logiciel évolue et que vous souhaitez pouvoir effectuer une réduction globale à votre commande. Que faire ? Rajouter une méthode dans la class qui s’occupe de gérer la réduction ? Modifier la méthode getAmount pour gérer le cas ? Grand dieu… NON ! Cette class est minimaliste ! Elle marche, elle sait faire déjà des choses, vous pouvez la tester facilement, on ne va pas la toucher. Et par expérience, il est de bon goût d’être aussi minimaliste quand vous faites de la OOP même si évidemment, il ne faut pas non plus faire de l’over-engineering et toujours s’adapter à son contexte de développement ( expérience + compétences + temps + budget ). Et tout simplement… Non parce que cet objet est ouvert à l’extension alors, fermez-le à la modification : le principe d’open / close. Alors la grande question…

Comment étendre un objet et ne pas le modifier ?

Pour cela, il y a plusieurs possibilités dont notamment une, peut-être l’a plus connue : l’héritage. Dans un premier temps et pour cet article, c’est cette méthode que je vais vous présenter. Mais, sachez qu’il n’y a pas que l’héritage et qu’il existe d’autres façons de faire qui sont bien plus ingénieuses, notamment grâce aux design patterns. Nous reprendrons d’ailleurs cet exemple pour que je puisse vous présenter l’un d’entre eux : le décorateur.

En revanche, l’héritage mériterait lui aussi son article parce que c’est un fondamental de la programmation orientée objet. Or, on ne fait pas tout et n’importe quoi avec les fondamentaux. Chaque mot-clé, chaque écriture, chaque façon de faire ont une raison. Le mot extends n’est pas là juste pour vous “faire plaisir” de procéder à l’héritage d’un objet vers un autre. Comme j’aime le dire, faire de la programmation orientée objet, c’est avant tout une philosophie. Mais, c’est vrai dans tous les types de programmations. Pour bien les maîtriser, il faut en comprendre les fondamentaux. Trêve de bavardage, voyons la solution que je vous propose en code.

Et bien voilà! L’objet Order n’a pas bougé d’un poil, extraordinaire ? Nous avons donc recréé un objet OrderDiscount qui hérite d’Order. Il va donc récupérer toute la logique et le comportement initial d’une commande et va venir l’améliorer avec sa propre méthodologie. On retrouve ici le fait de faire une surcharge. L’idée est de venir améliorer la méthode “getAmount” en allant chercher le résultat initial que l’on peut récupérer à travers la méthode parente sur Order et ensuite, y appliquer la réduction que sait faire OrderDiscount.

Order est donc ouvert à l’extension et fermé à la modification. Nous ne l’avons pas touché et libre à chacun de toujours utiliser l’objet Order ou éventuellement notre objet OrderDiscount pour y appliquer une réduction.