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.

posted on Thursday, November 17, 2005 11:46 PM by scott

Comments

Thursday, November 17, 2005 10:11 PM by you've been HAACKED

# Defining a Contract Is Hard

Thursday, November 17, 2005 10:29 PM by Keyvan Nayyeri

# re: Defining a Contract Is Hard

The second parameter in your interface would be "string password", I think. Isn't it?
Friday, November 18, 2005 3:59 AM by scott

# re: Defining a Contract Is Hard

Oops, thanks Keyvan. See how hard this is? :).
Friday, November 18, 2005 9:35 AM by Steve Campbell

# re: Defining a Contract Is Hard

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.
Friday, November 18, 2005 10:27 AM by Jeff Atwood

# re: Defining a Contract Is Hard

> 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.
Friday, November 18, 2005 10:44 AM by scott

# re: Defining a Contract Is Hard

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).
Friday, November 18, 2005 10:32 PM by you've been HAACKED

# Defining a Contract Is Hard