.. Kenneth Lee 版权所有 2019-2020
:Authors: Kenneth Lee :Version: 1.0
高层封装的设计战略
本文延续前一篇(:doc:不知为美
)的逻辑,讨论一下高层封装的设计战略问题。
如前所述,我们围绕“身份”进行高层设计,避免破坏身份定义进行逻辑组装。这会导向两 种及接口封装逻辑。一种是“贴紧身份”的原始接口。一种是“紧贴需求”的高层接口。
还是用前一篇的设备管理做例子:
你在内核封装了一个设备,这个身份确定了,围绕它建立最基本的功能:open, close, ioctl, mmap, signal等等。做这种接口是没有多少风险的。因为要不你这个身份定义得不 好,要不你就只能这样实现,无论用多少“巧妙”的设计方法和算法,你改变不了这些基本 功能。
对于这层接口,我觉得遵循一个原则就可以了:不要引入多余的依赖。比如你不要引入线 程库,因为在这里引入线程库,其他所有设计都会受这个线程库约束的影响。其他的引入 也是一样的,比如你可以引入自动加密,使用openssl,openssl就会引入其他依赖,比如 它会以来线程库,然后它会引入dl_open库,然后它会引入对linux的依赖,然后……。然后 ,你基础平台的逻辑就全部被绑定了。
本文关键要讨论的是更高一层的设计封装。比如前面这个设备管理。假如用户要求我们自 动管理设备损坏的时候自动重新open一个新的设备,保证业务延续性。
那么按我们前面的原则,我们不动基本的open/close/ioctl...的接口层,我们以它为基础 ,封装一个高层接口,实现一个可以自动在设备发错误signal上来后替换的封装层,这时 应该怎么封装?
有人天然就会想到:创建线程处理信号,这个线程和read,write流程(这个不一定经过内 核,可能通过mmap的内存进行通讯)进行同步,保证在读出数据后判断中间有没有发生异 常,然后根据这个要求返回数据……
这种思路常常是错的。
大家做过装修吗?在同一个房间里面,你要放排水,要安排电路,要安排房间。你可以在 看不到其他设计的情况下直接决定你的排水的设计吗?
你做一个业务,所有平台功能都是要为主业务服务的。你装修一个房间,核心解决的问题 是住在里面的人和他的需要,其他功能是围绕这个需要来的。
所以,没有房间的业务使用方法设计前,水电设计是做不了的。你应该先做房间和房间功 能安排,然后根据这个安排来决定水电怎么部署。
所以,你要封装一个信号处理和线程库,你首先要分析的是用户将会怎么使用你的设备, 保证你的线程安排满足它的要求了,然后你把你的信号处理藏到这个需求中。否则,你信 号处理做一个线程安排,用一个锁。用户的线程按他的业务要求做一个线程安排,再用一 个锁。就这两个锁怎么能不互相死锁都是你未来面对的巨大挑战,你这不是找死?
所以,高层封装的设计战略,大部分时候,就是“依附主业务”,不能独立于主业务进行设 计。
这个逻辑其实也解释了之前在这里讨论过的问题:
:doc:`弟子规:美国军方禁止在C语言程序中使用malloc`
为什么“弟子规”或者“弟子规一样的编程规范”不可接受?因为它们作为辅助设计,不能超 过主业务逻辑的位置。C++好不好?好,但你不能要求全公司只能用C++。git管理代码好不 好,但你也不能要求全行业只能有git。更不要说你这个项目不用goto很好,你要求所有人 都不要用goto了。万物芸芸,各归其根,才是最高效的,把一些“貌似正确”的要素提高到“ 原则”的位置,这是把好变坏的方法。