2013-10-16 5 views
1

J'ai eu une discussion avec un collègue concernant l'utilisation des contrats de code pour effectuer les vérifications des prérequis.Validation des propriétés des paramètres avec les contrats de code

Disons que nous avons le code suivant:

namespace Project 
{ 
    using System; 
    using System.Diagnostics.Contracts; 

    public class Thing 
    { 
     public string Foo { get; set; } 

     public int Bar { get; set; } 
    } 

    public class ThingsManipulator 
    { 
     public void AddThing(Thing thing) 
     { 
      Contract.Requires<ArgumentNullException>(thing != null); 

      // Do something 
     } 
    } 
} 

Si dans le // Do something j'accéder thing.Foo et thing.Bar de faire les choses, dois-je les valider aussi par le biais des contrats de code?

public void AddThing(Thing thing) 
{ 
    Contract.Requires<ArgumentNullException>(thing != null); 
    Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(thing.Foo)); 
    Contract.Requires<ArgumentException>(thing.Bar > 0); 

    // Do something 
} 

Mon collègue dit que seul le paramètre dans son ensemble doit être vérifié (nous ne placer le premier contrat), je pense que les méthodes doivent vérifier ce qu'ils utilisent peu importe si c'est le paramètre tout ou une de ses propriétés (c'est-à-dire que nous devrions tous les trois contrats).

Veuillez noter que je comprends et suis d'accord avec le fait que si les propriétés d'un paramètre doivent toujours remplir une exigence, cette exigence doit être placée dans les vérifications invariantes de l'objet. Ce que je veux dire sont des valeurs qui sont généralement valides, mais pas valables pour une méthode particulière (par exemple dans l'exemple ci-dessus thing.Bar peut contenir heureusement des valeurs négatives, mais AddThing ne les aime pas). Mon collègue dit dans ces cas que la signature de la méthode doit explicitement utiliser tous les éléments qu'elle utilise au lieu d'un seul objet (par exemple, AddThing(string thingFoo, int thingBar)), et lancer les vérifications.

Alors:

  • Doit-on valider quelle méthode utilise ou uniquement les paramètres dans son ensemble et « exploser » les paramètres?
  • Y a-t-il une raison technique de le faire (peu importe ce que "ça" veut dire) ou est-ce une question de préférence?

Je n'ai pas été en mesure de trouver des lignes directrices à ce sujet dans the manual, peut-être que j'ai raté quelque chose?

+1

Je pense que dans votre cas, la validation du nom et de l'âge de l'étudiant devrait être la responsabilité du constructeur de 'Student' et/ou des setters. –

Répondre

6

Il est utile de considérer les contrats non pas comme des éléments de validation, mais comme des énoncés de faits que le vérificateur tente de prouver. Vous êtes essentiellement dire "tant que mes entrées sont correctes (Contract.Requires), puis les propriétés suivantes (Contract.Ensures, Contract.Assert, Contract.Invariant)".

Les contrats doivent être des déclarations qui devraient toujours être vrai sur le contexte dans lequel ils sont utilisés. Dans votre exemple, vous dites que Foo est nécessaire d'une chose à être vide que quand ils sont utilisés par un ThingsManipulator.

Si vous pouvez dire qu'un Thing's Foo est toujours requis pour être non vide alors c'est toujours vrai d'une chose et les contrats appartiennent à Thing.

Dans ce cas, vous ne pouvez pas, donc je pense que cela devient un problème de conception OO sur les paramètres.Réflexion à voix haute:

  1. Les méthodes doivent avoir le moins de paramètres possible (voir https://stackoverflow.com/a/175035/1554471).
  2. AddThing étant une méthode publique, il devrait probablement traiter des abstractions de plus haut niveau, pas des détails comme les types de valeurs de Framework.
  3. AddThing est actuellement la seule méthode qui a ces restrictions sur Thing.
  4. Conservez donc un seul paramètre de type Thing, et vérifiez ses propriétés.

Si (3) cessé d'être vrai, alors il pourrait être utile de créer un nouveau type qui décore ou dérive par ailleurs de chose, et de mettre les contrats là-bas, pour éviter le problème de repeating yourself. Mais pour le moment, YAGNI.

+0

Merci pour votre contribution, je suis entièrement d'accord avec vous que le contrôle qui tient toujours doit être placé dans la méthode invariante de l'objet. Ce dont je parlais, ce sont des contrôles qui ne peuvent pas être appliqués globalement, qui sont pertinents pour une seule méthode (mon mauvais, j'ai choisi un mauvais exemple là-haut, je l'ai refaçonné et étendu la question). – Albireo

Questions connexes