Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Design-by-Contract: A Practical Introduction

[Update, 3/28/07:  Changed recommendation to not use conditional compilation constants to vary behavior between debug and release mode.] 

There are two things I loathe during development:  spending any time at all with the VS.NET debugger and seeing "object reference not set to an instance of an object" (which is usually why I'm in the debugger to begin with).  The technique of Design-by-Contract (DBC) allows you to not only eschew these two issues but also assists with writing better self-documenting and more expressive code.  Design-by-Contract isn't a new technique; in fact, the language Eiffel has DBC built right into it.  There's a lot we can learn (and take) from Eiffel's approach.

Implicit Contracts

Typically, if you're not using DBC techniques, comments are (hopefully) included to describe to the client how the code should be used and any caveats to be expected.  (The "client" could be a third party calling your API, another developer on your own team, or, most often the case, another bit of code you're writing yourself.)  As an example of an implicit contract - read "not DBC" - the following method fully describes the "contractual obligations" of the calling code:

/// <summary>Gets the user for the given ID.</summary>

/// <param name="id">Should be > 0</param>

public User GetUserWithIdOf(int id,

        UserRepository userRepository) {

    return userRepository.GetById(id);

}

The developer of this code has documented the restrictions for using the method - the "calling contract" - and, implicitly, has promised to return a User.  There are a few problems with this:

  • If the client didn't pay attention to the comments, there's nothing to stop the client from passing in an invalid ID of 0.
  • The client could easily pass null for userRepository.  This would result in an "object reference" exception.
  • Since the returned User may be null, the client will have to check for this possibility or also risk an "object reference" exception.  In other words, the target method has not obligated itself to any sort of "response contract."
  • What if, down the road, it becomes OK to pass 0 to the method.  If the developer neglects to update the comment, then the stated business rule will conflict with the inherit business rule within the code itself.

Explicit Contracts 

Design by Contract allows you to transform the implicit contract, described above, into an explicit bidirectional contract.  The bidirectional contract obligates both the client to invoke the method in a particular way and for the target method to guarantee a particular result.  If the contract is broken, then the breaking party will be severely chastised (conditionally...more on this soon).  The calling contract is expressed with one or more "pre-conditions" while the response contract is expressed with one or more "post-conditions."  A pre-condition states "this is your end of the bargain which must be adhered to to call me."  A post-condition states "this is my end of the bargain which you can count on me to enforce."  What follows is a very explicit, and very inflexible, bidirectional contract:

/// <summary>Gets the user for the given ID.</summary>

public User GetUserWithIdOf(int id,

        UserRepository userRepository) {

  // Pre-conditions

  if (userRepository == null)

      throw new ArgumentNullException(

          "userRepository");

  if (id <= 0)

      throw new ArgumentOutOfRangeException(

          "id must be > 0");

 

  User foundUser = userRepository.GetById(id);

 

  // Post-conditions

  if (foundUser == null)

      throw new KeyNotFoundException("No user with " +

          "an ID of " + id.ToString() +

          " could be located.");

 

  return foundUser;

}

With the bidirectional contract now in place, almost all of the previous drawbacks have been addressed.  Furthermore, the code is much more self-documenting and requires very few comments to be explicit.  But a few drawbacks still exist:

  • The contract is a bit verbose and easily blends in with the surrounding code.  Preferably, the contract should be more concise and easily spotted.
  • The contract above always throws exceptions.  It should be simpler to conditionally turn on exceptions for debug vs. release mode.  Furthermore, it should be simpler to always throw exceptions for pre-conditions, but only throw exceptions for post-conditions in debug mode.
  • What if we want to extend contracts to the class level, and not just on individual methods?  The above technique is difficult to extend for these purposes.
  • What if we want class level contracts to be extended (or restricted) within inherited classes?  Again, the above technique would become cumbersome - read "fragile and difficult to maintain" - when inheritance is involved.

Design-by-Contract Class Library 

Fortunately, a light-weight class library has been written for the .NET environment which addresses these issues:  http://www.codeproject.com/csharp/designbycontract.asp [1].  With this library in place, the previous example could be expressed as follows:

/// <summary>Gets the user for the given ID.</summary>

public User GetUserWithIdOf(int id,

      UserRepository userRepository) {

  Check.Require(userRepository != null,

      "userRepository may not be null");

  Check.Require(id > 0, "id must be > 0");

 

  User foundUser = userRepository.GetById(id);

 

  Check.Ensure(foundUser != null, "No user with an " +

      "ID of " + id.ToString() + " could be located.");

  return foundUser;

}

