【Unix编程艺术】第4章 模块性

来源:互联网 发布:淘宝网店怎么提高人气 编辑:程序博客网 时间:2024/06/06 09:33

4章 模块性


Unix程序员骨子里的传统是:更加笃信重视模块化、更注重正交性和紧凑性等问题。

4.1 封装和最佳模块大小

模块化代码的首要特质就是封装。封装良好的模块不会过多向外部披露自身的细节,不会直接调用其它模块的实现码,也不会胡乱共享全局数据。模块之间通过应用程序编程接口(API一组严密、定义良好的程序调用和数据结构来通信。这就是模块化原则的内容。

有一种很好的方式来验证API是否设计良好:如果试着用纯人类语言描述设计(不许摘录任何源代码),能否把事情说清楚?养成在编码前为API编写一段非正式书面描述的习惯,是一个非常好的办法。一些最有能力的开发者,一开始总是定义接口,然后编写简要注释,对其进行描述,最后才编写代码—— 因为编写注释的过程就阐明了代码必须达到的目的。

模块太大或大小都会引发bug的增多。Hatton的经验数据表明,假设其它所有因素(如程序员能力)都相同,200400之间逻辑行的代码是“最佳点”,可能的缺陷密度达到最小。

4.2 紧凑性和正交性  

紧凑型

紧凑性就是一个设计是否能装进人脑中的特性。测试软件紧凑性的一个很实用的好方法是:有经验的用户通常需要操作手册吗?如果不需要,那么这个设计(或者至少这个设计的涵盖正常用途的子集)就是紧凑的。

紧凑不等于“薄弱”。紧凑也不等同于 “容易学习”。紧凑也不意味着“小巧”。

极少有绝对意义上紧凑的软件设计,不过从宽松一些的意义上,许多软件设计还是相对紧凑的。他们有一个紧凑的工作集:一个功能子集,能够满足专家用户 80%以上的一般需求。

正交性

在纯粹的正交设计中,任何操作均无副作用;每一个动作(无论是API调用、宏调用还是语言运算)只改变一件事,不会影响其它。无论你控制的是什么系统,改变每个属性的方法有且只有一个。

SPOT原则 不要重复自身(Don't Repeat Yourself"),意思是说:任何一个知识点在系统内都应当有一个唯一、明确、权威的表述。在本书中,我们更愿意根据Brian Kernighan的建议,把这个原则称为“真理的单点性(Single Point of Truth)”或者SPOT原则。

数据结构也存在类似的SPOT原则:“无垃圾,无混淆”(No junk, no confusion)。“无垃圾”是说数据结构(模型)应该最小化.

形式法 要提高设计的紧凑性,有一个精妙但强大的方法,就是围绕“解决一个定义明确的问题”的强核心算法组织设计,避免臆断和捏造。

与形式法相对的是试探法——凭经验法则得出的解决方案,在概率上可能正确,但不一定总是正确。试探法的问题在于这种方案会增生出大量特例和边界情况。

4.3 软件是多层的

设计函数或对象的层次结构可以选择两个方向,一个方向是自底向上,从具体到抽象——从问题域中你确定要进行的具体操作开始,向上进行。

另一个方向是自顶向下,从抽象到具体——从最高层面描述整个项目的规格说明或应用逻辑开始,向下进行,直到各个具体操作。

有一个非常具体的方法可以考量二者的差异,那就是问问设计是围绕主事件循环(常常具备与其非常接近的高级应用逻辑)组织,还是围绕主循环可能调用的所有操作的服务库组织代码。自顶向下的设计者通常先考虑程序的主事件循环,以后才插入具体的事件。自底向上的设计者通常先考虑封装具体的任务,以后再按某种相关次序把这些东西粘合在一起。

Unix程序员继承了一个居于系统程序设计核心的传统,Unix程序员更倾向于自底向上的编程方式。

胶合层 当自顶向下和自底向上发生冲突时,其结果往往是一团糟。顶层的应用逻辑和底层的域原语集必须用胶合逻辑层来进行阻抗匹配(impedance match)。

4.4 程序库

Unix编程风格强调模块性和定义良好的API,它所产生的影响之一就是:强烈倾向于把程序分解成由胶合层连接的库集合,特别是共享库(在Windows和其它操作系统下叫做“动态连接库”(DLL)。

如果谨慎而聪明地处理设计,那么常常可以将程序划分开来,一个是用户界面处理的主要部分(策略),另一个是服务例程的集合(机制),中间不带任何胶合层。

Unix下,通常是清晰地划分出这种层次,并把服务程序集中在一个库中并单独文档化。在这样的程序中,前端专门解决用户界面和高层协议的问题。

库分层的一个重要形式是插件,即拥有一套已知入口、可在启动以后动态从入口处载入来执行特定任务的库。这种模式必须将调用程序作为文档详备的服务库组织起来,以使得插件可以回调。

4.5 Unix和面向对象语言

Unix的模块化传统就是薄胶合层原则,也就是说,硬件和程序顶层对象之间的抽象层越少越好。这部分是因为C语言的影响。在C语言中模仿真正的对象很费力。正因为这样,堆砌抽象层是一件非常累人的事。这样,C语言中的对象层次倾向于比较平坦和透明。

OO语言使抽象变得很容易了。OO语言鼓励“具有厚重的胶合和复杂层次”的体系。当问题域真的很复杂、确实需要大量抽象时,这可能是好事,但如果编码员到头来用复杂的办法来做简单的事情——仅仅是为他们能够这样做,结果便适得其反。

Unix世界对OO语言的批判更直接了当;Unix程序员知道什么时候不该用OO;就算用OO,他们也尽可能保持对象设计的整洁清晰。

OO在其取得成功的领域(GUI、仿真和图形)之所以能成功,主要原因之一可能是因为在这些领域里很难弄错类型的本体问题。

4.6 模块式编码

在编写代码时,问问自己以下这些问题,可能会有助于提高代码的模块性:

1. 有多少全局变量?

2. 单个模块的大小是否在Hatton的“最佳范围”内?

3. 模块内的单个函数是不是太大了?

4. 代码是不是有内部API

5. API的入口点是不是超过七个?有没有哪个类有七个以上的方法?数据结构的成员是不是超过七个?

6. 整个项目中每个模块的入口点数量如何分布?是不是不均匀?

0 0
原创粉丝点击