Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Fathi Bellahcene

.Net m'a tuer!

Interface-Based Programming & Code Contracts

 

Dans le post précédent, on a vue que les contrats définis sur une classe étaient reportés sur ses classes dérivés. Cela nous permet de mieux respecter le principe LSP.

Ce poste ne va pas porter sur les différentes features proposées par CC, vous les trouverez très bien documentées ici.

Mais l’utilisation de Code Contracts peut également nous permettre également d’améliorer l’utilisation d’une autre “technique” de programmation qui est très à la mode:l’Interface-based Programming.

IBS va nous permettre par exemple d’implémenter différents patterns d’injection de dépendances, mais cela permet également :

  1. d’abaisser le couplage entre nos entités :on limite le lien aux contrat définis dans l’interface.
  2. d’augmenter la modularité : on est en mesure d’intervertir les classes utilisés par d’autres (qui implémentent la même interface)
  3. et ca nous évite d’utiliser l’héritage (en .Net, l’héritage multiple est impossible).

L’idée est donc de se concentrer sur l’utilisation d’objets qui implémentent un comportement au lieu de se baser sur l’appartenance à une hiérarchie de classe.

Dans cette affaire, l’utilisation de Code Contracts peut nous être assez utile. L’idée étant d’ouvrir aux clients de nos entités la possibilité d’implémenter leur propre code pour nos traitement doit bien sûr passer par un certains  nombre de contrôle. Si on reprend l’exemple utilisé dans le post précédent, On se retrouve dans un scénario qui peut être le suivant:

 

J’ai un module de calcul d’augmentation avec un traitement par défaut :

  1. Récupération des informations
  2. calcul du nouveau salaire(salaire + augmentation)
  3. impression d’une lettre modèle

et je souhaite permettre aux différents services de pouvoir ajuster uniquement la partie qui calcul  le nouveau salaire.

Cela nous donne ca:

 

 

   1: public class EnvoiCourrierAugmentation
   2:     {
   3:         private ICalculAugmentation _CalculAugmentation;
   4:  
   5:         public ICalculAugmentation CalculAugmentation
   6:         { 
   7:             get
   8:             {
   9:                 if (_CalculAugmentation == null)
  10:                     _CalculAugmentation = new DefaultCalcul();
  11:                 return _CalculAugmentation;
  12:             }
  13:         }
  14:  
  15:         public EnvoiCourrierAugmentation(ICalculAugmentation calculAugmentation)
  16:         {
  17:             _CalculAugmentation = calculAugmentation;
  18:         }
  19:  
  20:         public void ExecuteTask( )
  21:         {
  22:             int salaire = GetSalaire(50);
  23:             int augmentation = GetAugmentation(2);
  24:  

25: int nouveauSalaire = _CalculAugmentation.Augmentation(salaire

, augmentation);

  26:  
  27:             ImprimerInfos();
  28:         }
  29:  
  30:         private void ImprimerInfos()
  31:         {
  32:          //   
  33:         }
  34:  
  35:         private int GetAugmentation(int p)
  36:         {
  37:             return p;
  38:         }
  39:  
  40:         private int GetSalaire(int s)
  41:         {
  42:             return s;
  43:         }
  44:     }

 

On a une classe qui effectue le traitement à qui on injecte un objet implémentant l’interface ICalculAugmentation (Injection par Constructeur).

Si l’objet est null, on utilise une classe par défaut  qui fait ca:

   1: public class DefaultCalcul : ICalculAugmentation
   2:    {
   3:  
   4:        public virtual int Augmentation(int salaire, int augmentation)
   5:        {
   6:            //précondition:
   7:            Contract.Requires(augmentation > 0, "Mon augmentation ne peut pas etre null!!");
   8:            //Post condition:
   9:            Contract.Ensures(Contract.Result<int>() > salaire);
  10:            
  11:            return salaire + augmentation;
  12:        }
  13:    }

 

Ici on a bien l’utilisation de CodeContract qui impose certaines conditions…mais pour pouvoir les appliquer sur d’autres classes, on va devoir hériter de cette classe…ce qu’on veut pas faire.

De plus j’ai la possibilité d’implémenter cette interface sans utiliser les contrats et donc planter le traitement (et bien sûr remettre la faute sur la personne qui à fait la classe de traitement Clignement d'œil ).

Une solution serait d’ajouter des contrats avant et après l’appel à notre calcul d’augmentation…mais cela baisse la lisibilité du code.

Code Contracts nous offre une solution “élégante” pour régler ce problème: implémenter les conditions sur l’interface et pas sur la classe! Du coup, toutes classes qui implémentera notre classe va pouvoir récupérer nos Contrats de validité.

Pour cela, on va ajouter une classe spécifique qui va définir nos contrats, on obtient donc:

Notre interface:

   1: [ContractClass(typeof(DefaultOperationClass))]
   2:   public interface ICalculAugmentation
   3:   {
   4:       
   5:       int Augmentation(int salaire, int augmentation);
   6:  
   7:   }

Notre classe avec nos contrats:

 

   1: [ContractClassFor(typeof(ICalculAugmentation))]
   2:     internal class DefaultOperationClass : ICalculAugmentation
   3:     {
   4:  
   5:         public int Augmentation(int salaire, int augmentation)
   6:         {
   7:             //précondition:
   8:             Contract.Requires(augmentation > 0, "Mon augmentation ne peut pas etre null!!");
   9:             //Post condition:
  10:             Contract.Ensures(Contract.Result<int>() > salaire);
  11:             
  12:            
  13:             return 0;
  14:         }
  15:  
  16:     }

Je met ma classe internal pour limiter sa porté…mais si vous essayer d’utiliser cette classe directement, vous aurez une erreur à la compilation: cette classe est donc réservé à la définitions des contrats…ca pour le coup c’est moche car ca pourrait être une classe de calcul par défaut par exemple.

 

Ma classe de calcul par défaut devient donc:

   1: public class DefaultCalcul : ICalculAugmentation
   2:     {
   3:  
   4:         public virtual int Augmentation(int salaire, int augmentation)
   5:         {
   6:             return salaire + augmentation;
   7:         }
   8:     }

 

Ma classe custom (la même chose)

 

   1: public class SuperOperation : ICalculAugmentation
   2:     {
   3:         public int Augmentation(int salaire, int augmentation)
   4:         {
   5:             return salaire / augmentation;
   6:         }
   7:     }

 

…qui plante quand je l’exécute:

   1: EnvoiCourrierAugmentation e = new EnvoiCourrierAugmentation(new SuperOperation());
   2:            e.ExecuteTask();

 

image

Magique!

 

Un petit bémol tout de même pour ceux qui utilisent les extensions CC comme  la possibilité de factoriser les conditions communes à plusieurs méthodes (ContractAbbreviator), cela ne fonctionne pas (erreur à la compilation) lorsque l’on définis des CC sur une interface…


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: lundi 17 janvier 2011 21:35 par fathi
Classé sous : ,

Commentaires

Pas de commentaires

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