Code Contracts Tutorial: What Are Mixins And How To Use Them

in #utopian-io7 years ago (edited)

What Will I Learn?

I continue my code contracts series. In this tutorial, I am going to explain what mixins are and how to use them.

Requirements

CodeContracts tools for .NET

Difficulty

Intermediate

Tutorial Contents

  • Adding Contract Information to Interfaces
  • Inheritance and Contracts

Curriculum

What Are Mixins And How To Use Them

In the context of object-oriented languages an evergreen question regards the alleged best way to abstract some functionality. Should you use a base class or is, instead, an interface preferable? As you know, in most languages, including the C# language, there's no middle way. However, there are some programming languages that support a new construct - half a class and half an interface. This construct is known as a mixin. The most effective way to grab the gist of a mixin is thinking of it as an interface with a few implemented methods. Two popular languages that support mixins are Python and Ruby.

So what's the purpose of talking mixins here in the context of an article about Code Contracts? Ideally, there's nothing to prevent you from adding Code Contracts rules to the definition of an interface. If you do so, though, you end up adding a few lines of code to each member of an interface. This is not a supported scenario in C# and other .NET programming languages as it would configure a mixin rather than an interface.

So can you use Code Contracts to set up some expectations about the behavior of an interface? And if so, how would you do that? The quick answer is yes; you can definitely use Code Contracts constructs to define preconditions and postconditions of interface members. However, because .NET languages don't support the concept of a mixin, you need to resort to some tricks.

Adding Contract Information to Interfaces

The code contract of an interface is not specified within the body of the interface itself. You code the interface as usual using the interface keyword and listing the signatures of contracted members. When you're done you simply decorate the interface block with a special attribute, as shown below: 

[ContractClass( typeof(CustomerRepositoryContract) )]
interface ICustomerRepository
{
   Customer FindById(string id);
   IList<Customer> GetAll();
   IList<Customer> GetByCountry(string country);
   bool IsValid(Customer customer);
   bool Update(Customer customer);
   :
}

Applied to an interface type, the ContractClass attribute indicates that a separate type defines the code contract for the interface. In other words, an intermediate class is necessary to hold the contract for an interface. Note that the same restriction applies to abstract methods defined on abstract classes.

With regard to the preceding example, what does the CustomerRepositoryContract class look like? Let's find it out. 

using System.Collections.Generic;
using System.Diagnostics.Contracts;
[ContractClassFor(typeof(ICustomerRepository))]
class CustomerRepositoryContract : ICustomerRepository
{
   Customer ICustomerRepository.FindById(string id)
   {
       Contract.Requires(id != null);
       Contract.Requires(id.Length == 5);
       return default(Customer);
   }
   IList<Customer> ICustomerRepository.GetAll()
   {
       Contract.Ensures(null != Contract.Result<IList<Customer>>());
       return default(IList<Customer>);
   }
   IList<Customer> ICustomerRepository.GetByCountry(string country)
   {
       Contract.Ensures(null != Contract.Result<IList<Customer>>());
       Contract.Requires(country != null);
       Contract.Requires(country.Length > 5);
       return default(IList<Customer>);
   }
   bool ICustomerRepository.IsValid(Customer customer)
   {
      Contract.Requires(customer!= null);
      return default(bool);
   }
   bool ICustomerRepository.Update(Customer customer)
   {
       Contract.Requires(customer != null);
       return default(bool);
   }
}

The contract class for an interface is primarily a class that implements the interface. If the contract class is specific of an abstract class then it is expected to derive from the abstract class itself. In addition, the contract class must be marked with the ContractClassFor attribute.

The interface implementation is required to be an explicit implementation. An explicit implementation of the interface occurs when all the members of the interface are prefixed in the source code of the class with the interface type. This is just what happens in the preceding code listing. Note that going for an explicit implementation is not an option as Code Contracts tools specifically demand for an explicit interface implementation. The reason is that in this way the tools can associate any invoked members with members on the interface with no ambiguity. With an explicit interface implementation callers can only access methods after casting the object to the interface. Instead, an implicit implementation would allow callers to access the interface members as if they were part of the class. As you can see, this may be source of ambiguity for the static checkers tool.

