一.信息隐藏:在模块的肚子里应该藏着决定
每个模块封装着几条知识,每条知识表示一个设计决定,这些知识嵌在模块的实现但不出现在接口中,所以其他模块根本看不见。
注意这里藏的不是代码,而是设计决定,什么是设计决定呢?
设计决定就是在实现某个功能时在多种可行的方案中最终拍板的选择。如用户列表用数组还是哈希,文本用行数组还是piece table,缓存用LRU还是LFU等。
决定会变,但变化会被关在一个模块里,这就是信息隐藏的全部意义。、
1.1 为什么藏住决定就能降复杂性?
1.接口变简单->认知负荷降低
接口只呈现模块功能的抽象视图,细节被抹掉,需要装进脑子里的东西少了。
2.系统可演化->变化放大小
被藏住的信息在模块外没有任何依赖,所以与这个信息相关的设计变更只影响这一个模块。
在设计新模块时,应该反复问自己一个问题:这个模块还能多藏点什么?藏的越多接口越简单,模块越深。
1.2 private≠信息隐藏
把变量和方法声明成private,只是让它们不能被类外直接访问。但如果类里面写着一堆getter/setter,私有变量的性质和用法照样通过公开方法泄露了出去,和public没有区别,信息隐藏藏的是为什么这样设计的只是,而不是访问权限关键字。
二.信息泄露:一次决定,多处安家
信息隐藏反面就是信息泄露:一个设计决定被反映到了多个模块中,在模块之间造成了依赖,决定一改,这些涉及到的模块都需要跟着改。
泄露有两条通道。
通道A:接口泄露(明面)
只要一条信息进了模块的接口,它就漏了,所以接口简单和信息隐藏的好是一件事的两面。
通道B:后面泄露(暗面)
接口很干净,但多个接口之间的知识却有共享
比如两个类都懂得某个数据类型,一个对文件进行读的操作,另一个进行写的操作,一旦文件类型变了,那么两个类的话都要变。
如果发现泄露的话有两条具体的路线:
1.合并 如果收影响的类都比较小,且和漏出的知识绑的比较紧,可以合并为一个类,让多个共同的知识合并到一个类中。
2.抽取
把这条知识抽取出来单独封装为一个类,但有个硬性条件:新类必须有一个能抽象掉细节的简单接口。如果新类的接口把大部分知识又透了出来,你只是把后门泄漏换成了接口泄漏——换了个漏法而已。
三.最常见的病因:时间顺序分解(按先做什么后做什么切模块)
这里举一个例子,在文件操作中,如果按照时间顺序分解的话有读文件,改文件,写文件三个操作,其中读文件和改文件都定义了文件的类型,但如果文件类型一变,读文件和写文件都要一起改,那不如把它们合并成为一个模块,很多时候设计模块时我们需要考虑的不是任务发生的先后顺序,而是每个任务包含哪些知识。

3.1 案例:HTTP server学生项目**:一场「类爆炸」**

中心主题