5.软件构建中的设计
5.3 设计构造块:启发式方法
- 当继承能简化设计时就继承。定义对象之间的相同点和不同点就叫“继承”。
隐藏信息
- 使用了隐藏信息的大型项目,比没使用的,容易修改4倍
秘密和隐私权
-
隐藏起来的秘密可能是某个易变的区域,或某种数据类型的实现方式,或某个需要隔离的区域,在这个区域中发生的错误,不会给程序其余部分带来太大损失。类的职责就是把这部分信息隐藏起来,并保护自己的隐私权。
-
隐藏信息就是隐藏复杂度。
-
设计一个类的一项关键性决策就是确定类的哪些特性应该对外可见或隐藏。
暴露过多的信息会增加人的智力负担,如果别的类引用了你的类的细节,这对双方都是bug隐患。
-
设计类的接口与设计其他环节一样,都是一个迭代的过程。一次没得到合适的接口就多尝试几次,知道稳定下来。
两类秘密
- 隐藏复杂度,这样你就不用再去应付它
- 隐藏变化源,这样当变化发生时,其影响就限制在局部范围内。
隐藏信息的价值
-
信息隐藏有着独特的启发力,他能够激发出有效的设计方案。
-
问题“这个类需要隐藏些什么?”正切中了接口设计的核心。
-
请养成问“我该隐藏些什么?”的习惯,你会惊奇地发现,有很多很棘手的设计难题都会在你面前化解。
找出容易改变的区域
- 好的程序设计所面临的最重要挑战之一就是适应变化。
- 目标应该是把不稳定的区域隔离起来,让变化所带来的影响限制在一个子程序、类或者包的内部。
- 找出看起来容易变化的项目。好的需求应该包含一份潜在变化清单。没有的话,请参考“在任何项目中都容易发生变化的区域”的讨论。
- 把容易变化的项目分离出来。容易同时发生变化的组件划分到同一类中。
- 把看起来容易变化的项目隔离开。类之间的接口可以使其对变化不敏感,要设计好接口。
- 主动应对变更的最有力的技术之一,是表驱动技术。
- 容易发生变化的区域:
- 业务规则:基于业务规则的逻辑不应该遍布于整个程序,而是仅仅隐藏于系统的某个角落。
- 输入和输出:检查所有外部接口,看看有哪些可能的变化,通常是个不错的主意。(类的私有方法不检查,公开方法检查输入)
- ⭐️困难的设计区域和构建区域:把困难的设计区域和构建区域隐藏起来也是很好的想法,因为这些代码很可能因为设计得很差而需要重新做。
- 状态变量:与大多数其他类型的数据相比,这种东西更容易改变。在使用状态变量时增加至少两层的灵活性和可读性:
- 不要使用布尔值作为状态变量,请换用枚举类型。
- 使用访问器子程序取代对状态变量的直接检查。
- 数据量的限制:使用具名常量 MAX_EMPLOYEES 来隐藏 100 这样的数字
- 预料不同程度的变化
- 如何找出容易变化的区域
- 首先找出对用户最有用的最小子集,它构成来系统的核心,不容易发生变化
- 然后逐步扩展这个系统
- 对于附加功能可以提取出来,并把它们的可能改进隐藏起来
- 如何找出容易变化的区域
保持松散耦合
耦合的标准
- 规模,模块间的连接数量
- 可见行。通过参数表传递数据便是一种明显的连接,因为值得提倡。通过修改全局数据而使另一个模块能够使用该数据便是一种“鬼鬼祟祟”的做法,因此是很不好的设计。
- 灵活性。一个模块越容易被其他模块所调用,那么它们之间的耦合关系就越松散。
耦合的种类
- 简单数据参数耦合。正常
- 简单对象耦合。也很不错
- 对象参数耦合。object1要求object2给它一个object3。有些紧密了,不大好。
- 语义上的耦合。一个模块使用了另一个模块的语法元素,而且还使用了有关那个模块内部工作细节的语义知识。
- 语义上的耦合是非常危险的,A模块调用了B模块,更改B模块中的代码可能会破坏A模块,破坏方式是编译器无法检查的。类似这样的代码崩溃时,其方式是非常微妙的,看起来与被使用的模块中的代码更改毫无关系,因此会使得调试工作变得无比困难。
松散耦合的关键之处在于,一个有效的模块提供的出了一层附加的抽象——一旦你写好了它,你就可以想当然的去用它。这样就降低了整体系统的复杂度,使得你可以在同一时间只关注一件事。如果对一个模块的使用要求同时关注好几件事——其内部工作的细节、对全局数据的修改、不确定的功能点等——那么就失去了抽象的能力。模块所具有的管理复杂度的能力也就削弱或完全丧失了。
类和子程序是用于降低复杂度的首选和最重要的智力工具。如果它们没有帮你简化工作,那么它们就失职了。
查阅常见的设计模式
5.4 设计实践
- 迭代
- 分而治之
- 自上而下和自下而上的设计方法
- 自上而下的论据
- 自下而上的论据
- 其实并没有争论
- 建立试验性原型
- 合作设计
- 要做多少设计才够
- 记录你的设计成果
要点
- 软件的首要技术使命就是管理复杂度。以简单性作为努力目标的设计方案对此最有帮助。
- 简单性可以通过两种方式来获取:一是减少在同一时间所关注的本质性复杂度的量,二是避免生成不必要的偶然的复杂度。
- 设计是一种启发式的过程。固执于某一种单一方法会损害创新力,从而损害你的程序。
- 好的设计都是迭代的。你尝试设计的可能性越多,你的最终设计方案就会变得越好。
- 隐藏信息是个非常有价值的概念。通过询问“我应该隐藏什么?”能够解决很多困难的设计问题。