• 2009-08-17

    《程序员修炼之道》 第四章 注重实效的偏执 - [新知随笔]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://liuyangsl.blogbus.com/logs/44384632.html

    不可能写出完美的软件,因此程序员应该针对自己的错误进行防卫性编码。

    1. 按合约设计
    坦率是交流过程中最好的解决办法,用合约规定双方的权力和义务。

    按合约设计(DBC)
    用文档记载并约定软件模块的权力与责任,确保程序的正确。

    一个程序业务例程的约定包括以下方面:
    <a> 前提条件:执行例程之前必须满足的条件。
    <b> 后条件:例程完成后,世界的状态。
    <c> 不变项:在例程执行前后,保持不变的事物。

    合约中应该保持“羞怯”,尽量缩小输入和输出的数量以及范围。

    对父类规定了合约之后,继承父类的子类可以有自己的合约,但至少要遵循父类的合约。
    这样用户在使用子类的时候,才能感到它是父类的一种。


    使用DBC的好处是,它明确了需求和约束,让程序员知道应该是什么样子,不允许是什么样子。

    一些编程语言具有内置DBC支持,这样就可以用“断言”让编辑器帮助检查合约。
    但是断言不能被继承,runtime系统和库的设计也不支持合约。
    <a> Eiffel 和 Sather 可以自动在编译器和runtime系统中检查前提条件和后条件。
    <b> C 和 C++ 可以使用Nana,用调试器在运行时监控断言。
    <c> Java 可以使用 iContract ,读取注释,生成包含断言逻辑的源文件。
    但是像<b><c>两种预处理实现断言的方式,可能会使项目集成变得杂乱。

    断言的判断,是在调用者调用例程之后,进入例程之前,在后台进行测试的,这样就可以在出现问题之前,阻止例程的错误运行。

    不变项的用法:
    <a> 循环不变项:循环中经常出现计数错误,用循环不变项来确定循环的最终目标,并且每次循环都应符合这个目标。
    <b> 语义不变项:定义一种在任何情况下都不可违反的需求。注意那些反复变化的政策性约定不属于语义不变项。

    ----------------------------------------------------------------------------------

    2. 死程序不说慌

    尽早地检测问题,尽早地让程序崩溃。
    例如在Java中,一个runtime中的异常发生时,RuntimeExcepton如果没有被捕捉,将渗透到程序的顶部,并留下痕迹。

    当程序中发现有不应发生的事情发生之后,程序应该尽早地报警崩溃,因为如果没有及时报告,这个异常的后果可能会在之后引发“不可能的错误”。

    ----------------------------------------------------------------------------------

    3. 断言式编程

    在编码的过程中,不要坚信某些事情是不可能发生的,就不去处理。应该用断言来确保这些事情不会发生。

    例如在C++中,用于判断布尔的assert宏:
    void writeString(char *string){
        assert(string != NULL);
        ...
    }
    注意在编译时,断言可能不会被执行,因此不要把必须执行的代码写在断言中。

    断言只是一种异常情况的预防,不要用断言代替异常处理代码。
    应该让断言失败,抛出异常,以便在某处做资源的释放等处理。

    虽然断言是在检查那些“绝不会发生”的事情,但是在软件经过了测试并发布之后,断言也应该开着。

    注意不要在使用断言时,引发新的危险代码。

    ----------------------------------------------------------------------------------

    4. 何时使用异常

    引入断言的一个副作用是,当需要进行断言的事物太多,很容易让代码变得丑陋。
    这就需要使用异常处理,可以对一整段内容进行异常处理,替代多个断言,使代码简洁。

    ----------------------------------------------------------------------------------

    5. 怎样配平资源

    对于资源,要有始有终。
    分配资源的例程要负责结束该资源。
    按照分配资源顺序相反的顺序来结束资源
    在代码的不同地方分配一组资源时,总是用相同的顺序来分配它们。

    配平资源的方法在不同程序语言中有所不同。
    当一个资源生成后,如果有多个途径来释放(比如可以在正常流程后释放,也可以在异常捕获后释放),可能会在维护时带来问题。

    当一个子结构创建的资源从属于一个顶层结构时,在配平时有以下选择:
    <a> 顶层结构负责释放包含的子结构。
    <b> 只解除顶层结构的分配。
    <c> 如果包含子结构,拒绝解除自身的分配。

    检查配平:
    为资源编写包装,用包装检查资源的状态,比如在包装里加入计数器。


    历史上的今天:


    收藏到:Del.icio.us