Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Fathi Bellahcene

.Net m'a tuer!

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:

image

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:

image

 

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 :
Posted: dimanche 25 décembre 2011 22:38 par fathi

Commentaires

smo a dit :

Il est amusant de noter que, ISP ou pas, les interfaces contiennnent deux "énaurmes" bugs de conception. Le premier "GerErrors" à la place de "GetErrors". Vu qu'en théorie, on ne doit jamais modifier une interface, on est fichu pour l'éternité avec une bête erreur de typo. La deuxième erreur, c'est que cette fonction renvoie un List[string] et non un IList[string], voire un IEnumerable[string]. Et ça c'est pas SOLID du tout. Bon. je tatillone sans doute :-), et c'est un exemple, mais en réalité, beaucoup de projets super ambitieux, SOLID, injection de dépendance et tout le tralala, contiennent plein de bugs idiots comme ça...

Simon.

# décembre 27, 2011 20:42

fathi a dit :

Hello,

les interfaces contiennent deux "énaurmes" bugs

&gt;&gt;ET encore tu est gentil! mon exemple est certainement ce qu'il y a de pire a faire en terme de design: data, validation, persistance au même endroit (Active Record), propriété qui s'appelle "Guid", propriété "State" de type string!!!...mais tout ça je le précise bien dans le post en disant que le but de cet exemple est de simplifier la compréhension du principe (j'aurai aussi pu appeler mes méthodes Foo1, Foo2.., Toto1, Toto2)et que l'exemple contient des erreurs de design.

Plus concrètement, si je devais corriger toutes ces erreurs et si on en reviens au principe ISP: si les interfaces sont séparées (comme dans la figure 2) on a une meilleur maitrise du code à modifier/corriger que dans le premier cas car on ne s’intéresse qu'aux clients et aux classes qui utilisent réellement ces méthodes (on a plus de "couplage" entre les différents clients): c'est tout l"enjeux du principe.

Donc, même si le code n'est pas parfait, respecter SOLID ça a du sens.

mais en réalité, beaucoup de projets super ambitieux, SOLID, injection de dépendance et tout le tralala, contiennent plein de bugs idiots comme ça...

&gt;&gt; D'accord avec toi, c'est pas parce que tu met une casaque et un jockey sur un âne que ça en fait un cheval de course.

# décembre 29, 2011 14:56
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01