![c41bea7ce0e5827c0af78c4d81f82a8c.png](https://i-blog.csdnimg.cn/blog_migrate/54b93b403ab9df1c6a43a36a65ab7524.jpeg)
1. 异常流程
在确定产品功能的时候,人们提到更多的是,该产品应该怎样表现,
而实际上,产品所涉及的异常流程是否清晰,
才是提高功能可靠性的关键。
考虑如下一个简单的功能,
点击页面中的按钮,发起一个ajax请求,后端读取数据库返回相应的查询结果。
我们可以把它划分为三个环节,
(1)前端发起ajax请求给后端
(2)后端接受到请求,调用数据库查询服务
(3)查询数据库,返回相应的查询结果
![963204a72e6ac134aeb0e6697c077bee.png](https://i-blog.csdnimg.cn/blog_migrate/e06efd9ffeb92e8afcfee6fdb5bd932c.jpeg)
线性系统的可靠性是每个系统组件的可靠性的乘积。
——《持续集成》
因此,假设以上每个环节的可靠性是 90%,那么整个系统的可靠性,则将只有 72.9%。
如果整个系统包含 100 个环节呢?可靠性就只剩下 0.0027% 了!!
因此,如果在系统层面承诺具有高可靠性,就得在每个环节上下足功夫。
2. 异常在所难免
在计算机科学中,健壮性(robustness)指的是,
一个计算机系统在执行过程中处理错误,
以及算法在遭遇输入、运算等异常时继续正常运行的能力。
要想提高代码的健壮性,我认为首先应该改变我们对错误的认知习惯。
![ee2331788ce77141609bc47f844c4485.png](https://i-blog.csdnimg.cn/blog_migrate/dc15d4c970fb985ad88eaa305531e6bc.jpeg)
我们应该认为,异常是在所难免的,
而剩下的问题是,都有哪些异常,以及如何处理它们。
有一个类似相同的论断,来自于《Reactive Design Patterns》
The question therefore is not if a failure occurs but only when or how often.
我们在这个基础上考虑问题。
3. 如何提供可靠的服务
人们通常认为,提供可靠的服务,就是不发生错误,
我认为这是不太恰当的。
因为作为底层服务,在出现某些错误的情况下,我们实在不应该替用户拿主意。
![93672d3441b0bc70c310df5eb36ce4bb.png](https://i-blog.csdnimg.cn/blog_migrate/810afd32fe9c2abff11e916b3ef8be26.jpeg)
因此重要的事情,不是吞掉异常让服务看起来可靠,
而是,考虑用何种方式将错误呈现给用户,
这需要我们站在用户的角度考虑问题。
当考虑了异常之后,接口所传递的知识就在无形中被扩充了,
接口实际上包含了在不同情况下(正常/异常),应该返回什么结果。
3.1 反模式:出错消息
![7eab80109a658ac1321844cb03c01545.png](https://i-blog.csdnimg.cn/blog_migrate/a44b7f1188be71f027fd691b7d63cbf9.jpeg)
返回一段出错消息,是一种最常见的不为用户考虑的反模式,
因为用户不得不解析这段消息来确定究竟发生了什么错误。
这种情况用户一般也不会解析它,
而反馈给更上层的用户也是不合理的,
因此,这种消息最多保留到了日志中,或者干脆被直接忽略了。
为了让消息无歧义,更好的办法是返回错误的类型(或者是错误的统一编码),
它们会作为接口文档的一部分提供给用户,
并由用户决定处理方式。
3.2 反模式:不一致
比无法区分错误类型更好一点,但是同样有问题的报错方式就是,不一致的展示错误,
某些接口通过抛异常来报错,
另外一些接口统一捕获了异常,通过一个错误标志位来报错。
无疑这在某种程度上加大了用户代码的复杂度,
而且对于用户来说,他们也无法区分一个异常到底是接口的编写者已知的还是未知的。
用户无法确认一个使用错误标志位来报错的接口,会不会抛异常。
![b3afb266f992f546e68773f65040413b.png](https://i-blog.csdnimg.cn/blog_migrate/15ed7206e8def35d6fb0769d440b5618.jpeg)
人们的确痛恨不一致性,但是却没有找到问题的症结所在,
让接口表现一致,是一种经常被推进,但实际上是一种过于理想化的解决方案。
在这一点上,我认为问题的症结在于知识没有被明确的传达给用户。
例如,如果我们在某个地方明确表明,“通过错误标志位来报错的接口一定不会抛异常”,
那么即使某些接口采用了不一致的报错方式,
我们仍然可以从容的处理了。
因此,不一致并没有问题,有问题的是歧义性。
4. 传递知识
经过上文的例证,我们看到接口所传递的知识比接口所提供的功能一样重要。
为了提供健壮的接口,那就得从一开始告诉用户,不能这样使用。
![81997d6673e729799624a252d8029715.png](https://i-blog.csdnimg.cn/blog_migrate/f6498558ed6d35de06e7598444c8027f.jpeg)
当然,寄希望于用户不会这样使用是没作用的。
墨菲定律表明,凡是可能出错的事,迟早会出错。
因此,除了上文讨论如何提供功能之外,
我们还要对用户,以及我们的依赖方进行管理。
4.1 用户管理
至少应该在某个地方明确表明,接口总共包含了哪些使用方式,
每一种使用方式,预期会得到怎样的结果,
越明确越好。
![f55413fea9a36c26bc24481292d6f5a8.png](https://i-blog.csdnimg.cn/blog_migrate/7eb61ced166de9fd89ba9848905ab08d.jpeg)
什么时候会出现异常,接口会以什么形式反馈出来。
例如,如果用户传入的参数不符合业务逻辑上的约束,接口会怎样表现,
接口在调用它的依赖方时出现了错误,应该如何处理。
这些都应该明确的向用户表明,
而不是,让用户假定接口应当采用何种处理方式。
4.2 依赖管理
![9cc7e241d3209f623a13da2fc70a4066.png](https://i-blog.csdnimg.cn/blog_migrate/15e44736bc4dce6e5dcab02ac738b5e9.jpeg)
我们要知道有哪些依赖,然后再仔细挖掘被依赖方所隐藏的信息,
被依赖方所采用的报错方式,可能并不是我们所期望的,
这其实并没有问题,问题是我们要有能力区分它们。
上文我们也看到了,很多项目都在“让依赖方进行修改”方面做出了无谓的努力,
这可能意味着我们在依赖管理方面做的还不够友好。
实际上,我们并不需要一个“规范的”接口,
但是我们需要一个具有“明确含义”的接口。
结语
我们来感受一下异常作为一种知识,是如何通过接口来传递的,
它如同接口的正常返回值一样。
可靠性的关键可能不在于表面上它不会出现错误,
而是在于,每一种可能出现的错误,都被恰当的责任人进行了处理。
![97fdaf7b1ffc23a8bfed1c632909b6fe.png](https://i-blog.csdnimg.cn/blog_migrate/e140bf2ba8135aaf97055dfd6764fc0c.jpeg)
当我们调用一个接口,它并没有抛异常,而是返回了一个Null
,
而且在多种不同的情况下,它都返回Null
,
这种接口简直是可笑的。
我们不得不联系接口的设计者,请他帮忙看看,到底发生了什么事情。
这真是一种极大的资源浪费。
这只是不良接口的一种表现形式而已,
所以重要的还是对异常流程,以及异常中所隐藏的知识有所察觉。
梁惠王曰:“寡人之于国也,尽心焉耳矣。河内凶,则移其民于河东,移其粟于河内。河东凶亦然。察邻国之政,无如寡人之用心者。邻国之民不加少,寡人之民不加多,何也?”
孟子对曰:“王好战,请以战喻。填然鼓之,兵刃既接,弃甲曳兵而走,或百步而后止,或五十步而后止。以五十步笑百步,则何如?”
曰:“不可。直不百步耳,是亦走也。”
曰:“王如知此,则无望民之多于邻国也。“
下一篇:设计模式奏鸣曲(四):可配置化与领域特定语言