What kind of code should you have in the methods of the contract class for the interface? In first place, you have all the Code Contracts instructions you reckon valid for your purposes. Next, you need to return some valid value. The recommended approach is that you use the default value for the type via the C# default(T) keyword, as shown below. 

bool ICustomerRepository.Update(Customer customer)
{
  Contract.Requires(customer != null);
  return default(bool);
}

No other significant code is expected to be in this class except perhaps for a simple assignment to a local variable of the current instance. This is acceptable when you need to reuse this variable in subsequent contracts, as shown in the example below. 

bool ICustomerRepository.Update(Customer customer)
{
  ICustomerRepository repo = this;
  Contract.Requires(customer != null);
  Contract.Requires(repo.IsValid(customer));
  return default(bool);
}

Without first saving the current instance to a local variable specifying the contract that involves the member IsValid would have required the following code: 

Contract.Requires( ((ICustomerRepository) this).IsValid(customer) );

It goes without saying that the readability of the code is much better if a local assignment is used. You get a warning for this, but it is an acceptable situation.

Inheritance and Contracts

A contract defined on a class - whether a concrete or abstract type - or associated with an interface is automatically inherited by derived classes. This happens regardless of whether you actually invoke base methods in any overrides.

Note that the Liskov's substitution principle holds true for Code Contracts in much the same way it holds for plain classes. 

The Liskov's principles says the following:

Subclasses should always be substitutable for their base classes.

In terms of the Code Contracts API this means that a derived class (or a class that implements a contract-based interface) should not expect more preconditions as the parent. Consider the following example: 

public virtual int Test(int input)
{
   Contract.Requires(input > 0);
   Contract.Ensures(input > 0);
   return input;
}

The Test method defines one precondition - input value greater than zero - and ensures one postcondition - return value is greater than zero. What if you override the method on a derived class? Let's add the following code and compile 

public override int Test(int input)
{
   Contract.Requires(input > 10);
   return base.Test(input);
}

The method override adds a new rule thus making the set of preconditions even stronger than in the parent class. This apparently reasonable change violates the Liskov's principle. (Note that violations to the Liskov's principles are among the most common causes of software deterioration and what I like to call the biodegradability of classes.) More in detail, you are in a situation in which the Customer class requires that method Test get a value greater than zero. Thanks to polymorphism, however, you can pass an instance of PreferredCustomer wherever type Customer is accepted. However, as of the previous code, method Test of PreferredCustomer requires that input values are greater than 10. In other words, a value of 1 is acceptable if Customer is used, but not if PreferredCustomer is used.

What if, instead, the derived class adds a weaker condition such as 'method Test accepts also some negative numbers'? The tool might even be able to detect weaker conditions, but what would be the rationale for that? If it is important that a given class has all of its conditions verified you just enforce it in the signature of callers.

So to be on the safe side, and avoid the intricacies and subtlety of inheritance, the Code Contracts API just doesn't allow adding preconditions when a virtual method is overridden. The following code therefore doesn't compile. 

public override int Test(int input)
{
   Contract.Requires(input > 10);  // DOESN'T COMPILE
   return base.Test(input);
}

You can't add preconditions to overridden methods.

At the same time, you are allowed to add postconditions to an overridden method. 

public override int Test(int input)
{
   Contract.Ensures(input > 10);   
   return base.Test(input * 10);
}

The bottom line is that the Code Contracts API is fully aware of the Liskov's principle which can be effectively summarized as "Expect no more, provide no less."



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Good job brother.

Hey @carver I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x

Coin Marketplace

STEEM 0.19
TRX 0.15
JST 0.029
BTC 63834.78
ETH 2627.38
USDT 1.00
SBD 2.78