如何开始软件设计 -- 浅议软件设计中的方法论

前言

当我们开始构建一个全新的系统时,常常是又喜又愁:喜的是这下终于有机会大展身手,创造自己的作品,不用再去做修修补补的事情;愁的是万事开头难,千头万绪,不知从何着手。诸君请先不要发愁,听我排解排解。

解决具体问题的方法虽各不相同,但获得问题解决方法的方法却是相通的,探求方法之方法便是哲学上所谓的“方法论”。方法论如同远方高耸的灯塔,指引着航船找到通向港口的航线。软件开发是一项创造性活动,一个产品在开发中的各个阶段,需要我们逐步的去认识问题,其间存在大量的抉择和取舍,需要可观的创造力与判断力。这些认识活动,包括需求分析、业务建模、关键技术调研、系统架构设计等,当我们将问题的方方面面有个比较清晰的认识后,解决方法自然是水到渠成、跃然纸上。那么有哪些方法可以指导我们认识软件开发过程中的种种问题,以便找到可行的解决方案,顺利完成代码构建呢?

从本质特性出发

亚里士多德最早系统性研究事物类属,他提出本质主义的哲学观点:事物属性可分为本质属性(essential)和偶然属性(accidental),本质属性是事物固有的属性,它不会因为外部环境的改变而变化,固有属性决定事物的类属,具备相同固有属性的个体是同一类事物;偶然属性是事物碰巧具有的属性,它可能会改变或者消失,它不影响事物的类属。亚里士多德以人举例:譬如人类基本上是理性的—具备理性思维是人类的本质属性,人有两条腿是偶然,实际上某个人失去一条腿后,他还是属于人类,但如果没有理智,或许会被当作野兽或者其它物种。哲学家对人类的定义可能过于严格,我们再看一个现代点的例子,譬如汽车,必须具备引擎、车轮、车门等必要组成部分,缺乏任何一样,它便不是真正意义上可用的汽车,但无论车门颜色、轮子大小等等这细节怎样,它还是一辆车。这里拥有引擎、车轮、车门是汽车本质属性,车门颜色、车轮大小是偶然属性。偶然属性附属的、任意的、非必须的。

同样,我们也应基于目标软件本质属性开始设计。那么什么是软件的本质属性呢?一个原则,不可或缺的功能特性。正如程序的定义是数据结构+算法,软件的本质属性也主要从业务数据与业务流程两方面去分析,这些特性包括:

  1. 核心数据的组成、形式与规模;
  2. 核心业务流程执行步骤与并发规模;

通过对业务本质特性的分析,我们便可以进行软件层面的概要设计,所谓概要设计,便是在软件层面对业务建立概要模型。这包括:

  1. 数据建模:建立业务数据在计算机世界的表达方式,并识别各类数据的访问特性(读写特性,访问频率等);
  2. 模块分解:将数据操作以及业务流程划分到多个物理模块中,模块是业务逻辑承载的实体,模块化是将复杂问题分而治之的基本手段;
  3. 接口设计:模块间交互的方式,以协调各模块职能,共同完成业务流程的执行;
  4. 运行时设计:运行时状态定义,标识数据访问与业务流程的并发特性;

以上均是对业务、软件的本质特性进行分析,并不涉及编程语言、数据库、第三方组件等实现层面的问题,这些属于偶然属性的范畴,不管具体的实现方案如何,系统的本质特性还是那些。我们应该根据项目的本质特性和开发团队的实际情况,选择合适的解决方案。

在实现层面,必然会引入一定的偶然属性。软件本质特性所蕴含的复杂性称之为本质复杂性,这是实现软件所必须克服的。其它由偶然属性引入复杂性称之为偶然复杂性,随着软件产业的发展,偶然复杂性已经大为降低,譬如操作系统、编译器已经非常稳定可靠,有更丰富的编程语言,更多高质量的组件和服务软件可以选择。但我们仍然需要非常小心,避免引入不可控的偶然复杂性,譬如引入过于庞大的框架组件,可能带来昂贵的学习与维护成本,甚至可能会因框架本身的缺陷可能会制约软件的发展,那就更是得不偿失了。能否有效控制复杂性,可以说是能否保障软件顺利完成开发的首要问题,Steve McConnell 在其经典著作《代码大全》中非常详细透彻的探讨了这个问题,非常值得一读。