Now, the contract is concise, explicit and easy to spot.  The library also provides for inheritance, allowing you to (only) weaken preconditions and (only) strengthen postconditions in overriding methods.  (As an exercise for the reader, you'll also want to explore this library's inclusion of an "invariant" which allows contracts to be applied to the class level.)  Finally, a few conditional compilation constants are provided to vary behavior between debug and release mode.  I recommend not varying the conditional compilation constants regardless of the compilation mode.  As commenters have noted, and as I have seen from using the library, behavior should not vary between debug in release mode.  A contract is always a contract.  Furthermore, variations in behavior may lead to difficult-to-reproduce bugs.  In my own, modified version of the DBC library, I have removed all capabilities of varying the contractual behavior based on compilation mode and, therefore, have all conditions turned on at all times.

Day-to-Day Use & Benefits 

From a practical standpoint, once you have the DBC class library in place, you'll find the "Check.Require" to be, by far, the most useful addition to your coding arsenal.  In my projects, I find myself having one or two of these at the top of every method.  They only take a few seconds to write and save many hours in the debugger.  In fact I haven't used, or needed, the debugger since adopting the principles of design-by-contract in earnest.  The benefits of Design-by-Contract [2] include:

  • A better understanding of the method and how the software is constructed.
  • A systematic approach to building bug-free systems.
  • An effective framework for debugging, testing, and more generally, quality assurance.
  • A method for self-documenting software components.

I hope this give a hint behind the benefits of design-by-contract...they'll certainly become apparent the very first day you include the DBC class library in your own project.

References and Additional Resources


Posted 09-22-2006 9:51 AM by Billy McCafferty

[Advertisement]

Comments

Rob Eisenberg wrote re: Design-by-Contract: A Practical Introduction
on 09-22-2006 2:25 PM

If you haven't seen this:

http://research.microsoft.com/projects/specsharp/

I think you'll be very excited.  There are a couple of webcasts at the bottom of the page worth watching.

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 09-22-2006 2:42 PM

Yes, I'm very much looking forward to the future of C#.  I read somewhere that Spec# has been shelved for C# 3.0 but should be in the following release.  Hopefully, that's not the case.

Nick Parker wrote re: Design-by-Contract: A Practical Introduction
on 09-22-2006 2:53 PM

Billy,

I had talked about this awhile ago in a few blog posts, I implemented an example that relied in some of the AOP support in Spring.NET, take a look if your interested.

http://developernotes.com/archive/2005/10/19/757.aspx

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 09-22-2006 3:30 PM

Nick,

Using AOP for Design-by-Contract is a clever approach.  I still haven't decided if AOP is a good thing or not.  ;)  The cool factor (as "cool" as things get in geekville, anyway) is very high, but I've found it to decrease code-readability and expressiveness.  On the flipside, it does brilliantly with centralizing concerns.  I think AOP's time to shine will be coming soon, though, with Software Factories...time will tell I suppose.

Stephen Wright wrote re: Design-by-Contract: A Practical Introduction
on 09-22-2006 5:01 PM

One of the best functions in Refactor! Pro is the "Create Method Contract" option.  If you right click at the begining of the function, it will make sure that you have valid parameters passed into the method before it will continue into the rest of the method.

Definately worthwhile.

Eric Wise wrote re: Design-by-Contract: A Practical Introduction
on 09-23-2006 11:19 PM

There is one quandary you will run into in ASP .NET development with the gridview if you never return a null object.  The EmptyItemTemplate thinks that a generic list with 0 items isn't empty and hence will not fire.

Also, throwing exceptions is expensive, and should be used for exceptional situations.  Returning a null in all cases means not found, null, no records, etc.  It's much faster to do a null check than to throw an exception for a non exceptional case (search not finding a match).

Just my 2 cents.  Either way, consistency is the key in any framework.

Jeff Perrin wrote re: Design-by-Contract: A Practical Introduction
on 09-24-2006 10:28 AM

Nice post, Bill. I would like to know where you're coming from with this quote:

"It should be simpler to conditionally turn on exceptions for debug vs. release mode."

I'm not sure I see the benefits to this. I'd want my app to fail the same way regardless of where or how it's being run.

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 09-24-2006 11:22 AM

Eric,

Breaking a pre-condition would always be exceptional.  In other words, if the software is bug-free and working as it should be, a pre-condition would never be broken.

jperrin,

