21、按照合约设计

与计算机打交道很困难,与人打交道更困难。合约既规定你的的权利与责任,也规定对方的权利与责任,还有关于任何一方没有遵守合约后果的约定。

用文档记载并约定软件模块的权利和责任,以确保程序正确性。用文档记载这样的声明,并进行校验,是按合约设计(DBC)的核心所在。

在做某事之前,例程对世界的状态可能有某种期望,并且可能有能力陈述系统结束时的状态:

  • 前条件

    为了调用例程,必须为真的条件

  • 后条件

    例程保证会做的事情

  • 类不变项

    类确保从调用者的视角来看,该条件总是为真

如果调用者满足了例程的所有前条件,例程应该保证在其完成时,所有后条件和不变项将为真。

实现DBC

  • 断言

    让编译器为你检查合约,能够获得大得多的好处,在有些语言中,你可以通过断言对此进行部分的模拟。

  • 语言支持

    有内建的DBC支持的语言自动在编译器和runtime系统中检查前条件和后条件。

DBC与早崩溃

通过早崩溃,在问题现场找到和诊断问题要容易得多。

不变项的其他用法

  • 循环不变项

    在复杂的循环上正确的设定边界条件可能会很成问题。循环不变项是对循环的最终目标的成熟,但又进行了一般化,使得循环执行之前和每次循环迭代时,它都是有效的。

  • 语义不变项

    你可以使用语义不变项表达不可违反的需求,一种“哲学合约”。不要把固定的需求、不可违反的法则与那些仅仅是政策的东西混为一谈。后者可能会随着管理制度的出台而改变。

动态合约与代理

我们一直把合约作为固定的、不可改变的规范加以谈论,但在自治代理的领域中,情况并不一定是这样。任何依赖于代理技术的系统对合约写上的依赖都是至关重要的。

22、死程序不说谎

有时别人在你自己意识到之前就能察觉你的事情出了问题。我们很容易掉进“它不可能发生”这样一种心理状态。所有的错误都能为你提供信息,如果有一个错误,就说明非常、非常糟糕的事情已经发生了。

要崩溃,不要破坏

尽早检测问题的好处之一是,你可以更早崩溃,有时候,让你的程序崩溃是你的最佳选择。

有时简单的退出运行中的程序并不合适,申请的资源可能没有释放,或者你可能要写出日志消息,清理打开的食物,或与其他进程交互,异常的技术在此时能对你有帮助。

异常和崩溃的基本原则是一样的,当你的代码发现,某件被认为不可能发生的事情已经发生时,你的程序就不再有存活能力,它做的所有事情都会变得可疑,应该尽快终止它。

23、断言式编程

如果它不可能发生,用断言确保它不会发生

无论何时你发现自己在思考“但那不可能发生”,增加代码检查它,最容易的方法是使用断言。不要用断言代替真正的错误处理。断言检查的是绝不应该发生的事情。

让断言开着

在程序交付时关闭断言就像是因为你曾经成功过,就不用保护网去走钢丝,这样做难以获得人身保险。即使确实有性能问题,也只关闭哪些真的有很大影响的断言。

断言与副作用

如果增加的错误处理代码引入了新的错误是非常尴尬的。

24、何时使用异常

检查每一个可能的错误,特别是意料之外的错误,是一种良好的实践,但是在实践中这可能会把我们引向相当丑陋的代码。你的程序的正常逻辑最后可能会被错误处理完全遮蔽。

什么是异常情况

异常很少应作为程序的征程流程的一部分使用,它应保留给意外事件。如果移走所有的异常处理器代码不能仍然运行,那么异常也许就正在被用在非异常的情形中。

将异常用于异常情况

把异常用作正常处理的一部分的程序,将招收到经典的意大利面条式代码的所有可读性和可维护性问题的折磨。这些程序破坏了封装性,通过异常处理,例程和他们的调用者被更紧密的耦合在一起。

错误处理器是另一种选择

错误处理器是检测到错误时调用的例程。使用错误处理器代替异常或者与异常一起使用。

25、怎样配平资源

只要在编程,我们都要管理资源:内存、事务、线程、文件、定时器,所有数量有限的事物。

对于资源分配和基础分配的处理,许多开发者没有始终如一的计划。分配某项资源的例程或对象应该负责解除该资源的分配,要有始有终。

嵌套的分配

对于一次需要不止一个资源的例程,可以对资源分配的基本模式进行扩展。

  • 以与资源分配的次序相反的次序解除资源分配
  • 在代码的不同地方分配同一组资源时,总是以相同的次序分配它们

对象与异常

分配与解除分配的对称让人嫌弃类的构造器与析构器。如果你使用面向对象语言编程,把资源封装在类中很有用。需要特定资源时,实例化该类的对象,对象出作用域或被垃圾回收时,析构函数解除资源分配。

配平与异常

支持异常的语言可能会使解除资源的分配很棘手。如果有异常被抛出,如何保证在发生异常时分配的所有资源都得到清理?

  • c++异常机制下配平资源

    c++的支持try catch的异常机制,这意味着退出某个捕捉异常并重新抛出时,总是至少有两条路径去释放资源,这违反DRY原则,也可能会发生可维护性问题。解决办法是不使用指针或者使用类包装资源。

  • java中配平资源

    java的finally子句不管是否有异常发生,总是会执行,因此可以在这里配平资源。

当你无法配平资源时

有时基本的资源分配模式并不合适,这通常会出现在使用动态数据结构的程序中。为每个重要结构编写一个模块,为该结构提供分配和接触分配的设施。如果资源追踪很棘手,可以通过在动态分配的对象上实现一种引用计数方案。

检查配平

构建代码、对资源确实得到了适当释放进行实际检查,这总是一个好主意。

更多有关《程序员修炼之道》的读书笔记,请关注 :
http://tabalt.net/blog/the-pragmatic-programmer-reading-notes/

本文链接:http://tabalt.net/blog/tpp-pragmatic-paranoia/,转载请注明。