第4章:注重实效的偏执
每个人都知道只有自己是好司机,其他人都不会好好开车。
——因此,你需要在麻烦发生之前小心谨慎、预判意外之事、从不让自己陷入无法解救自己的境地。
而一个“注重实效的”程序员会更进一步——他们连自己也不信任。因为他们知道没人能编写完美的代码,自己也不例外。
因此,需要对错误进行防卫性编码。
本章的内容即是各种防卫策略:
21《按合约设计》
合约既规定你的权利与责任,也规定对方的权利与责任。此外,还有关于一方未遵守的后果的约定。
我们也可以采用同样的理念来帮助软件模块进行交互。
DBC(Design By Contracts),通过合约进行设计
Meyer这样描述:
- 前条件。为了调用例程,必须为真的条件。
- 后条件。例程保证会做的事,例程完成时世界的状态。
- 类不变项:类确保从调用者的视角来看,该条件总是为真。
“继承”和“多态”是面向对象的基石,也是DBC闪耀的领域。
比如 Liskov替换原则 :
子类必须能通过基类的接口使用,而使用者无需知道其区别
实现DBC:
- 断言
- 语言支持
- 就算没有机制,也可以添加注释
可以使用DBC来早崩溃。在问题现场找到和诊断问题更容易。
不变项的其他用法?
- 循环不变项?
- 语义不变项?
动态合约与代理?
22《死程序不说谎》(提早崩溃)
如果有错误,那么提早崩溃。
要崩溃,不要破坏。
虽然,有时简单地退出运行中的程序并不合适。
但是,要尽快终止它,死程序带来的危害通常小于有疾患的程序。
23《断言式编程》
无论何时你发现自己在思考“但那当然不可能发生”时——增加代码检查它,最容易的是使用断言。
注意:
- 断言不应该有副作用
- 断言可能会被关闭,所以不要把必须执行的代码放进去。
- 不要用断言去替代真正的错误处理,因为断言检查的是决不应该发生的事。
尽量保持断言开着:
- BUG有可能没有找干净
- 有一些突发情况(耗尽内存,塞满硬盘等)
24《何时使用异常》
检查每一个错误可能会导致代码很丑陋。
如果你用的语言支持异常,则可以用更简洁的方式表示。
什么是异常情况?
——问问你自己:“如果我移走所有的异常处理器,这些代码是否仍旧能运行?”
如果答案是“否”,那么异常也许就被用在了非异常的情形中。
25《怎样配平资源》(资源分配善始善终)
分配资源的例程也应该释放它。
对于一次不止需要一个资源的例程,两个建议:
- 以与资源分配的次序相反的次序解除资源的分配。
- 在代码不同地方分配同一组资源时,总以相同的次序分配它们。这降低死锁发生的可能性。
资源的配平可以与构造函数与析构函数结合。
配平与异常:
当异常时,需要释放资源,但是有可能违反DRY原则。
处理方法有多种,根据语言有所不同。
动态的数据结构往往有无法配平资源的情况。
比如有层级结构。三个主要选择:
- 顶层结构还负责释放它包含的子结构。这些结构随即递归地删除包含的数据等等
- 只解除顶层结构。它指向地(没在别处引用的)任何结构都会被丢弃。
- 如果顶层结构包含子结构,就拒绝解除自身的分配。
你可能需要个检查资源配平的工具