Along those same lines, breaking a post-condition would not necessarily be exceptional.  Consequently, throwing exceptions for every broken post-condition would be, arguably, somewhat overkill.  But there are certainly two-sides to this coin.  On one side, the post-conditions aren't as contractually binding as pre-conditions.  On the other side of the coin, if someone hasn't taken the time to put in a post-condition, then it should be able to be trusted at all times, regardless of build mode.  Being that it is a contract, and stated as so, then I'd agree with your suggestion...the contract should work/fail the same way no matter what build mode you're in.  Thanks for the bringing up the point...that's certainly one of the reasons I love posting my ideas.

Eric Wise wrote re: Design-by-Contract: A Practical Introduction
on 09-24-2006 8:56 PM

Since I live and breathe the web, what about sites that load a record by an ID in the querystring?  Sure, the application/webform should only get to that querystring value if it already exists having been selected from some form of list, however, this doesn't handle the case where an ID may have been deleted, or someone is attempting to change their querystring value (yes, hashing the querystring can stop this).  In these cases, you're being passed a positive integer which for practical purposes looks like a good id, but a database lookup will find no result. In these cases, I think a null return is quite appropriate.

In the end, we all must decide what is exceptional *shrug*.  I just find this type of failed lookup to not be exceptional.

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 09-25-2006 1:01 AM

It's certainly a good point that each of us much decide what's exceptional and not exceptional.  For my current web project, if an object is not found for a given ID, it means that A) something was misaligned within the application or B) someone was having to fiddle with the querystring...a sign that the data is not as easy to get at as it should be.  In the first case, I want to know what the bug is.  In the second case, I want to be made aware that there is an area of the application that is not accessible as it should be since the user is wanting to modify the querystring directly.  (Obviously, there are exceptions to this "warning sign.")  But in any case, this situation is "exceptional" in my application so I want to be made aware of it.  (I have an email sent to me whenever there's an exception thrown.)  But as you stated, this is certainly situational and the definition of what is exceptional and what is not needs to be decided on each project.

Joe wrote re: Design-by-Contract: A Practical Introduction
on 09-27-2006 12:49 PM

I usually run into needing nulls when you are addressing generic list subclasses of objects.  Like I have a Customer object with a List of Invoices, List of Addresses, List of PhoneNumbers as properties on it.  When I'm binding a customer summary screen, the customer may not have purchased anything yet, just registered, they also may not have provided a phone number.  In these cases I want these lists to be null both for ease of databinding and because we have decided as a team that null means does not exist.

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 10-02-2006 11:49 AM

Joe, Eric,

Null certainly has its place in the domain layer.  The sample is more of an illustration of using design-by-contract and not indicative of null-usage best practices.  Thanks for bringing up the point!

dave wrote re: Design-by-Contract: A Practical Introduction
on 10-04-2006 3:31 PM

have you used ibatis for .net?

thoughts, comments, sugestions?

better then hibernate?

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 10-05-2006 12:35 PM

Dave,

Honestly, I couldn't tell you.  I've been using NHibernate religiously ever since my "day of ORM enlightenment."  Albeit, I've heard good things about iBatis.net as well.  Particularly, I've heard NHibernate's strength is with basic CRUD while iBatis.net's strength is with more flexible querying.  (But again, that statement's not from direct experience.)  Interestingly, some developers at ThoughtWorks use both NHibernate and iBatis.net on the same project...NHibernate for doing most of the stuff (read CRUD) and iBatis.net for the hard-core querying.  But now that NHibernate will be supporting stored procs, and native generics, with their next release, currently in beta, it looks like NHibernate is becoming even more competitve.

On a related note, you'll probably want to look at LLBGen Pro as well...I've heard great things about that as well but haven't used it personally.  So many choices, so little time!! ;)

Billy

Bill Campbell wrote re: Design-by-Contract: A Practical Introduction
on 11-26-2006 1:19 PM

Billy,

Great article! I've started using the DBC class that you suggested and find it to really be helpful. I was curious if you have any interesting ways of validating a DateTime being passed in as a parameter from the point of view of it actually containing a datetime? Just curious.

Also - really enjoying your work on Refactor It! Thanks for shaing all this with the community!

regards,

Bill

Billy McCafferty wrote re: Design-by-Contract: A Practical Introduction
on 11-27-2006 1:19 PM

Are you saying you're actually passing in a string and then checking to see if the string contains a valid datetime?  If that's the case, you could use the VisualBasic.Information.IsDate() method within your C# code.  So it would be something like Check.Require(VisualBasic.Information.IsDate(myDateParameter, "myDateParameter must be a valid date").

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)