.. Kenneth Lee 版权所有 2019-2020
:Authors: Kenneth Lee :Version: 1.0
接口和名称空间辨识
在前面的讨论中,我常常用“名称空间”取代“接口”,这是一种下意识的行为,我没有细想 过这里的区别,上周讨论一个战略决策的时候用到这个概念,我这里细化一下。
我们在前面的讨论中已经反复强调这个概念了:设计设计现在,架构设计未来。你设计一 个线程库,你定义这样的接口:::
thread_create()
thread_delete()
thread_start()
thread_stop()
这在设计师眼中就是4个函数,我把这个叫做“接口”。但在架构师眼中它是一组概念:
| 线程:一组指令序列,通过函数指定序列的入口
| 线程的创建和删除:线程的管理需要占用资源,这些资源的分配和释放,
| 称为线程的创建和删除
| 线程的启动和停止:线程资源分配后并不能导致线程指令序列的执行,
| 需要主动激发,这个计划的要求,称为启动。线程启动后,
| 有两种可能终止指令序列的执行,其一是指定的序列函数返回。
| 其二是在指定的序列函数中要求停止。
| 其三是在指定的序列函数之外要求停止。前两者是同步行为,
| 其语义显而易见,后者为异步行为,其定义参见下文。
| 线程的异步停止:线程指令序列中包含一组“可终止点指令”(定义见下文),
| 在可终止点指令上,线程调度程序会判断是否有从其他线程发出的停止要求,
| 如果存在,线程会被终止。这种中止,称为线程的异步中止。
| ……
这种对概念的定义,在我的“名称空间”中,就称为“名称空间”:)
很明显,接口比概念空间精确,但概念空间比接口的定义范围广。但架构师定义的是概念 空间,不是接口。概念空间决定了接口的发展。因为如果只需要保证前面那四个接口,编 码的时候我有数不清的方法让你后面什么接口都加不上,还能证明给你看我的性能比谁都 高。
定义接口而不定义概念空间,从概念空间的角度来看,你也是错漏百出的。比如这个 thread_stop的语义,其实是非常模糊不清的。从实现者的角度,你觉得你每步都是精确的 语义,但使用者是否绑定你代码上的每个语义?使用者是否就根据你的代码写法来决定什 么时候可以调用stop?比如你是靠等待线程被调度出去再回来这个点来停止的,那使用者 就用使用这个语义,认为“可以在调度函数上加钩子释放线程资源”。这个特点一旦被这样 使用,你的代码就必须保持这个设计,你在这个问题上的设计自由度就没有了,竞争对手 用了更好的算法你也不要指望可以上了。
所以,设计构架的阶段,我们更强调设计概念空间,而不是接口本身。你分析可以分析流 程,分析实现方法,分析接口……都行,但你做架构设计,你必须做概念空间的定义。
概念空间的设计,重点是避免自相矛盾。用道德经的概念来说,就是要“食母”,找到影响 你定义接口的那些基本要素。你创建线程,本质是干什么?这个东西是否是必须的?你是 因为你要管理线程,而线程入口只有一个函数(以及它的参数),那么线程的状态能否从 这两个信息中获得?不能,所以,线程创建和删除的概念就是不可省略的,然则,它的行 为特征是什么?和其他概念是什么关系?控制了这个,所有外层(接口)的变化就被控制 了。概念空间是这些外部行为的“德”,是最难改变的部分,改变他们就是整个接口定义的 崩塌,调好这个调坏那里。控制了概念空间,就控制了未来。
然后我们讨论一个具体的问题:比如我们有一个硬件,对外提供了两个相对独立的功能, 但作为同一个硬件,他们有千丝万缕的关系:
现在的问题是,这个概念空间应该怎么设计?
上图这样摆肯定是不行的,两个功能是会被竞争环境驱动的,我们的目的不是自己好实现 ,而是竞争,两个功能发展的时候不断被对方的名称空间牵扯,你很难让名称空间自恰。
更合理的定义应该是这样的:
功能2管理了硬件的整个概念空间,并对功能1提供概念空间,这样几个概念空间就容易控 制自恰。
但很多人不愿意用这样的设计的,特别是功能1的设计者:尼玛老子也对硬件优化有要求, 凭什么要经过效率低下的功能2提供功能?
这听起来是个部门政治问题,但其实它也是个技术问题,它是有合理性的。我们要让一线 的人呼唤炮火,所以在这种问题上,这样才是更合理的设计:
让功能1和功能2各种控制各自的功能名称空间,但让两个名称空间正交。比如硬件初始化 和复位,休眠,让它们落实在其中一边,这样也可以构成合理的名称空间设计。