第 11 章 设计它两次
设计软件非常困难,因此您对如何构造模块或系统的初步思考不太可能会产生最佳的设计。如果为每个主要设计决策考虑多个选项,最终将获得更好的结果:设计两次。
假设您正在设计用于管理 GUI 文本编辑器文件文本的类。第一步是定义该类将呈现给编辑器其余部分的接口。与其选择想到的第一个想法,不如考虑几种可能性。一种选择是面向行的界面,该界面具有插入,修改和删除整行文本的操作。另一个选择是基于单个字符插入和删除的接口。第三种选择是面向字符串的接口,该接口可对可能跨越线边界的任意范围的字符进行操作。您无需确定每个替代方案的每个功能;在这一点上,勾勒出一些最重要的方法就足够了。
尝试选择彼此根本不同的方法;这样您将学到更多。即使您确定只有一种合理的方法,无论您认为有多糟糕,都应该考虑第二种设计。考虑该设计的弱点并将它们与其他设计的特征进行对比将很有启发性。
在对备选方案进行粗略设计之后,列出每个方案的优缺点。接口最重要的考虑因素是高级软件的易用性。在上面的示例中,面向行的界面和面向字符的界面都需要使用文本类的软件中的额外工作。面向行的界面将需要更高级别的软件来在部分行和多行操作(例如剪切和粘贴所选内容)期间拆分和合并行。面向字符的接口将需要循环来实现修改多个字符的操作。还值得考虑其他因素:
- 一种选择是否具有比另一种更简单的界面?在文本示例中,所有文本界面都相对简单。
- 一个接口比另一个接口更通用吗?
- 一个接口是否比另一个接口更有效地实现?在文本示例中,面向字符的方法可能比其他方法慢得多,因为它需要为每个字符单独调用文本模块。
比较了备选设计之后,您将可以更好地确定最佳设计。最佳选择可能是这些选择之一,或者您可能发现可以将多个选择的功能组合到一个比任何原始选择都要好的新设计中。
有时,没有其他选择特别有吸引力。发生这种情况时,请查看是否可以提出其他方案。使用您在原始替代方案中发现的问题来推动新设计。如果您在设计文本类并且仅考虑面向行和面向字符的方法,则可能会注意到每个替代方案都比较笨拙,因为它需要更高级别的软件来执行其他文本操作。那是一个危险信号:如果要有一个文本类,它应该处理所有文本操作。为了消除其他文本操作,文本界面需要更紧密地匹配高级软件中发生的操作。这些操作并不总是对应于单个字符或一行。
两次设计原则可以在系统的许多级别上应用。对于模块,您可以首先使用此方法来选择接口,如上所述。然后,您可以在设计实现时再次应用它:对于文本类,您可以考虑实现这些实现,例如行的链接列表,固定大小的字符块或“间隙缓冲区”。实现的目标与接口的目标是不同的:对于实现,最重要的是简单性和性能。在系统的更高层次上探索多种设计也很有用,例如在为用户界面选择功能或将系统分解为主要模块时。在每种情况下,如果您可以比较几种选择,则更容易确定最佳方法。
对其进行两次设计不需要花费很多额外的时间。对于较小的模块(如课程),您可能不需要一两个小时就能考虑替代方法。与您将花费数天或数周时间来实施该课程相比,这是很少的时间。最初的设计实验可能会导致明显更好的设计,这将比花两次设计时间所花的时间多。对于较大的模块,您将花费更多的时间进行初始设计探索,但是实现也将花费更长的时间,并且更好的设计所带来的好处也会更高。
我已经注意到,真正聪明的人有时很难接受两次设计原则。当他们长大后,聪明的人会发现,他们对任何问题的第一个快速构想就足以取得良好的成绩。无需考虑第二种或第三种可能性。这使得容易养成不良的工作习惯。但是,随着这些人变老,他们将被提升到越来越困难的环境中。最终,每个人 都达到了您的第一个想法不再足够好的地步。如果您想获得非常好的结果,那么无论您多么聪明,都必须考虑第二种可能性,或者第三种可能性。大型软件系统的设计属于此类:没有人能很好地在首次尝试时就将其正确。
不幸的是,我经常看到聪明的人坚持要实现第一个想到的想法,这会使他们无法发挥其真正的潜力(这也使他们沮丧地工作)。也许他们下意识地相信“聪明的人第一次就能做到”,因此,如果他们尝试多种设计,那将意味着他们毕竟并不聪明。不是这种情况。不是说你不聪明;问题真的很难解决!此外,这是一件好事:处理一个必须认真思考的难题比处理一个根本不需要思考的难题更有趣。
“两次设计”方法不仅可以改善您的设计,而且可以提高您的设计技能。设计和比较多种方法的过程将教您使设计更好或更坏的因素。随着时间的流逝,这将使您更容易排除不良的设计并磨练真正的出色设计。