软件模块化设计--接口,数据和上下文

写”给人读的代码”—软件模块

东方瀞
ylxxwx@gmail.com
为什么大多数代码都那么难懂?如果简单归结于代码设计太差,这样的解释太容易了,也太廉价了,但却没有任何帮助。


听说过这么一个小故事,有人在大街上采访了很多人,“如果你在一条陌生的大街上感到内急,你最希望看到什么?”结果大多数人都说麦当劳或肯德基。这是为什么?难道满大街只有麦当劳、肯德基里有卫生间?显然不是,而是大家都知道麦当劳、肯德基里面肯定有卫生间;而且他们的布局都有固定模式,很容易找到卫生间;另外还知道他们不拒绝外人使用卫生间。而对于其他的商户,有没有卫生间?卫生间在什么地方?会不会让外人使用?这些都是未知的。作为普通人,我们对未知的事物都有着天然的恐惧和焦虑。两相比较,自然对确定的麦当劳、肯德基就特别期待了。
代码亦如是。我们对别人写的代码有抵触、恐惧的情绪也是人之常情。这是因为别人的代码对我们来说可能是一个完全未知的事物,也许是我们不了解这个领域的知识,也许是我们不知道软件设计的思路,或许两者都有。如果是这样,那我们也学习麦当劳、肯德基,将我们所有的代码都设计成相同的布局,是不是我们读来就容易很多。前些年,我们小组曾经学习过某个大公司的一个产品代码,大家都认为比较容易理解,虽然其设计不是很精妙。究其原因,就是这个产品的代码都有着相同的风格、布局。读懂一个模块,其他模块也就容易了。
那什么样的代码风格、布局能更让人理解呢?显然是和人类认知事物的规律越靠近就越容易。让我们来看看我们是怎样认识一个新系统的。当我们面对一个新系统时,一般我们都从三个层面来了解他。1.他和哪些外界系统有交互;2.他是什么,由哪些部分组成;3.他对外界事件是如何响应的。这就是软件设计中常用的Interface View,Interaction View(也叫Dynamic View),Static View。那相应的我们把一个模块的代码分为三个子部分,Data部分对应Static View,Context部分对应Interaction View以及Interface部分对应Interface View。并且分布在各自的文件夹中。如图一所示。

Interface View,揭示该系统与其他系统的接口关系。在实现中,还可以使用不同文件来区分提供给不同模块的接口。如图二所以,DHCP模块需要分别提供结构给输入输出模块和硬件驱动模快。在图一对应文件就相应有Itf4Io和Itf4Hwd文件。

Static View,揭示了组成系统的对象,包括概念对象、实体对象和对象间的关系,以及装载这些对象的容器。在代码实现时,应该是一个个的class或container。这部分是整个模块的抽象。为了抽象出更容易理解的代码,可以参照以下几个原则:1)对象及对象关系必须真实反映领域知识;2)尽可能减少对象间的依赖;3)如果要引入对象间的依赖,优先考虑是不是放在Context部分更合理。图三是为DHCP模块设计的static View。他完全对应于DHCP协议里的概念。

Interaction View(Context View),揭示系统在输入事件下的响应。这部分的工作应该是1)对象的创建、删除和事件通知,2)对容器进行移入,移出和查询操作。这部分代码应该专注于外界事件触发下,对象的增、删、改以及对象在容器间的移动。他只关注对象的变化和交互,不做具体的事情,而且只是提供一个对象相关发生关联的上下文。

与一般软件代码布局相比,这里将Context View的代码和Static View的代码稍加分离。为什么要将Context View的代码和Static View代码分离开?人类认识新事物是分两个阶段的,第一个阶段是概念获取和辨识,第二个阶段是了解系统对外界事件的响应。


软件设计也是这样,我们在软件设计中分Static View和Interaction View。Static View着重描述软件是什么; Interaction View着重描述软件对外界事件的反应。另外,Context View代码与Static View代码的分离,为减少个对象间的依赖也提供的方便。例如,在上述DHCP系统中,对DHCP请求事件的响应,可以由一个Request Context处理,在这个Context中,创建一个TranX,并把他加入到WaitList中,而不是由TranX自己加入到WaitList中,这样TranX就不需要知道WaitList的存在。再如,ResAvaiable Context处理底层资源空闲的事件,这个Context,从WaitList中取出一个TranX,放入InProcessList中,并发送SendRequest事件给该TranX。而不是由WaitList或InProcessList来响应这个事件,TranX,WaitList和InProcessList就相互没有了依赖。
这样的代码功能划分,使得代码的布局更贴近人类认识事物的规律。也使得代码和设计可以一一对应,提高了代码的可阅读性和可维护性。当然软件设计与其说是一门技术,不如说更像一门艺术,没有绝对的正确和错误。这里提供一种软件设计思路的探索,愿其有益。也欢迎拍砖。

posted @ 2015-11-22 10:07 东方瀞 阅读(...) 评论(...) 编辑 收藏