Los Techies : Blogs about software and anything tech!

Refactoring Day 25 : Introduce Design By Contract checks


Design By Contract or DBC defines that methods should have defined input and output verifications. Therefore, you can be sure you are always working with a usable set of data in all methods and everything is behaving as expected. If not, exceptions or errors should be returned and handled from the methods. To read more on DBC read the wikipedia page here.

In our example here, we are working with input parameters that may possibly be null. As a result a NullReferenceException would be thrown from this method because we never verify that we have an instance. During the end of the method, we don’t ensure that we are returning a valid decimal to the consumer of this method and may introduce methods elsewhere.

   1: public class CashRegister
   2: {
   3:     public decimal TotalOrder(IEnumerable<Product> products, Customer customer)
   4:     {
   5:         decimal orderTotal = products.Sum(product => product.Price);
   6:  
   7:         customer.Balance += orderTotal;
   8:  
   9:         return orderTotal;
  10:     }
  11: }

The changes we can make here to introduce DBC checks is pretty easy. First we will assert that we don’t have a null customer, check that we have at least one product to total. Before we return the order total we will ensure that we have a valid amount for the order total. If any of these checks fail in this example we should throw targeted exceptions that detail exactly what happened and fail gracefully rather than throw an obscure NullReferenceException.

It seems as if there is some DBC framework methods and exceptions in the Microsoft.Contracts namespace that was introduced with .net framework 3.5. I personally haven’t played with these yet, but they may be worth looking at. This is the only thing I could find on msdn about the namespace.

   1: public class CashRegister
   2: {
   3:     public decimal TotalOrder(IEnumerable<Product> products, Customer customer)
   4:     {
   5:         if (customer == null)
   6:             throw new ArgumentNullException("customer", "Customer cannot be null");
   7:         if (products.Count() == 0)
   8:             throw new ArgumentException("Must have at least one product to total", "products");
   9:  
  10:         decimal orderTotal = products.Sum(product => product.Price);
  11:  
  12:         customer.Balance += orderTotal;
  13:  
  14:         if (orderTotal == 0)
  15:             throw new ArgumentOutOfRangeException("orderTotal", "Order Total should not be zero");
  16:  
  17:         return orderTotal;
  18:     }
  19: }

It does add more code to the method for validation checks and you can go overboard with DBC, but I think in most scenarios it is a worthwhile endeavor to catch sticky situations. It really stinks to chase after a NullReferenceException without detailed information.

This is part of the 31 Days of Refactoring series. For a full list of Refactorings please see the original introductory post.

Kick It on DotNetKicks.com
Posted Aug 25 2009, 08:02 AM by schambers

Comments

alberto wrote re: Refactoring Day 25 : Introduce Design By Contract checks
on 08-25-2009 9:24 AM

That's not a refactoring by any mean. You are changing the behaviour of your class. A call that used to throw a NullReferenceException now will throw an ArgumentNullException.

schambers wrote re: Refactoring Day 25 : Introduce Design By Contract checks
on 08-25-2009 9:48 AM

True, this is more of a modification then a refactoring because we are changing the exception that is being thrown. However, this change won't affect any consumers until you choose to catch the different exception being thrown.

This one isn't really an exact fit for refactorings but I wanted to show it was a worthwhile thing to do when trying to apply some of the refactorings.

on 08-25-2009 5:55 PM

Agile/ALT.NET James Shore provides a Introduction to Agile for QA People This is late but Davy Brion has started a series on Build Your Own Data Access Layer Series . Some posts include Out of the Box CRUD Functionality , Mapping Classes to Tables , and

John Meyer wrote re: Refactoring Day 25 : Introduce Design By Contract checks
on 08-26-2009 1:54 PM

The orderTotal exception should not be one from the ArgumentException family, since orderTotal is not an argument to the method.  InvalidOperationException, or a custom exception, would be more appropriate in this case.  See code analysis (FXCop) warning 1040.

Thomas Weller wrote re: Refactoring Day 25 : Introduce Design By Contract checks
on 09-01-2009 12:44 PM

While I agree that DbC checks are not really 'Refactoring', but a modification of code behaviour, introducing it will greatly enhance the stability and maintainability of a codebase. So if you understand 'Refactoring' as 'making the code better and more consistent', then introducing DbC is clearly something to consider (although I think we shouldn't call it 'Refactoring'...).

Btw, did you know that there's a really useful out-of-the-box library for writing DbC-Attributes like e.g. '[NotNull(something)]. Really nice, and spares tons of repetitive hand-rolled verification code: See here: validationaspects.codeplex.com

Regards

Thomas

Ed wrote re: Refactoring Day 25 : Introduce Design By Contract checks
on 09-04-2009 8:05 AM

I'm pretty sure we have been calling this "defensive programming" for as long as I have been developing

31 Days of Refactoring « Vincent Leung's .NET Tech Clips wrote 31 Days of Refactoring &laquo; Vincent Leung&#039;s .NET Tech Clips
on 10-28-2009 9:28 AM

Pingback from  31 Days of Refactoring « Vincent Leung's .NET Tech Clips

PetterLiu wrote 31 Days of Refactoring
on 11-27-2009 4:02 AM

Refactoring Day 1 : Encapsulate Collection Refactoring Day 2 : Move Method Refactoring Day 3 : Pull ...

PetterLiu wrote 31 Days of Refactoring
on 11-27-2009 4:04 AM

Refactoring Day 1 : Encapsulate Collection Refactoring Day 2 : Move Method Refactoring Day 3 : Pull ...

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems