摘要:

十几年前刚毕业不久,我从事第一份软件开发的工作,要完成一个项目,但没有任何软件设计的思路,于是请教我的老板。我的老板给了我两种思路:1)先假设软件已经做出来了,想好软件的外在表现,由此倒推软件的实现方法;2)思考程序的数据结构,先设计数据库,然后再搭建软件的上层建筑。老板给了我很大的启发,随着工作的开展,后来我又发现了第3种设计的思路。本文将为你分享三种软件设计的思路:1)由顶而下;2)由底而上;3)由中间到上下。


大纲:

1.什么是优秀的设计?
2.优秀的设计能节省项目工作量
3.优秀设计从分析需求开始
4.软件系统不是木桶型的
5.软件设计的“大道理”
6.规划系统骨架——架构设计
7.打造系统的底蕴——数据库设计
8.细节决定成败——详细设计
9.用户感觉好才是真的好——用户体验设计
10.持续提升设计水平


本文章是系列文章之一,如果你还没有看过之前的文章,建议先看完前面的文章再看本篇,这样效果更好。



5.软件设计的“大道理”



5.1 我的第一次商业软件的设计经验

1999年刚毕业不久,我从事第一份软件开发工作,当时要负责一个大型桌面软件,但不知道应该如何开展软件设计工作,于是向老板请教。老板也仅仅是年长我几岁,不过公司的核心产品是老板开发的,老板说他其实也没有什么系统的方法,不过有两种思路供我参考:
1)先假设软件已经做出来了,想好软件的外在表现,由此倒推软件的实现方法;
2)思考程序的数据结构,先设计数据库,然后再搭建软件的上层建筑。
上述的两种软件设计思路,相信很多有软件设计经验的朋友都能体会到。后来我又体会到第三种的设计思路,后文将会为你分享我对这三种设计思路的一些体会。


5.2 N层架构是怎么回事?

这三种设计思路都与软件系统的N层架构有关系,我们以常见的四层架构为例子,请看图:


图5.1 四层架构

这个是UML的包图(Package Diagram),图中好像文件夹的那个东西就是”,包与包之间的虚线箭头表示的是依赖关系。
上图表示的意思如下:
1)四层架构的四层,分别是指:表现层、逻辑层、数据访问层和数据层;
2)表现层依赖于逻辑层,逻辑层依赖于数据访问层,数据访问层依赖于数据层。

依赖是什么意思呢?
依赖”可以是以下情况之一:
1)A需要调用B的方法,则A依赖于B;
2)A的方法中某些参数的类型是B,则A依赖于B;
3)A的某些方法的返回值类型时B,则A依赖于B。

这样我们大概了解了四层架构是怎么回事了,但我们还会有以下问题:
1)表现层如何将数据传递给逻辑层?
2)逻辑层如何将数据传递给数据访问层?
3)数据访问层如何将数据传递给数据库?

通常我们不会这么老土通过一大堆参数来传递层与层之间的数据,通常我们会将数据装在实体类中,通过实体类来传递数据。所以图5.1可以进一步表示为图5.2:

图5.2 四层架构与实体类

补充说明一下什么实体类?
实体类通常是一个只有属性没有方法的类,通常我们会将某一业务对象的数据装在一个实体类中。例如:某请假单实体类,该类可能有请假人姓名、请假起止时间、请假类别和请假事由等属性。


5.3 “由顶而下”的设计思路

看了图5.1和图5.2,你大概就清楚了什么是软件的“顶”?什么是软件的“ 底”?“顶”就是表现层,“底”就是数据层。那么“由顶而下”的设计思路,其实就是先想清楚软件的表现层,然后再思考逻辑层、数据访问层、数据层的实现。
前文我们提到要“需求驱动设计”,这个说法有点笼统,我们需要进一步思考:“什么需求”驱动“什么设计”?
请看下图:

图5.3 由顶而下的设计思路

这是UML的活动图,横线将图分成了上下两部分,上部分是需求分析,下部分回答了“什么需求”驱动“什么设计”的问题。
说明一下:“需求驱动”及横线不是UML图的标准语法,图加上这些非UML元素是为了更好地表达问题。
“需求分析”这个活动有三种工作产品,分别是:
1)用例/用户故事;
2)业务流程图;
3)业务概念图。
你可以理解为上述三种工作产品是“需求分析”这个活动的“输出”。
“用例/用户故事”和“业务流程图”是“规划界面”这个活动的“输入”;类似,“业务流程图”和“业务概念图”是“设计逻辑层”这个活动的“输入”。其他就不再多解释了,你应该可以看懂这个图了,后续几个图的语法类似,也不再解释了。

上述是对图5.3语法及表达意思的基本解释,这里再稍微小结一下:
1)分析需求是设计的开始,我们还需要将需求至少分解为三部分:软件要满足的功能(用例/用户故事)、业务流程(业务流程图)、业务概念(业务概念图)三部分;
2)设计不同的层时,主要依赖的需求是不太一样的,上述分解的三部分需求对不同的层设计提出了不同的要求。比方说:设计数据库时主要是根据业务概念来设计的,规划界面时主要根据软件需要满足的功能点,还有业务流程来设计。


5.4 “由底而上”的设计思路

经过前面的铺垫后,这个“由底而上”的设计思路,相信你一看图就可以懂了。

图5.4:由底而上的设计思路

这个图也分成了上下两部分,上部分的内容其实和图5.3是一样的,只是左右顺序不太一样而已。


5.5 “由中间到上下”的设计思路

这种设计思路是我从事软件研发工作若干年后才认识的,当时是因为项目出现了特殊状况,为了应对这样的状况而采取的一种设计方法。

案例分享:客户要改SQLServer为Oracle
签订合同时,我们和客户约定的项目技术架构是.net+SQLServer,当时客户没有反对,我们就按这样的技术架构完成了系统,并且部署上线。但是不久客户居然提出了这样的要求:要求我们使用Oracle数据库,而不能用SQLServer数据库!我们通常是按照“由底而上”的思路来设计软件的,如果数据库要更换,基本上整个软件就等于重做!
如果你遇到这样的状况,你会怎么办呢?能不能按需求变更来处理呢?只有客户愿意付钱,我们就愿意干!但客户愿意付钱吗?这可是要付推翻重做的钱啊!!

最后我们的领导决定免费重做,领导决定免费重做的原因是:
这是公司的一个核心项目,我们期望这个项目将来能产品化,能持续赚钱。但我们技术选型主要是根据我们当前的技术情况来决定的,没有充分考虑客户的情况。客户是某重要行业的企业单位,单位体制内的所有企业基本都是用Oracle的,但我们选择“视而不见”,选择了我们最熟悉的SQLServer来开发系统,其实迟早是要遇到问题的。客户除了用我们的系统,还会用其他更大型的更重要的系统,客户的其他系统基本上都是使用Oracle数据库的。所以如果我们要在这个客户领域打开市场,将项目做好,就有必要将系统改造为Oracle数据库。

但是我们已经有部分客户使用了我们的基于SQLServer的系统了,将来也有可能会有部分客户要求用SQLServer,所以我们领导决心改造软件的架构,要让我们的软件可以支持SQLServer,也可以支持Oracle!于是我们按照“由中间到上下”的思路,重新打造了软件架构,请看下图:

图5.5 由中间到上下的设计思路

这个图也分成了上下两部分,上面部分和前面的图内容也是一样的,但下面部分就很不一样了,而且可能比较难理解。
“由中间到上下”基本的思路是这样的:
1)先不考虑表现层,也不考虑数据层;
2)先定义实体类和数据层接口;
3)接口定义好后,往上可以设计逻辑层和表现层,往下可以设计数据层接口的实现和设计数据库。

按照这样的设计思路做出来的软件架构,应该是这样的:

图5.6 由中间到上下的系统架构

图中见到数据操作层接口有两种不同的数据库实现,分别是SQLServer和Oracle,如果要考虑第三种数据库,那么再增加一个实现就搞定了,而系统的上层建筑(表现层、逻辑层)不需要改变。

这样的设计方式看上去很酷,是不是应该所有系统都要考虑用这样的方式来打造呢?
不是滴,这样的设计方式是有缺点的:
1)系统将不能充分利用数据库的特性,一般会禁止在数据库中写存储过程、触发器、甚至是视图等,程序的的性能其实会降低;
2)因为不能充分利用数据库本身的特性,所以大部分甚至是全部的业务逻辑只能靠程序搞定,这样其实增加了程序的复杂度和工作量。

所以每种设计方法都是有针对性的,都很难做到十全十美,一般只能针对主要矛盾做出一些取舍。


5.6 小结

如果系统没有多数据库的要求,我会比较建议你用“由顶而下+由底而上”的设计思路;如果程序需要支持多数据库,那么可能考虑“由中间到上下”。上面介绍的三种设计思路,其实在实际工作中我们往往不会只选其一,往往是结合了多种思路的。不要局限自己的思路,软件设计的可能性是无穷的。




本文是系列文章的第4篇,要做软件设计师一点都不简单啊,请留意后续文章!



作者:张传波

创新工场创业课堂(敏捷课程)讲师

软件研发管理资深顾问

CMMI首席专家

《火球——UML大战需求分析》作者

www.umlonline.org创办人