ISP: Interface Segregation Principle
Ce post fait partie d’une serie se proposant de présenter les principes SOLID. Vous trouverez l’introducton ainsi que les liens vers les autres articles
ici
.
Définition:
Un client ne doit jamais être forcé de dépendre d’une interface qu’il n’utilise pas (Robert C. Martin).
Ce principe est somme toute assez simple et ressemble beaucoup au principe de responsabilité unique. L’idée sous jacente étant de limiter au maximum le couplage en limitant le “bruit” généré par la modification d’une interface.
Exemple:
Prenons un exemple simple avec l’interface suivante:

Alors oui, cet exemple est mauvais car on ne respecte pas le principe de responsabilité unique et que c’est une très mauvaise idée que d’avoir la persistance au même endroit que le stockage des informations….mais on est dans un exemple dont le but est la compréhension du principe.
On a donc une interface IBusinessObject qui présente des groupes de méthodes que l’on peut facilement regrouper en deux parties:
- Persistance des données
- validations
Maintenant, supposons que nous ayons deux clients distincts qui s’intéressent à la validation et à la persistance:
1: public class ValidationManager
2: {
3: public void ValidateBusinessObjects(List<IBusinessObject> businessObjects)
4: {
5: if (businessObjects != null)
6: {
7: foreach (IBusinessObject bo in businessObjects)
8: {
9: bo.Validate();
10: }
11: }
12: }
13: }
14:
15: public class PersistManager
16: {
17: public void InsertAll(List<IBusinessObject> businessObjects)
18: {
19: if (businessObjects != null)
20: {
21: foreach (IBusinessObject bo in businessObjects)
22: {
23: bo.Insert();
24: }
25: }
26: }
27: }
Nos deux classes sont couplés à l’interface IBusinessObject, et donc chaque modification de cette interface est susceptible d’avoir un impact sur ces deux classes…même si l’impact ne concerne pas le boulot effectué par la classe.
Une solution serait donc d’avoir une structure différente:

De cette manière, si un client doit dépendre de la partie liée à la validation, il utilisera l’interface IValidatable, la persistance: l’interface IPersistable…et des deux parties l’interface IBusinessObject.
Ca nous donne donc le code suivant:
1: public class ValidationManager
2: {
3: public void ValidateBusinessObjects(List<IValidatable> validatableObjects)
4: {
5: if (validatableObjects != null)
6: {
7: foreach (IValidatable vo in validatableObjects)
8: {
9: vo.Validate();
10: }
11: }
12:
13: }
14: }
15:
16: public class PersistManager
17: {
18: public void InsertAll(List<IPersistable> persistableObjects)
19: {
20: if (persistableObjects != null)
21: {
22: foreach (IPersistable po in persistableObjects)
23: {
24: po.Insert();
25: }
26: }
27: }
28: }
On casse donc ainsi le couplage indirect entre les classes PersistManager et ValidationManager.
Conclusion:
Ce principe met donc en exergue des défaut de design qui ont le mauvais gout d’ajouter du couplage inutile. Dans une expérience très récente, nous avons été confrontés à ce genre de problème et nous avons eu pas mal d’impact sur le code du SI que nous avions à gérer (ajout de dépendances sur des dll inutiles, ajout de code…inutile!): une interface de très bas niveau comportant plusieurs dizaines de méthodes complètements disjointes imposait un couplage extrêmement couteux alors qu’une simple réorganisation de cette interface aurait suffit pour ne pas générer du couplage et donc du code inutile. C’est donc un principe simple, facile à comprendre, facile à corriger…on a donc aucune raison de ne pas l’appliquer!
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :