Defining a Contract Is Hard

We often talk about interfaces defining contracts.

interface UserValidator

{

    bool ValidateUser(string username, string password);

}

The above interface seems simple. We can go to any object implementing the interface and invoke the ValidateUser method. Pass in a username and password, and the object will tell us if the user is valid.

Still, there is plenty left unspoken. Take the abstract base class MembershipProvider in ASP.NET. MembershipProvider includes an abstract ValidateUser method, just like the one in the interface defined above. Here are the remarks for the method:

Takes, as input, a user name and a password and verifies that the values match those in the data source. ValidateUser returns true for a successful user name and password match; otherwise, false.
For successful validations, ValidateUser updates the LastLoginDate for the specified user.

Now we know a little more about our “contract”. If we implement ValidateUser we should update the LastLoginDate during a successful validation. If software were really a science, I think we’d have some language construct to announce and enforce the LastLoginDate update.

There’s more though – if you look at the two available implementations of MembershipProvider in ASP.NET (SqlMembershipProvider and ActiveDirectoryMembershipProvider), they have another side-effect in common. Both classes increment performance counters and raise health monitoring events when users authenticate and fail to authenticate.

To the client who needs a method to validate a user, these events and perfcounters are inconsequential side effects. The events don't change the state of my object, or the provider. However, if I was a sysadmin who monitors failed authentication events, but can’t get the events to work when I plug in a custom build membership provider, I’d consider something broken.

A contract is a simple concept, but the devil still waits in the details.

Print | posted @ Friday, November 18, 2005 4:46 AM

Comments on this entry:

Gravatar # re: Defining a Contract Is Hard
by Keyvan Nayyeri at 11/18/2005 6:29 AM

The second parameter in your interface would be "string password", I think. Isn't it?
  
Gravatar # re: Defining a Contract Is Hard
by scott at 11/18/2005 11:59 AM

Oops, thanks Keyvan. See how hard this is? :).
  
Gravatar # re: Defining a Contract Is Hard
by Steve Campbell at 11/18/2005 5:35 PM

Lets assume that you are correct that the additional actions performed are part of the interface (some people might say they are implementation-specific).

From a test-driven perspective, I would want to mock out a class that updates the last login date, and other classes that update performance counters. I can use the mocks to write tests that assert that the relevant methods are called by a particular implementation.

Should I have multiple implementations, I can verify that they all meet the definition of my interface by generalizing the test to work against that interface.

In practical terms, my interface might more end up looking like this:<pre>
bool ValidateUser(string username, string password, ILoginAudit audit, IPerformance perf);
</pre>
That is much more expressive of what I expect the function to do, and my tests would fill in the gaps of the specific requirements.
  
Gravatar # re: Defining a Contract Is Hard
by Jeff Atwood at 11/18/2005 6:27 PM

> if you look at the two available implementations [..] they have another side-effect in common. Both classes increment performance counters and raise health monitoring events when users authenticate and fail to authenticate.

Perhaps this should be base class inheritance instead of contract-- the inherited base method would do all these "common" things.
  
Gravatar # re: Defining a Contract Is Hard
by scott at 11/18/2005 6:44 PM

Steve:

You make a great point. Microsoft doesn't provide us with unit tests for provider implementations - that would be a great idea though.

Jeff:

I was thinking of inheritance, or an interception pattern. The only problem is - the interceptor has to decide what to do based soley on the return value of a method call (which in this case is probably enough information to go on).
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 4 and 4 and type the answer here: