软件设计实践
本文首发于个人微信公众号《andyqian》, 期待你的关注!
前言
拿破仑曾经说过:“不想当将军的士兵不是好士兵”。而在软件行业中,几乎每个程序员心里都有一个成长为架构师的梦想。程序员与架构师之间,不仅仅是角色的转变,更多的是知识的积累,以及架构,设计能力的转变。最近有幸参与了一些设计工作,系统算不上复杂,庞大。但在这过程中将自己的不足暴露的淋漓尽致,如同裸奔,有着凉飕飕的感觉。记录这篇文章,算是一次小结,同样也给自己之后系统设计时留下一丝指引。
愿景
愿景二字其字面意思是:希望看到的场景。这一词在潘加宇老师的《软件方法》中解释为:我们希望新系统 / 服务 是怎样的 ?也就是为以下几点:
-
我们为谁编写系统?也就是寻找到业务 “老大”,可以理解为解决XX的问题?这个 XX 就是老大。
-
我们编写该系统的目的是什么?
-
希望通过它能够解决什么问题?
-
没有该系统前,业务是怎样运行的?(现状)
-
有该系统后,业务会发生怎么样的变化 ?(改进后)
在愿景中,个人建议添加适量的可量化值,以及图(交互图)等更直观的表达体来体现两者之间的差异。例如:
-
改进前:TPS 是 1000,改进后:TPS 是 2000 等。
-
改进前:对接新产品需要 40天, 改进后:对接新产品只需要 20 天。
业务用例
技术人员一直有个不愿承认的事实是:技术是为业务服务的,而技术是一种业务实现的手段/工具。在这样的前提下,我们在做新系统设计时,优先需要理清业务,先做业务分析设计。在进行业务分析设计时,我们以组织为研究对象。组织提供的价值称之为业务用例,组织外部与系统交互的人或组织称之为业务执行者,组织内的人称之为业务工人,组织内非人系统称之为业务实体。
进行业务设计分析时,常用工具有:用例图, 时序图。
-
用例图:
其表达的核心价值是:业务组织向业务执行者提供哪些价值,而这些价值通常用业务用例表示。在用例图中用到的元素有:业务执行者,组织,业务用例。例如:腾讯这个组织,向微信支付用户,提供小程序支付这个价值的业务用例。如下图所示:
图(1) 业务用例图
-
时序图:
在UML中,时序图是动态的交互图。其表达的核心价值是:业务执行者,业务实体,业务工人之间在完成该业务时的交互。如下图所示:
图(2) 业务时序图,来源于微信支付官方
其中:
-
业务执行者:微信支付用户
-
业务实体:商户系统,微信后台, 微信小程序
-
在上图中,并没有业务工人。
系统用例
上面介绍了业务用例,接下来就是系统用例。上面说到,业务用例是以组织为研究对象,对外提供的价值称之为业务用例。而系统则是以系统为研究对象,与系统交互的人或系统称之为系统执行者,系统提供的价值单元称之为系统用例。在系统用例中,比较常用的工具有:用例图,时序图。
以上面例子所述,系统用例图,如下图所示:
图(3) 系统用例图
解释:微信支付系统向微信支付商户提供:统一下单,订单查询,订单关闭,查询退款,下载对账单 等等系统价值,而该价值称之为系统用例。
其中:
-
系统:微信支付系统就是我们研究的对象。
-
系统执行者:微信支付商户。
-
系统用例:统一下单,订单查询,订单关闭,查询退款,申请退款,下载对账单 等称之为系统用例。
如果说:每个系统用例是系统提供的价值封装。而系统时序图:则是系统在完成该价值时的内部交交互。
包图
包图是最容易忽略的,恰恰它在项目中发挥着极其重要的作用。倘若一个系统没有使用包进行归类,其可维护,可扩展性是不可想象的。例如:我们熟悉的 java.util 下的简易包图,如下所示:
图(4) java.util 包图
仔细看看上面的包图,其实不难发现,每个包做的事情都是原子的,如同设计模式中的单一职责一样。我们在使用包图时,也要遵循这样的设计思想。
包的分类:
-
项目中按照模块进行划分,每个模块就是一个包。
-
在模块内部,则按照职责进行划分,相同职责的类放在同一个包中。
-
当然,在使用包图时,我们还需要注意包与包之间的关系。
(在这里就不展开了,请自行查阅文档)
类图
类图是UML中常用的静态结构图,在系统设计时,我们需要用类图来表达类的构成,以及类与类之间的关系。
如下所示:
图(5) ArrayList 类图
在UML中类图由以下几点构成:类名,类属性,类方法。
常用的类与类之间的关系有:
-
实现:
-
继承(在UML工具中用:
Inheritance 或 Generalization 表示)
-
关联
-
聚合
-
组合
类图对编码工作有着非常大的指导意义,在前期捋清楚类与类之间的关系,编码时,就可以按照设计进行编码。
状态设计
在核心对象的状态变化,需要设计清楚。
-
该对象有哪些状态?
-
状态的转换条件是怎样的?
在UML中状态设计中常用的工具有:状态图。
例如:订单有:未支付,支付中,支付完成,支付失败 等状态。
-
用户下单完成,但未支付时为未支付状态。
-
用户正在支付时,从未支付状态转换为支付中状态。
-
用户支付完成后,从支付中转换为支付完成状态。
-
由于系统原因或其他原因,从支付中转换为支付失败状态。
关键算法
在进行系统设计时,如有关键算法,也是需要在设计文档中体现的,并需要举例说明。
数据结构设计
数据结构设计包括以下几点:
-
使用关系型数据库,还是非关系型数据库?(具体到某个实际的组件,例如:MySQL)。
-
使用什么存储引擎?是Innodb还是MyISAM ?
-
使用什么字符集,是 utf8 还是 utf8mb4 ?
例如:
表名:t_user
存储引擎:Innodb
字符集:utf8
图(6) 表结构设计
在数据结构设计中,需要注意的是:每个表结构中的公共字段名,类型统一。例如:创建时间,修改时间,逻辑删除标识,主键等等。
部署图
部署图,表达的是部署结构,也就是说系统部署态是怎样的。如:
-
部署到云服务商,还是自建机房?
-
从用户请求到服务中,执行全过程是怎样的?
非功能性设计
在非功能性设计时,需要考虑的点有以下几点:
-
监控体系 :监控体系通常包括 系统监控,业务监控。
-
性能指标 : 例如 TPS 等等。
无论是监控体系,还是性能指标,其实都是相辅相成的。一来能够知道系统在运行态的情况,二来可以根据监控,性能指标数据来辅助系统的迭代,完善。
陷阱列表
在做系统设计时,时常会遇到一些”陷阱”。之所以称之为”陷阱”,因为这些点足够小,在不经意间就会入坑。例如:
-
业务分析用例时,掺杂着系统用例。
-
缺少关键状态变化设计。例如:订单状态由未支付到支付中,再到支付完成。这些状态由谁触发?在什么条件下触发?
-
数据结构设计时,公共字段属性不统一,例如:A 表的创建时间使用 created_at, B 表的创建时间为 created_time,A 表的主键是自增,B 表的主键又是另外一种等等。
-
缺少监控以及性能监控。
…
一份好的设计文档,对编码工作有着非常大的指导意义,既能提高软件质量,也能提供交付效率。在这方面,我自己也是非常薄弱,在此将自己的一点经验和感想记录成文,希望对之后的系统设计能有所启发!
参考文献:
1. 潘加宇 《软件方法(上) 业务建模和需求》
相关阅读: