YellowWee's Scripts

妳的世界能夠從此不同...而我的世界... 亦因妳而改變...

导航

C#中如何使用----按合约设计(Design by Contract)

Posted on 2004-06-07 14:31  YellowWee(端木柒)  阅读(1557)  评论(0编辑  收藏  举报
     按合约设计---DBC(Design By Contract)是一种简单而强大的技术,它关注的是用文档记载(并约定)软件模块的权利与责任,以确保程序正确性.简单的说,就是用文档记载这样的声明,并进行校验,以确保程序能做它声明要做的事情.

    Eiffel 发展了这种概念并很好的实现了它,那像Java & .NEt这样的更流行语言能否支持DBC呢?查了一下资料,Java使用iContract这样的工具做为预处理器(perprocesseor),处理作为特殊注释嵌入在原代码中的合约.而.NET框架具有预处理器(perprocesseor)并且支持预处理指令(如:#if,#elif,#define),那.net能否不借助其他工具直接就能支持DBC呢??

摆弄了一番,发现.NET没有单独的preprocessor,预处理指令只是让你感觉有那么一个preprocessor,所以它不能很好的实现DBC中的前条件(precondition),后条件(postcondition)和不变项(invariant).那.NET没有preprocessor就没法实现DBC了吗?答案不是绝对的,因为DBC的主要内容是断言(assert)的一种观念,所以我们能使用断言(assert)对此进行模拟:

    iContract中实现percondition代码如下:

         /**
         * @pre f >= 0.0   //@pre定义percondition,确定参数 f 必须大于等于0
         */
        public float sqrt(float f) { ... }

   用C#可以这样模拟实现:
 
        public float sqrt(float f)
       {
             System.Diagnostics.Debug.Assert(f>=0,"parameter f  < 0 ");
             ......
       }

    postcondition也可以类似实现:

 iContract:

      /** 
     * @pre f >= 0.0
     * @post Math.abs((return * return) - f) < 0.001
     */
     public float sqrt(float f) { ... } 

 C#:
      public float sqrt(float f)
       {
             System.Diagnostics.Debug.Assert(f>=0,"parameter f  < 0 ");
             ......
             System.Diagnostics.Debug.Assert(Math.abs((result * result) - f)<0.001);
             return result;
       }

    Invariants 也可以如此炮制,这里就不写了.

    上面说了,使用断言(assert)实现DBC始终是一种模拟实现,也就是说,我们不能使用Assert做DBC能做的每一件事.主要原因断言(assert)不能沿着继承层次向下遗传.这就意味着,如果你重新设定了某个合约的基类方法,你必须把你的修改手工复制到他的底层类中.

    只用断言(assert)实现DBC虽然功能不全,但还是很有必要的,它能根据最初的简单设计,控制住参数的输入输出等,大大增加了程序的"安全性"(降低Bug出现的几率),使你的程序完成一次质量的飞跃.

    最后我列举 [The Pragmatic Programmer] 中的例子(有的地方做了适当修改),它们存在于我们的常识中.加上这些断言判断是多么的有必要,你往往认为绝对不会出错的地方,它也存在着Bug隐患.我们很容易掉进"它不可能发生"这样一种心理状态.

   1. 一个月不可能少于28.
   2. 在C#里: a = 2;b = 3; if(a + b !=5)  exit();
   3. 不会有内角和不等于180度的三角形.
   4. 不存在大于60秒的一分钟.
   5. 在C#中: (a+1) <= a;

 P.S: 我想我们实现的接口,抽象类也应该算是一种DBC,它完全符合DBC的定义,并能在编译期实行对合约实现地检查.