防御性编码与其说是一种编码风格,不如说是一种编码习惯。
每个人都负责“自己”的模块,然后与“别人”的模块发生各种关联,进而实现需求。在这样的复杂环境下,每个模块都会形成一些假设,也就是前置条件。只有前置条件被满足,才能按预期正常工作。这些前置条件有些是有明确的协议的,但无明确协议的往往占据了大多数,与“别人”之间的调用关系是通过口头约定来进行的,或者没有约定,只是一个假设。
要想让“自己”的模块不被这种情况伤害得更深,就需要进行一些有意识的防御,对不符合前置条件的情况进行快速反馈,对可以预见到的陷阱进行主动防守,通过明确的预期行为来代替运行时的不确定性,可以为问题跟踪带来非常大的便利,提升开发效率。
常见的防御手段包括:
- 对空指针的防御。这是最常见的方法,不再展开。
- 利用运行时异常进行业务层面的防御。对运行时可能产生的不满足业务条件的情况进行运行时异常抛出。
- 利用异常重试机制对外在环境的不可控变化进行防御,以增强健壮性。比如写文件时的IO异常可以做重试处理。
- 利用断言在开发阶段及早发现非预期状态。
以上不是全部,但也要防止过量使用,导致代码中到处都是这种语句,影响对业务逻辑的理解,甚至影响运行效率。
一般而言,“自己”模块对外的界面的界面会多采用防御性编码,作为对前置条件的验证。模块内部,由于是自己的实现逻辑,只要可控,可以减少这种编码,但对调用其他模块的返回值可以有意识的增加防御。