小结

分析系统,首先是识别业务本质特性(数据,流程),进而在软件层面构建概要模型。在确定具体的编程方案时,应聚焦于本质特性,控制好软件构建的复杂性,避免由具体解决方案所引入的偶然复杂性失控。

开发原型代码

在项目开始,可能会出现两类相反的极端情况:

  1. 未经认真考虑,便开始埋头编码;
  2. 期望构思出一个终极方案,总是对现有的设计不满意,迟迟启动不了编码;

第一种是鲁莽,第二种则过于谨慎,都不利于我们建立对手头项目的正确认识,不利于开个好头。在社会学中,有“邪恶问题(Wicked Problem)”的概念,有先贤指出软件设计也是一个“邪恶问题”,所谓“邪恶问题”是这样的问题:即只有已经解决一遍或者部分解决之后,才能真正认识清楚的问题。这确实一针见血的说明了软件设计的特点:不论开始我们对系统有多么周全的考虑,但在实际开发过程中,以及后续系统运行过程中,总是会出现我们不曾预料到的问题,如果我们的前期设计缺乏弹性,这些问题可能还会彻底推翻现有的设计方案。盲目开始编程,因为对问题缺乏必要的认识或者根本就是搞错了问题,终归会使项目陷入困顿,浪费人力和时间。探求终极方案,因为始终未曾去解决或者部分解决问题,很难获得真知,这样的探求无异于缘木求鱼,难有成效。这就像是困在两堆干草之间的比亚当之驴,不先去吃一口,怎么知道不能吃呢?突破困局的办法是开发原型代码,这些代码不会作为正式的产品代码之用,我们可以只聚焦于问题核心本身,省却枝蔓,用最小的代价先让部分逻辑实际运行起来,无论如何推理想象,也不可能有实际所见让人对事物的认识更深刻。这些原型程序可能会否定我们的设计,我们不得不重新探寻新的方案,但这只是付出了必须付出的代价,远远好于因为问题发现太晚而导致项目返工,有些时候,可能连返工的机会也没有。建立原型在科研和工程实践中普遍方法,想想看物理化学的理论发现怎能离开实验,每一款新车在设计的过程中都要制作各种模型车,每一栋大厦在破土动工前也需要制作模型,验证设计方案抗风、抗震能力。可能因为代码推到重来的成本是无形的,大家因而忽视了模型的作用。

小结

在构思解决方案的同时编写必要的原型程序,让部分业务逻辑尽早运行起来,可以让我们更深刻的认识问题,以便设计出真正可行的解决方案。系统设计如此重要,它直接解决定了系统能否实现以及付出的代价,方案合理,便成功了一多半。套用Alan Kay的名言:预测未来最好的方式是创造它,创造完美设计的最好方式是部分实现它。

以发展的眼光看问题

往往想一次性就设计出一个“完美的”系统,足够灵活、足够强大,可以支持今后可能才会用到的各种特性,可以支持业务规模的任何扩展。 这往往是徒劳的,对当下有碍,对今后也不一定有实际的用处,因为:

  1. 认识不准。千万、上亿级别用户规模与数万、数十万用户规模对技术的挑战可能完全不同,虽然也有很多业界的经验可以借鉴,但只有真实经历了系统规模增长的过程,对这种差异的理解才会比较准确。另外,未来产品特性的发展也不可能准确预计,当前付出的代价不一定有收益,甚至会妨碍今后需要的特性开发;
  2. 力量不够。一般来讲,在业务初始阶段,不会有太多人力物力的投入,开发团队面临尽早让产品上线的压力,以便更好捕捉用户需求、验证产品模型。应该以尽量简单的方式实现产品,而要支持更大用户规模,所面临的问题当然会更复杂,需要的投入会更多,与当下的目标冲突。

我们应该以发展的眼光看问题,当下的设计主要为满足当下真实的产品需求,不要过份预设今后的功能需求,以免增加不必要的负担。真正可伸缩可扩展的系统设计,不在于当下预设了多了功能特性,而在于系统要有演进发展的潜力,这赖于结构清晰的数据模型、职责分明的模块定义,系统耦合适度。所谓发展眼光看问题,就是要根据业务的发展来演进软件,而软件的演进又能支持业务发展到更大的规模。《程序员》杂志2013年6月刊的一篇文章:“从一扩展到无穷大:“架构艺术与工程实践的华尔兹 ——读 ‘Scaling Memcache at Facebook’有感”,对Facebook为应对业务规模的快速增长、从小到大建设Cache 系统的工程实践进行了剖析,很好的诠释了系统如何伴随业务的发展而自然地演进,看后相信会对大家有所启示。

小结

在设计系统时,我们应该以发展的眼光看问题,立足业务本质特性与于当前需求,定义客观合理的开发目标。对整体系统而言,绝不可存一劳永逸之幻想,好比给小孩买大码的鞋穿,貌似可以为今后省钱,但却不能让小孩现在穿着好好走路。

后记

一年之计在于春,一日之计在于晨,因为春天、早晨是规划的时节,规划得当,我们才能行之有效的去做事情。同样,项目开头的时间也是非常关键的,这时对系统本质特性认识是否准确,设计方案是否合理,决定了后面我们还能否愉快的编程,决定了产品最终可以达到质量水准的上限,以及实现成本。方法论是强大的武器,它能让我们形成正确的分析问题的思维模式,以不变应万变。面对新的项目,我们应该根据系统本质特性进行设计,管理好项目复杂性,避免偶然复杂性失控;可以用快速开发的原型程序来帮助认识问题、验证方案;同时应该发展的看问题、阶段性的去解决问题。如果您刚好要着手新系统的开发,刚好在起步的时候也有些踯躅,希望这篇文章能对您有所帮助。

参考文献

  1. Steve McConnell 著,金戈等译。《代码大全2》中文版。P73-80。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据猿

机械公敌?一场乌龙,Facebook的AI“失控”只是程序存在bug

为何Facebook AI会出现上述的对话?究其原因才发现,在实验期间,研究人员将机器人设置为“用英文沟通”,而非“用英文文法沟通”,实属程序bug,这一错误导...

37960
来自专栏CSDN技术头条

D语言架构师Andrei Alexandrescu谈D、Go、Rust取代C/C++

【编者按】本文是D语言联合创始人、架构师Andrei Alexandrescu在问答Quora上关于“在取代C语言的道路上,D、Go和Rust谁的前途最光明?为...

34670
来自专栏行者悟空

项目进度估算难题

19220
来自专栏AI启蒙研究院

【机器学习】python凭什么能被纳入教材?

15130
来自专栏SDNLAB

NFV主导向开放平台的演进

近日,有调查显示网络功能虚拟化(NFV)和软件定义网络(SDN)平台是电信和网络设备供应商关注的重点,该调查同时表明网络正在向开放平台和开放的软件体系结构转变。...

34680
来自专栏大数据文摘

“开源”创造者为你论述这一术语的前世今生

13740
来自专栏程序员的知识天地

专科程序员含泪谈及与本科程序员的差距!五年心酸历程,悔不当初

坦白来讲在实际项目开发过程中,谁会关心你是专科还是本科,对于企业来讲谁的水平高就用谁,技术领域对于学历区分不是很明显,当然这只是针对私企来讲,国有企业的程序员可...

2.1K10
来自专栏全栈工程师成长之路

程序员最佳学习方法(干货总结)

20570
来自专栏企鹅号快讯

一个19岁萝莉程序媛的内功心法

Lydia才19岁,但她绝对是那种“毕业两年,五年工作经验”的类型。年纪轻轻,却有数年的导师经历。她的内功心法都是非常实用的干货,不鸡汤,不矫揉造作。 软件门外...

23260
来自专栏前沿技墅

一个图书策划的自我修养之一:这个产品不寻常

17660

扫码关注云+社区

领取腾讯云代金券