在《unix编程艺术》提到过,Postel规定过:“宽容的收,谨慎的发”。
当时看到这一段话时,并没有多加思考,以为仅仅是对于网络协议多加检查就好了,并没有深想。
然而经过这两年的实践,我发现其实这条准则可以适用于更多的领域。
对于一个进程来说,他的输入可能是标准输入、管道、网络、配置文件等这些外部输入源。
那么进程所表现出来的宽容就是,不管从外部输入源接收到任何数据,都不应该影响程序的正常执行。即不会由于错误的数据格式或内容,造成崩溃,污然数据等副作用。
实现没有外部输入源程序时,我习惯上会信任设计正确的数据。
即,设计上在使用这个数据时,数据是合法的,那么一般不会再多加检查是否合法,最多加个assert去保护一下。
因为我觉得如果设计上正确的数据,如果出了问题,一定是设计出了问题,加上数据有效性检测并不能解决根本问题。此时需要做的是去修正设计,而非在局部打补丁。
而第一次接触到有外部输入源程序的那一段时间,由于没有经验,并没有区分哪些是内部数据,哪些是外部数据。所有的数据只要是设计正确的,全部都被信任,以至于在输入非法数据时,逻辑错误,数据紊乱。后来简直是草木皆兵,所有使用数据的地方都对其进行合法性判断。
经过几次实践并思考之后,发现其实可以将整个进程提象成一个整体,所有的外部输入源可以抽象成几个可以流入数据的口子。
然后抽象出一个筛子层,将筛子置于各外部输入源的口子处。筛子层负责对口子输入的数据进行仔细校验合法性,以确保通过筛子的的数据都是有效的合法数据。
由于所有的外部输入数据已经经过筛子过虑, 在此在实现真正的逻辑层时,完全可以和以前一样,直接信任设计正确的数据。这样在写逻辑代码时就不再需要提着思想的包袱,时时刻刻想着是否需要验证数据的合法性了。可以让我们更专注于逻辑设计本身。
然而,由于外部数据的合法性校验必然与具体逻辑密切关联,因此筛子层必然与逻辑层耦合在一起。
不过提象出筛子层的意义并不是用于与逻辑层分离。而是为了抽象出一个屏障,以清晰指明,哪些数据是由外部输入得来,需要仔细判断是否正确,哪些数据是程序内部自己产生,可以完全信任。
此准则不仅仅适用于进程,还适用于框架和库。
一般来说一个框架或库,一定是用于解决某个问题而生的。 即然抽象成框架或库,那么一定是希望别人能够快速使用,并且尽可能的屏蔽细节。因此,我们可能仅仅使用框架提供有很的几组API就可以实现一个很强大的功能。
但是由于这样或那样的原因,可能别人在开发过程中并没有完全领会这组API的输入数据的要求。如果框架不能宽容的收,框架的使用者就可能面临着很艰难的局面。
框架宽容的收,并不是说像进程收到非法数据一样,保证进程的稳定运行。由于框架是向业务逻辑提供功能的一个组件。因此,如果框架被误用,必须要尽快的将问题暴露出来。
因此,我觉得框架的宽容,应该仅仅保证不会因为非法数据造成误动作及污染数据等副作用。在一些特殊的情况下是可以直接崩溃,以尽快提醒业务逻辑的实现者此处的错误。
总而言之,不管是对于进程还是框架,只要是一个完整的个体,一定是自成闭环的。即,只靠自己个体内的数据,就有足够的能力确认当前外部输入数据是否合法。