设计笔记[2008-2-17]
高质量的设计具有很多常见的特征。如果你能实现所有这些目标,你的设计就真的非常好了。这些目标之间有时会相互抵触,但这也正是设计中的挑战所在——在一系列相互竞争的目标之中做出一套最好的折中方案。有些高质量设计的特征也同样是
高质量程序的特征,如可靠性和性能等。而有些则只是设计范畴内的特征。
下面就列出一些设计范畴内的特征:
最小的复杂度(
Minimal complexity
)
正如刚刚说过的,设计的首要目标就是要让复杂度最小。要避免做出“聪明的”设计,因为“聪明的”设计常常都是难以理解的。应该做出简单且易于理解的设计。如果你的设计方案不能让你在专注于程序的一部分时安心地忽视其他部分的话,这一设计就没有什么作用了。
易于维护
(
Ease of maintenance
)
易于维护意味着在设计时为做维护工作的程序员着想。请时刻想着这些维护程序员可能会就你写的代码而提出的问题。把这些程序员当成你的听众,进而设计出能自明的(
self-explanatory
)系统来。
松散耦合(
loose coupling
)
松散耦合意味着在设计时让程序的各个组成部分之间关联最小。通过应用类接口中的合理抽象、封装性及信息隐藏等原则,设计出相互关联尽可能最少的类。减少关联也就减少了集成、测试与维护时的工作量。
可扩展性(
extensibility
)
可扩展性是说你能增强系统的功能而无须破坏其底层结构。你可以改动系统的某一部分而不会影响到其他部分。越是可能发生的改动,越不会给系统造成什么破坏。
可重用性(
reusability
)
可重用性意味着所设计的系统的组成部分能在其他系统中重复使用。
高扇入
(
high fan-in
)
高扇入就是说让大量的类使用某个给定的类。这意味着设计出的系统很好地利用了在
较低层次上的工具类(
utility classes
)。
低扇出
(
low fan-out
)
低扇出就是说让一个类里少量或适中地使用其他的类。高扇出(超过约
7
个)说明一个类使用了大量其他的类,因此可能变得过于复杂。研究发现,无论考虑某个子程序调用其他子程序的量,还是考虑某个类使用其他类的量,低扇出的原则都是有益的(
Card and Glass 1990; Basili, Briand, and Melo 1996
)。
可移植性
(
portability
)
可移植性是说应该这样设计系统,使它能很方便地移植到其他环境中。
精简性
(
leanness
)
精简性意味着设计出的系统没有多余的部分(
Wirth 1995, McConnell 1997
)。伏尔泰曾说,一本书的完成,不在它不能再加入任何内容的时候,而在不能再删去任何
内容的时候。在软件领域中,这一观点就更正确,因为任何多余的代码也需要开发、复审和测试,并且当修改了其他代码之后还要重新考虑它们。软件的后续版本也要和这些多余代码保持向后兼容。要问这个关键的问题:“这虽然简单,但把它加进来之后会损害什么呢?”
层次性
(
stratification
)
层次性意味着尽量保持系统各个分解层的层次性,使你能在任意的层面上观察系统,并得到某种具有一致性的看法。设计出来的系统应该能在任意层次上观察而不需要进入其他层次。
举例来说,假设你正在编写一个新系统,其中用到很多设计不佳的旧代码,这时你就应该为新系统编写一个负责同旧代码交互的层。在设计这一层时,要让它能隐藏旧代码的低劣质量,同时为新的层次提供一组一致的服务。这样,你的系统的其他部分就只需与这一层进行交互,而无须直接同旧代码打交道了。在这个例子中,层次化设计的益处有:(
1
)它把低劣代码的烂泥潭禁闭起来;(
2
)如果你最终能抛弃或者重构旧代码,那时就不必修改除交互层之外的任何新代码。
标准技术
(
Standard techniques
)
一个系统所依赖的外来的、古怪的东西越多,别人在第一次想要理解它的时候就越是头疼。要尽量用标准化的、常用的方法,让整个系统给人一种熟悉的感觉。
CHECKLIST: High-Quality Routines
核对表:高质量的子程序
大局事项
q
创建子程序的理由充分吗?
q
一个子程序中所有适于单独提出的部分是不是已经被提出到单独的子程序中了?
q
过程的名字中是否用了强烈、清晰的“动词+宾语”词组?函数的名字是否描述了其返回值?
q
子程序的名字是否描述了它所做的全部事情?
q
是否给常用的操作建立了命名规则?
q
子程序是否具有强烈的功能上的内聚性?即它是否做且只做一件事,并且把它做得很好?
q
子程序之间是否有较松的耦合?子程序与其他子程序之间的连接是否是小的(
small
)、明确(
intimate
)、可见(
viaible
)且灵活(
flexible
)的?
q
子程序的长度是否是由其功能和逻辑自然确定,而非遵循任何人为的编码标准?
参数传递事宜
q
整体来看,子程序的参数表是否表现出一种具有整体性且一致的接口抽象?
q
子程序参数的排列顺序是否合理?是否与类似的子程序的参数排列顺序相符?
q
接口假定是否已在文档中说明?
q
子程序的参数个数是否没超过
7
个?
q
是否用到了每一个输入参数?
q
是否用到了每一个输出参数?
q
子程序是否避免了把输入参数用为工作变量?
q
如果子程序是一个函数,那么它是否在所有可能的情况下都能返回一个合法的值?
健壮性
(robustness)
是指“系统在检测到错误后继续运行”的能力。通常架构详细描述的系统会比需求详细描述的系统更健壮。理由之一是,如果组成系统的各个部分都只能在最低限度上满足健壮性要求,那么系统整体上是达不到所要求的健壮程度的。在软件中,链条的强度不是取决于最薄弱的一环,而是等于所有薄弱环节的乘积。架构应该清楚地指出程序员应该“为了谨慎起见宁可进行过度工程
(overengineering)
”,还是应该做出最简单的能工作的东西。
详细定义一种过度工程(裕度工程)的方法尤其重要,因为许多程序员会出于专业自豪感,对自己编写的类做过度工程。通过在架构中明确地设立期望目标,就能避免出现“某些类异常健壮,而其他类勉强够健壮”的现象。