NotImplementedException and the Interface Segregation Principle


This week, Derrick Bailey will be in town (Austin) to talk about the SOLID principles.  One of the hardest ones to talk about, and find examples for, is the Interface Segregation Principle.  The ISP states:

CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE

While this may be more difficult to recreate in software we both create and consume, you’ll see it pop up more in libraries meant for mass consumption, such as ASP.NET or NHibernate.  I think I’m starting to notice a trend in the .NET space that provides us a bright flashing blue light to indicate violations of the ISP: usage of NotImplementedException.

When talking about ISP in the .NET space, one of the more widely-used targets is the massive MembershipProvider class.  It has dozens of members, of which only a handful are used in custom implementations.  The MembershipProvider allows just about every possible membership feature under the sun to be used, but you’re still stuck with providing implementations to everything.  Don’t want to allow users to be locked out?  Too bad, provide an implementation.  Can’t figure out how many users are online?  Too bad, provide an implementation.

Even ALT.NET-y frameworks like NHibernate (though ALT.NET is NOT NOT NOT about tools) has its own issues with ISP.  In one recent project, we interacted with a legacy database to read about 80% of our entities as read-only.  That is, a large portion of the tables we read from were never written to, and we turned off the ability to write back to from NHibernate.  As it was a legacy database, we made extensive use of NHibernate’s custom user types, which allowed us to encapsulate the…more interesting parts of the database.  Instead of “Y” and “N” in our system, we wanted booleans.  Instead of integer dates (20080904), we wanted actual DateTime types.  Custom user types allowed us to translate from the database to our types, completely transparent to our actual entities.  Our entities were built with “real” types, and NHibernate took care of the translation.

Unfortunately, we still had quite a few of these around:

public override Type ReturnedClass
{
    get { throw new NotImplementedException(); }
}

public override void Set(IDbCommand cmd, object value, int index)
{
    throw new NotImplementedException();
}

public override Type PrimitiveClass
{
    get { throw new NotImplementedException(); }
}

public override object DefaultValue
{
    get { throw new NotImplementedException(); }
}

A bunch of features we didn’t need to support, all throwing NotImplementedExceptions.  Instead of providing an empty implementation, we wanted to make sure these areas were never called, and if they did, something else was wrong.

These NotImplementedExceptions are pretty strong smells for ISP violations.  We were forced to provide an implementation, but the exception shows a refusal on our part to do so.  Part of this is a design issue, and part of it is the unfortunate side-effect of designing an API that is used in a variety of situations.  The NotImplementedExceptions are noisy, and I’d rather never see one in code (even temporarily in a test).  In both the MembershipProvider and the NHibernate case, we were using 3rd-party libraries which we had no control over.  It’s not like we could try and do something about the ISP violations.  But in our client software, we can look out for NotImplementedExceptions, and examine the underlying type to see if there aren’t more concerns that need to be split out.

Three simple Rhino Mocks rules