IMZRH的日志

努力成为一个有用的人

导航

31天重构指南之二十五:引入契约式设计

Posted on 2009-10-19 15:04  张荣华  阅读(456)  评论(1编辑  收藏  举报

契约式设计(Design By Contract DBC)规定方法应该对输入和输出进行验证,这样你便可以保证你得到的数据是可以工作的,一切都是按期望进行的,如果不是按期望进行,异常或是错误就应该被返回,要想了解DBC的更多信息请访问这里

下面我们举的例子中,我们方法中的参数可能会为值为null的情况,在这种情况下由于我们没有验证,NullReferenceException异常会报出。另外在方法的结尾我们也没有保证会返回一个正确的decimal值给调用方法的对象。

   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: }
 
对上面的代码应用DBC是很简单的,首先我们断言我们不会有一个null值的customer对象,检查我们最少会有一个product对象。在返回订单总和之前先确保
我们会返回一个有意义的值。如果上面说的检查有任何一个失败,我们就抛出对应的异常,并在异常里说明错误的详细信息,而不是直接抛出
NullReferenceException。
 
在.NET Framework 3.5的Microsoft.Contracts命名空间下应该有一些DBC的类库方法,我还没有尝试过,但我想这应该是值得一看的,这里是我找到的惟一的资料。
 
   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: }
 
上面的代码中添加了额外的代码来进行验证,但我认为这是非常值得做的,因为当NullReferenceException发生时去追查异常的详细信息真是很令人讨厌的。
 

原文链接:http://www.lostechies.com/blogs/sean_chambers/archive/2009/08/25/refactoring-day-25-introduce-design-by-contract-checks.aspx