Jamie Dixon
Jamie Dixon

Reputation: 54001

Code contracts and interface implementations - What are some reasons for the 1-1 relationship

Learning more about code contracts, they seem like something I want to use in my project.

Given the way that I'm structuring my web-service-layer (an abstraction of our service layer accessed from our MVC controllers) I find myself wondering why it's not possible to specify different contracts for the various permutations of an interface implementation.

Specifically I'm interested in the reasons behind only being able to specify the contracts in a 1-1 fashion given a generic interface method.

Here's an example of my code structure and I'll aim to identify how I'd like to use code contracts. I'm sure someone with more experience will be able to nudge me in the right direction.

I'm using a CQRS style approach such that:

public interface IQuery<in TInput input,out TOutput output>
{
     TOutput Invoke(TInput request)
}

public interface IGetSomeUnicornsFromAMagicalLand : 
                                             IQuery<int, IEnumerable<Unicorn>>{}

 // Implementation
public class GetSomeUnicornsFromMagicLand : IGetSomeUnicornsFromAMagicalLand
{
    public IEnumerable<Unicorn> Invoke(int numberOfUnicornsToReturn)
    {
       // Here I'd like to specify some preconditions on the input, 
       // specific to type int

       return _wizardry
                 .GetMagicCreature<Unicorn>(numberOfUnicornsToReturn)
                 .DoMagicalConversionToEnumerable()
    }

}

Given this context it seems reasonable to want to specify contracts at the implementation level rather than in an abstract class designed to apply contracts on the interface (as a generalised mechanism).

Upvotes: 1

Views: 237

Answers (1)

jeroenh
jeroenh

Reputation: 26782

In light of the Liskov Substitution Principle, in general I'd say it does make more sense to specify these contracts at the interface level.

The LSP basically says that each implementation of an abstract concept should be interchangeable without the user of the abstract concept having to know the difference. In other words, your code should not start to break because you switch to another implementation of a certain interface.

By imposing implementation-specific additional requirements on the input arguments, you are effectively coupling the calling code (which is in your case most likely framework-level code dealing with the general concept of IQuery), to the specific implementation.

For your specific situation that reasoning may or may not apply (that's why it's a principle, it's not written in stone), but obviously a general framework like code contracts can only take general principles into account :-)

EDIT: After thinking this through some more, it seems to me that the derived interface is probably the 'real' one, not the generic IQuery<TInput, TOutput>. The generic base interface seems to be more of an 'implementation detail', you can indeed not really make any meaningful assumptions (contracts) on this generic base interface. Only at the concrete level can you make any meaningful reasonings about these input and output parameters.

Maybe we need C++'s private inheritance (or implementation inheritance) in C# to 'hide' this interface from consumers. Read this excellent answer to a related question. It also talks about Liskov Substitutability being "a lie" in this case, which I'm inclined to agree with.

Upvotes: 2

Related Questions