本节书摘来自华章计算机《需求设计:构建用户想要和需要的产品》一书中的第1章,第1.2节,作者:[英] 克里斯·布里顿(Chris Britton) 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1.2 什么是设计
设计是个很常见的说法,没有几个人会深挖它的含义,但如果仔细想一想IT应用程序的设计与一般的设计的区别,你就会发现,IT应用程序其实是很特殊的。这种特殊性促使我们思考一个问题:应用程序的设计真有这么特殊吗?或者说,我们是不是将它看得太过特殊,以致忽视了一些最为基本的共性?
首先,我们来定义设计这个词。设计,是对想要构建的事物或系统所做的概念化处理。设计(design)既是名词,又是动词。刚才定义的是名词,现在我们给出动词的定义:(作为动词的)设计,就是指创建设计方案。
笔者故意把这个定义下得比较宽泛。你可能既没有给自己所构想的最终产品绘制一份图表,也没有给自己所要构建的事物或系统撰写一份描述,但只要你脑中有它的概念,就仍然可以说是在做设计。
设计当然可以有很多种不同的方式,但就其基本结构来看,所有的设计都有着深刻的相似之处。笔者的这种说法,以前还没有别人提过,但我想大多数的设计者都应该能理解这个意思吧?(有一次我看到过一种类似的说法,但它讲得太过隐晦,我无法确定和自己说的是不是一个意思。)
设计的结构,可以像下面这样分成3步或4步:
1.理解(understanding)。理解自己想要达到的目标(也就是想要开发的东西),以及需要克服的困难。这一步的成果是一份需求清单。
2.猜想(hypothesis)。这一步需要有一些创意,需要依靠直觉并做出一些有理由的猜测。我们从诸多想法中挑选一个或几个,并进行下一步。
3.细化(elaboration)。要确保所有的需求都得到覆盖,并确保所有已知的弱点都得到解决,以便使设计更加充实。
4.分析(analysis)。对设计进行检查、测试或运算,看看当初的猜想有没有不对的地方。
其中的第4个步骤是可选的,或者说,很多人都不做这一步。
我们以桥为例来说明这4个步骤。需求很简单,就是建造一座每小时可以通行X辆车的桥,该桥在Y点跨越一条河,桥的单程长度是半英里,桥的成本不超过Z。由于成本限制,我们必须把隧道方案排除。设计猜想,指的是要构建什么桥,是悬索桥、箱梁桥、传统的日式木桥、典型的石拱桥,还是其他类型的桥。细化阶段,指的是确定桥的样子,桥柱的位置,以及桥的材质,换句话说,就是确定桥的各种细节。分析阶段,可能指的是我们需要计算桥的承重能力,并判断当河水流得比较快时,会不会把桥冲垮,此外可能还要做其他一些检查。
上面这个例子可以把这4个步骤划分得非常清晰,但现实工作中的设计流程,其各个步骤之间一般都没有这么分明。尤其要注意的是,分析步骤有可能融入细化步骤中,而我们在敲定整个设计之前,还有可能需要再度进行分析。有的时候,我们有可能刚提出一个猜想,就立刻要对其进行迅速的分析检查,以判断这个想法是否合理。为了从某些方面推进设计的细化,我们需要寻找设计中的弱点,并寻找弥补这些弱点的方式,而分析,正是一种可以用来寻找弱点的工具。
IT界有一个流行的概念,叫做模式(pattern)。它有点像预先提出的一些设计猜想。这个概念其实并不新颖,它固然可以就近追溯到建筑师Christopher Alexander所提出的设计理念,但早在18世纪,就已经出版了很多模式方面的书籍,这些书可以帮助那些训练较少的建筑者去建造古典风格的房屋。模式本身是好的,但是大家要注意:设计者必须发挥出自己的创意,必须用创新的态度去思考并寻找解决方案。
由于设计过程中会有反馈,因此设计者可能需要跳回早前的步骤。在细化阶段,你可能发现当初所做的设计猜想是行不通的,于是必须回头去尝试另外一种办法,换句话说,就是要返回猜想环节,去提出另外一种想法。此外,还有一点也非常重要,那就是:我们有可能会在细化环节之中,发现当初所提的需求有所遗漏,或是有相互不一致之处,此时,必须回头去修改需求。当设计猜想发生变化时,需求的重点也会随之改变。例如,在设计桥梁的时候,如果我们决定把设计猜想由原来的悬索桥,改为一种像伦敦的塔桥或武汉的转体桥那样,能够提升或移动路面的桥,那么就必须定出一套全新的需求,并在其中指出这座桥多长时间需要做一次提升或旋转。因此,在整个设计流程之中,即使是早已定好的需求,也依然有可能会发生变化。图1-1演示了流程之中的反馈情况。
在分析环节中,我们当然有可能发现错误。有的时候,可以通过调整细节来修复该错误,也就是说,可以回到细化环节去重做其中的某一部分。还有一些时候,则需要返回第2步,也就是重新提出另外一种猜想。
成本因素,很有可能会出现在项目的需求之中。如果在细化或分析阶段发现成本过高,那有可能必须重新考虑整个设计方案。
笔者刻意选择了猜想这个词,来指出做设计与推进科学理论之间的相似点。这种提出假说并试图证伪的办法,与Karl Popper的主张有关,现在大家基本都把它当作一条科研准则来看待。在Popper提出该主张之前,很多人都以为科学的进步依赖于事实的积累,事实积累到一定程度,科学定律自然就会展现出来。很多人对设计的看法也和这种积累及涌现模型类似,而且笔者认为,大部分人在开发应用程序时也是这样想的,我们总是去收集需求,然后等着设计方案自己涌现出来。科学界已经抛弃了这种错误的想法,而我们今天在设计应用程序时,也同样应该放弃这个想法。笔者之所以这样说,主要原因有两个。第一,我们要处理的并不是确定的问题,而是一些通过猜测和直觉所提出的想法,在这些方面,是没有唯一答案可言的,于是,我们就需要通过细化及分析来确保这些猜测和直觉是有理有据的,并把它们构建在坚实的基础之上。第二,正如前面所讲,我们有可能需要在猜想环节和细化环节中去修改早前的需求,这说明早前的需求与最终要执行的解决方案之间有着某种交互关系。
由于这些步骤之间会发生反馈,因此设计活动不应该锁定在某个固定的时间段内。我们唯一能指望的,就是设计活动所花的时间应该远远少于实现活动所花的时间。然而这对IT应用程序的设计来说却很成问题,因为编程本身也是在做设计。本书中的很多内容,都是想帮助大家尽早(而不是过早地)做出重要的决策,从而使得编程活动变得相对有规律一些。
笔者认为,优秀的设计者会本能地完成这四个步骤。他们身上的两项优点是糟糕的设计者所不具备的。第一,他们不会频繁地返工,而是会在脑中画有一幅设计全景图,这张图的内容领先于当前正在做的内容。换句话说,他们确实也会想到返工,但这种返工,指的是在思维层面上反复推演,而不是在手头工作上来回折腾。第二,他们会花时间来处理一些主要的决策,比如在诸多猜想中进行选择等。他们并不急于得出结论,而是愿意倾听建议,并乐于探索各种替代方案。笔者在本书中会多次建议大家像设计者一样去思考。所谓像设计者一样去思考,是说你应该仔细思考这些猜想,思考它们将会细化成何种模样,并且应该考虑设计与需求之间的互动。此外,还应该寻求一些对设计进行分析的方式。
设计有很多种做法。笔者将这些做法分为专项的设计、有计划的设计以及工程化的设计这三大类,并在下面三个小节中分别讨论它们。
1.2.1 专项的设计
专项的设计(ad hoc design)是从一张草图或一个想法开始构建的。在推进过程中,你很可能会发现设计方案里面有一些疏漏或问题,并会对其做出调整。有的时候,这种调整的幅度相当大,以致你必须把某些已经完工的部分重新做一遍。
专项设计在IT项目里面很常见,笔者自己就多次做过这种设计。它很适合应对那种例行的项目,也就是说,那些项目与你原来做过的项目相似,而且你也大概知道自己应该怎么做。更准确地说,这种设计方式通常适用于具备下列特征的项目:
- 项目规模较小。设计者一个人就能把设计方案构思好,并且可以相当明确地给团队中的其他成员指派任务。有些情况下,如果可以精确地厘定其中某一部分设计工作的范围和需求,那么设计者还可以把这部分设计工作转交给项目团队之中的另外一个人来做。
- 项目本身是独立的。也就是说,在开发该项目的同时,没有别的项目需要依赖于本项目。一般来说,如果某个项目依赖于另外一个项目,那我们就必须把这种依赖关系精准地确定下来。比如,我们必须准确指出项目所要接收及发送的数据,或是指出项目需要在数据库之共享的数据。采用专项设计方式所开发的项目,是很难做到这一点的,因为这种项目的设计者经常会改变自己的构想,而对于依赖本项目的其他项目来说,其设计者可能会对这种频繁的改动感到恼火。
- 设计者与利益相关者之间的关系很密切。这种项目的利益相关者,即便没有看到太多证据,也依然必须相信项目的设计者确实能做出他们想要的东西。为了缓解这种忧虑,项目团队可以经常为利益相关者展示当前已经完成的某一部分功能。如果利益相关者在项目的开发过程中确实有时间和精力去反复地评审这个程序,那么这种方式就是可行的。
除了上述三种情况,还有一种情况也适合做专项设计,那就是你在开始做项目的时候,并不知道它最终会做成什么样子。实际上这相当于通过尝试来进行设计。笔者在本书中要提到一个自己所构建的范例程序,该程序会根据数据来绘制一些图表。我之所以用专项设计的方式来构建这个程序,一部分原因在于本程序的利益相关者就是我自己,还有一部分原因则是当初我并不清楚这个产品的最终样貌。
要想采用专项设计的方式来做项目,你最好一开始就有心理准备:项目过程中有可能要把已经做好的一部分内容丢掉,因为在开发过程中可能会碰到障碍,或是会发现更好的解决方案。笔者在编写那个绘图程序时,曾经放弃了20%已经写好并测试好的代码。艺术工作也可以视为一种设计,它通常是采用专项设计的方式来完成的。有些艺术家之所以优秀,是因为他们敢于把不够好的作品丢掉重做。更进一步地说,即便你要面对的是一个相当复杂而且有挑战性的问题,也依然可以采用专项设计的方式去制定解决方案,但是你必须做好准备:项目在最终完成之前,有可能要丢弃大量的内容。也就是说,你可能已经写完了一些代码,但是却发现还有更好的解决办法,于是要把早前的代码再写一遍。
我们经常听到有人说:软件产品的第二版或第三版,要比最初那一版好得多。没错,如果已经有一个系统摆在那里,那我们自然可以更好地理解其各个部分之间的关系,而且也可以更容易地指派多位设计者去协调处理系统中的不同部分。第二版或第三版之所以会比第一版好,一部分原因在于公司的营销策略,因为公司想尽快推出一个能用的版本来打入市场,这个版本可能做得有点草率。(尽管如此,笔者从来没有发现哪个优秀的程序员会用草率的态度来写代码,即便在时间比较紧的情况下,也不应该这样做,因为这通常会对开发工作造成不利的影响。)然而笔者却认为,出现这种情况的主要原因,还在于开发者面对第二版或第三版的时候,更愿意抛弃旧版本中的代码,而他们在制作第一版的时候,则不太愿意把自己编好的代码丢掉。
想用专项设计的方式来做大型项目,其中一个办法是把项目分成很多小的版本来发布。复杂的设计方案会随着这些小版本的发布而逐渐浮现出来,等到它完全出现在我们面前的时候,实现团队中的每一位成员就都应该能够理解这套设计方案了。可是这里面有一个奇怪的问题:为什么我们能够在心中理解一个庞大而复杂的软件,却没办法用大家都能看懂的方式把它落在纸面上呢?本书中有很多内容都会谈到怎样解决这个问题。
1.2.2 有计划的设计
如果你采用有计划的设计(planned design)方式来做项目,那么你会用图纸把自己想要设计的产品绘制出来,或是用文字将其描述出来。我们今天能看到的很多优秀的历史建筑,都是有计划地设计出来的,建筑师会把建筑的楼面和外墙用图纸绘制出来,并指出其材质或需要使用的技术。笔者觉得欧洲很多雄伟的哥特式教堂,都应该是采用这种有计划的设计方式设计出来的,否则你很难想象建筑者怎么可能把它建得这么好,尤其是要把一块石头刻得恰到好处,使其刚好能够放到花饰窗格之中的特定位置上面。不过,这些哥特式教堂的建造者在建造柱子的时候,使用了一些经验法则来决定其厚度,而且他们还可以依赖一些工匠,这些工匠知道应该怎样把图纸上的东西建造出来。
如果你确实可以调动一些优秀的手艺人来完成项目,那么这种有计划的设计方式,就可以体现出一个极大的优势,也就是说,我们不需要再像专项的设计方式那样,整天盯着他们干活,因为他们完全可以根据图纸把所要制作的内容构建出来。
在构建复杂的项目时,我们可以看出设计方案的层次感。例如,要设计一座中世纪风格的教堂,那么最宏观的设计应该是教堂的基本形状,这需要我们确定柱子和墙面的位置,但目前,我们还不需要考虑柱子或屋顶的内部结构,因为这些内部结构属于更为详细的设计,需要留给其他人去做,或者说,更有可能是留给工匠去做,这些工匠可以按照几百年来一直流传的设计风格把这些内容构建出来。此外,可能还需要有人来设计石雕窗格与彩色玻璃,并规划圣坛与唱诗班座位的布局。
有计划的设计方式是一种较为保守的方式,因为它必须依赖于手艺人所具备的知识和技能体系,如果要脱离这套熟悉的做法,那就需要培养一套新的知识与技能体系。
IT界经常采用这种有计划的设计方式来做项目,尤其是会用它来做大型的项目。笔者觉得,这种有计划的设计方式,确实在应用程序的设计方面取得了一定的成功。
1.2.3 工程化的设计
工程化的设计与有计划的设计很相似,它们都需要表示成图纸或文本,然而两者的区别在于:工程化的设计是一种经过测试的设计。我们早前讲过一套带有4个步骤的设计流程,分析环节是其中的一个步骤。在本节所讲的这三种设计方式里面,只有工程化的设计方式才会包含正规的分析环节,然而这并不是说专项的设计方式和有计划的设计方式就绝对不做分析。笔者觉得,优秀的设计者通常会在心里做一些简单的分析,例如,他们可能会构想一下本产品将会在哪些场景中使用。笔者甚至觉得,中世纪那些哥特式教堂的设计者,应该花了很长的时间去思考教堂的使用情况,甚至有可能想到应该怎样留出一定的灵活空间,以方便信徒们列队行进并举办仪式。令人遗憾的是,教堂的建筑者并不懂得去做一些力学和承重方面的计算,有很多教堂的钟塔,在建造后不久就坍塌了。工程化的设计与前两种设计的不同点正在于它会进行正规的分析,并且会用一套知识体系来确定合适的分析方式。现代的建筑工程师都会计算建筑物的承重能力,只要建筑物建造正确,他就可以确信:该建筑在结构上是稳固的。除了纯粹的计算,我们还可以用其他一些形式来进行分析。例如,可以通过计算机建模来分析建筑物中的人在火灾发生时的逃生时间,或是通过风洞来分析汽车设计方案的防风能力。
除了那种非常简单的东西,工程化的设计一般都会体现出设计的层次感。比如,要设计一种新的喷气式客机,那么最顶层的设计,就应该是从各组件的组合情况着眼来描述飞机的形状。其中的每个组件都有各自的小设计,而且像发动机这种复杂的组件,还会分成很多子组件,每个子组件都具备各自的设计。图1-2抽象地展示了这种层次化的设计。
图中的每个小方块表示一个组件,包围着小方块的每个大方块表示一种设计。最顶层的设计图,展示了飞机的机身、机翼、发动机、起落架等部件的拼接情况,而发动机的设计图,则展示了发动机里面各个小部件之间的拼接情况。
对于组件的设计来说,其需求可以分为两类,一类是宏观的设计方案对该组件所提出的需求,另一类是外界对该组件所提出的需求。比如,在飞机的发动机所应满足的各项需求之中,发动机的功率以及重量和大小方面的限制来自宏观的设计方案,而噪声限制及可服务性则来自外界。图1-3演示了这两种需求。
在工程化的设计之中,每个组件的设计都是经过测试的。
工程计算中有两个很重要的地方。首先,这种计算要算的是组件能不能满足其需求。比如,飞机设计者会通过计算来判断这架飞机是否能够飞行。要想完成这样的计算,就必须先知道发动机的重量以及它的输出功率。在分析组件的过程中所用到的每一个因素,都必须由组件设计中的一项需求来支撑。如果发动机所输出的功率无法应对预定的重量,那么宏观设计就必须重做。换句话说,组件设计与宏观设计之间会形成反馈回路,如图1-4所示。
https://yqfile.alicdn.com/558444cac847c073819028a17a4c797d85dd3f82.png" >
第二个要注意的地方是,工程计算并不能证明产品一定成功,它们只能保证产品在一系列预先定义的情景之中不会失败。以建筑为例,结构工程师可以算出建筑的承重能力,确保人们在走进建筑物或是在其中添设家具之后,该建筑不会坍塌。但是如果想保证建筑物可以抵抗强风,那么还需要再做另外一些计算。建筑物的抗震和抗洪能力,也需要通过其他一些计算来了解。换句话说,工程计算只能证明产品可以应对当前所涉及的这个问题,而并不能证明它可以应对尚未指出的那些问题。之所以要说明这一点,是因为很多IT人士都追求一种境界,他们想要设法证明程序是正确的。工程师想要证明的,并不是设计方案的正确性,而是该方案可以应对一系列预先想到的问题。因此,我们在验证程序的时候,应该更像工程师,而不是更像数学家。
我们要在出问题之前就预料到某些问题,而不是等出了问题之后再去修复,1940年的塔科马海峡吊桥垮塌事件[2]说明了这一点。当时有一阵中等强度的风引发了共振,使桥开始摇摆。这种共振,把小幅度的上下运动变成大幅度的上下运动,并且令桥面扭曲,最终使其崩塌。当代的工程师已经能够理解这种共振现象,他们可以通过计算来确保建筑物的结构不会因此而受损。但有时还是会出错,如伦敦的千禧桥,这座桥也出现了共振,但不是由风引发,而是由过桥的行人所引发。该桥在开放后不久,就因为要解决这个问题而关闭。
我们总是会发现,有的时候必须重新做设计,才能把某个问题解决好,而且有的时候,要想重新设计某个组件,就必须重新设计与之相关的其他一些组件,甚至需要重新设计整个产品。这两种风险,在有计划的设计方式中表现得会比工程化的设计方式更为严重,因为工程化的设计可以通过分析来尽早发现问题。
https://yqfile.alicdn.com/82db240ec48b31928f9ed5bf5ceaa9275ee115bc.png" >
1.2.4 设计方法小结
前面几个小节讲了很多概念,这里有必要回顾一下:
设计可以看成一套含有4个步骤的流程,这4个步骤分别是:理解、猜想、细化和分析。
复杂的设计可以分成很多个组件,这些层次不同的组件可以分别进行设计。
设计方式可以分成三大类:
- 专项的设计。这种设计没有正规的形式,它完全是在设计者的头脑中构想出来的。该方式适用于规模较小而且理解较为充分的产品,此外,如果某产品的设计方案已经做出来了,但是设计者现在又想重新设计产品中的大部分组件,那么也可以用这种设计方式来进行探索。
- 有计划的设计。这种设计方式具有正规的形式,设计者不用一直督导其他人来完成工作。任何规模的项目(甚至是那种很大的项目,如金字塔)都可以用这种方式来做。该方式不包含正规的分析环节,因此产品的表现要依靠设计者的直觉。
- 工程化的设计。它与有计划的设计方式很像,但是还包含一个正规的分析环节。如果产品较为复杂,有较多的内部依赖关系,或是其表现很难预测,那么我们就应该用工程化的设计方式来做这个项目。
为了解说后面两节中的观点,笔者绘制了图1-5来演示工程化的设计。
我们能不能像工程学那样来开发应用程序呢?如果可以,那是不是应该这么做?