Ce post fait suite à une introduction sur les Code Contracts publié précedement.
Les codes contracts permettent de spécifier et contrôler des règles au sein de votre code. Dans l’optique de la création d’API, il est interessant d’utiliser les Code Contract au sein de classe mais il est encore plus judicieux de les utiliser sur des interfaces (notamment pour les tests unitaires / IoC …). Nous allons donc voir comment réaliser des “code contracts” sur des interfaces.
- Pour cela mettons nous en situation …
public interfaceIPerson
{
void SetAge(int age);
}
public interfaceICustomer : IPerson
{
void SetId(int Id);
}
public classPerson: IPerson
{
protected int Age { get; set; }
public void SetAge(int age)
{
this.Age = age;
}
}
public classCustomer: Person,ICustomer
{
protected int Id { get; set; }
public void SetId(int id)
{
this.Id = id;
}
}
Nous créons deux interfaces IPerson pouvant modifier l’age d’une personne et ICustomer dérivant de IPerson (donc on pourra modifier son age aussi) où l’on pourra modifier son Identifiant client. Ces deux interfaces ont été implémenté dans deux classes Person et Customer. Si on prend le scénario de la création d’une API, le créateur de l’API aura défini et manipulera les interfaces et le développeur utilisant l’API, aura implémenté les deux classes.
- Comment le créateur de l’API peut définir des spécifications sur les paramètres de ses interfaces ?
Le but pour le créateur de l’API est de fournir un modèle de développement. Il peut ainsi indiquer les méthodes et les types à utiliser mais ne peut pas avoir plus de précision quand au contenu des paramètres. On a vu, qu’avec Code contract, il peut définir ces informations au sein des méthodes. Cela reviendrait à créer une classe abstrait (pas toujours pertinent surtout dans un langage où il n’y a pas d’héritage multiple comme le C#).
Pour définir un code contract sur une interface, quatre étapes sont nécessaires :
- Créer une classe contrat héritant el ‘interface sur laquelle on souhaite faire le contrat.
- Implémenter les méthodes de l’interface dans la classe de contrat en fournissant les code contracts.
- Décorer l’interface avec l’attribut ContractClass en fournissant le type de la classe de contract
- Décorer la classe avec l’attribut ContractClassFor en fournissant le nom des interface qui sont régit par le contrat
[ContractClass(typeof(PersonContracts))] public interface IPerson { void SetAge(int age); } [ContractClassFor(typeof(IPerson))] public class PersonContracts : IPerson { public void SetAge(int age) { Contract.Requires(age > 0 && age < 120, "L'age doit être compris entre 1 et 120 ans."); } }
Le tour est joué. Ainsi pour le code exemple devient :
[ContractClass(typeof(PersonContracts))] public interface IPerson { void SetAge(int age); } [ContractClassFor(typeof(IPerson))] public class PersonContracts : IPerson { public void SetAge(int age) { Contract.Requires(age > 0 && age < 120, "L'age doit être compris entre 1 et 120 ans."); } } [ContractClass(typeof(CustomerContracts))] public interface ICustomer : IPerson { void SetId(int Id); } [ContractClassFor(typeof(ICustomer))] public class CustomerContracts : PersonContracts,ICustomer { public void SetId(int Id) { Contract.Requires(Id > 0, "L'identifiant doit être supérieur à 0."); } }
Le code ci dessus est fait par le créateur de l’API.
public class Person : IPerson { protected int Age { get; set; } public void SetAge(int age) { this.Age = age; } } public class Customer : Person,ICustomer { protected int Id { get; set; } public void SetId(int id) { this.Id = id; } }
Le code ci dessus est fait par l’utilisateur de l’API. Il n’a pas à se préoccuper des contrats définis dans l’API et peut définir ses propres contracts.