读书笔记(六十) 如何做软件分析1-建模

已发布在微信公众号上,点击跳转

背景:

从入行以来自己做了很多项目,也分析了很多项目的程序架构。这次我希望能够更系统性的学习如何做架构分析和软件系统分析。

我本身对游戏项目中的业务架构、引擎架构、操作系统架构都有所了解,10多年来从业务架构一步步的深入探索到达了较为底层的架构。最近我突然有感触,自己在分析、解剖这些架构时发现,自己的速度慢效率低,所以我想学习一下,看看业内大佬是如何做架构设计、架构分析、系统解剖的。

找了3本书看《Thinking in UML》,《重构》,《系统分析师》这三本书。专门针对这一个主题做一个系统性的学习。由于内容过多,我只能一本本总结,完毕后再写个整体的总结。本篇主要总结《Thinking in UML》这本书。

《Thinking in UML》这本书内容丰富,近80万字很厚,尽管我认为他有点啰嗦,但从书本文字内涵上看,作者对软件分析、架构分析、架构设计、系统设计有比较深度的理解,值得去阅读。

概述:

概述一下我的理解,首先我不想讲UML,因为UML只是工具,我们有很多种工具可以替代它,所以讲它没什么用,最好的结果是我们理解更深层次的分析和建模方法以及它的本质和原理,这对我们来说才是最好的学习内容。

开头作者对建模方法做了些说明,从建模方式上看有两种,面向过程的建模方法和面向对象的建模方法,然后分别阐述了它俩的区别和优劣点,得出了一个结论,面向过程的建模方法更适合复杂度较低的系统,面向对象的建模方法更适合复杂度较高的系统。

然后就开始说建模了,讲了从现实世界到业务模型应该如何建模,从业务模型到概念模型要如何转换,从概念模型到设计模型要转换,中间抛出很多建模的要素,对这些要素进行了一一说明。

这其中有5个关键点,一是,抽象角度,二是,抽象层次,三是,用例,四是,参与者,五是,视图。

抽象角度要全才能分析的彻底,抽象层次要适度才能分析的到位,用例的捕获和驱动是设计和执行的根本,参与者是所有建模的中心,视图是建模分析后的产物。

下面就来详细说一下,以上概述的内容。

目录:

1.软件分析为什么要建模?
2.建模过程是怎样的?
3.建模的核心元素

内容:

人们做了很多努力积累了很多知识和经验,这些就是最佳实践。

对于软件产品来说,最佳实践来自两个方面:一方面是技术类,如设计模式等。另一方面是过程类,如需求方法、分析方法、设计方法等。

为什么要建模?建模的方法有哪些?

(面向过程和面向对象分析的图)

我们要分析这个世界,并用计算机来模拟它,首要的工作是将这个过程描绘出来,把它们的因果关系都定义出来;再通过结构化的设计方法,将这些过程进行细化,形成可以控制的、范围较小的部分。

这里有两种方法,一种是面向过程的分析,一种是面向对象的分析。

面向过程的分析方法是找到过程的起点,然后顺藤摸瓜,分析每一个部分,直至达到过程的终点。

但是呢,面向过程有巨大的问题,随着需求越来越复杂,系统越来越庞大,功能点越来越多,一份数据经常被多个过程共享,这些过程对同一份数据的创建和读取要求越来越趋于复杂和多样,经常出现相矛盾的数据需求,因此分析和设计也变的越来越困难。

面向过程遇到的困难,本质上是因为面向过程方法将世界看作是过程化的,一个个紧密相连的小系统,构成这个系统的各个部分之间有着密不可分的因果关系。

这种分析方法在需求复杂度较低的时候非常管用,但当系统如此复杂如同蝴蝶效应那般轻轻扇动一下翅膀就能颠覆变得面目全非时,面向过程分析就不再能进行的下去了。

实际上,并非面向过程的方法不正确,只是因为构成一个系统的因素太多,要把所有可能的因素都考虑到,把所有因素的因果关系都分析清楚,再把这个过程模拟出来实在是太困难了。

我们的精力有限,计算能力有限,只能放弃对整个过程的了解,重新寻找一个方法,能够将复杂的系统转化成一个个我们可以控制的小单元。

这个方法的转换正如:如果一次成型一辆汽车太过困难,我们可以将汽车分解为很多零件,分布制造,再依据预先设计好的接口把它们安装起来,形成最终的产品。

而面向对象的方法则不同,汽车不再被看作一个一次成型的整体,而是被分解成了许多标准的功能部件来分布设计制造。

面向对象方法将世界看作一个个互相独立的对象,相互之间并无因果关系,它们平时是“鸡犬之声相闻,老死不相往来”的,只有在某个外部力量驱动下,对象之间才会依据某种规律相互传递信息。

对象从外部来看,对象与外界有消息通道,对象内部是一个黑匣子,什么也看不到,我们称为封装。当对象聚集在一起形成新的对象,结合后的对象具有前两者特性的总和,这称为聚合。对象繁殖产下孩子拥有父辈的全部本领,这称为继承。每个对象都有多个外貌,在不同情况下可以展现不同的外貌,但本质只有一个,这称为接口。多个对象可能长着相同的脸,但背后对象不同,且它们有着不同的行为,这称为多态。

从宏观角度说,对象是“短视”的,它不知道也无法理解它所处的宏观环境,也不知道它的行为会对整个宏观环境造成怎样的影响。它只知道与它有着联系的身边的一小群伙伴,这称为依赖,并与小伙伴间保持着信息交流的关系,这称为耦合。

一旦我们在对象之间确定了一系列的规则,把符合规则要求的对象组织起来形成特定的结构,它们就能拥有某些特定的能力;给这个结构一个推动力,它们就能做出规则要求的行为。

建模过程是怎样的?

(从用例建模到概念建模到设计建模图)

建模过程首先要从现实世界出发,唯有从现实世界出发才是真正解决问题的出发点。接着从现实世界到业务模型,再从业务模型到概念模型,最后概念模型到设计模型。

从现实世界到业务模型

(从现实世界到业务模型图)

建立模型的过程是一个抽象的过程,首先要知道我们应该如何抽象现实世界。

当我们站在很高的抽象层次,以高度归纳的视角来看这个世界的运作时就会发现,

现实世界无论多复杂,无论是哪个行业,无论做什么业务,其本质无非是由人、事、物、规则所组成。

人是一切的中心,人要做事,做事就会使用一些物,同时做事需要遵循一定的规则。

有了事、物、规则就会逐渐形成系统,事、物、规则、系统它们是相辅相成的,

于是就有了,人驱动系统,事体现过程,物记录结果,规则代表控制。

因此建立模型的关键就是弄明白有什么人,什么人做什么事,什么事产生什么物,中间有什么规则,再把人、事、物之间的关系定义出来,一个模型也就基本成型了。

这里头最关键的就是参与者,也就是说,我们要建立的这个模型的意义,完全是被参与者所决定,所建立的这个模型也是完全为参与者所服务的,参与者是整个建模过程的中心。

而用例是一种用元模型来表示驱动者的业务目标的这么一个东西,也就是说,用例代表了参与者想要做什么并且最终获得什么的模型。

我们所说的这个业务目标就是现实世界中的“事”。而这件事是怎么做的,依据什么规则,则通过称之为业务场景和用例场景的视图来描绘,这些场景就是现实世界中的“规则”。

UML通过元模型和视图,捕获现实世界的人、事、物和规则,于是现实信息转化成了业务模型,这也是面向对象方法中的第一步。

从业务模型到概念模型

(业务模型到概念模型图)

得到业务模型仅仅是一个开始,要想将业务模型转化到计算机能理解的模型,还有重要的一步,概念模型。

我们可以通过概念化的过程来建立适合计算机理解和实现的模型,这个模型称为分析模型。

分析模型向下为计算机实现规定了一种高层次的抽象,这种抽象是一种指导也是一种约束,计算机实现过程非常容易遵循这种指导和约束来完成可执行代码的设计工作。

从业务模型到概念模型的过程有个特点,就是:

所有的操作都通过边界来决定,即能做什么不能做什么由边界决定。

所以边界类实际上代表了原始需求中的“事”,实体类代表了现实世界中的“物”,控制类体现了现实世界中的“规则”,再加上由参与者转化而来的系统的“用户”,这样一来都有了。

这个阶段,我们可以对这些分析类在不同的视角上进行归纳和整理,以表达软件所要求的一些信息。

可以说,从业务模型到概念模型这一过程,正是我们需要的一种从对象世界来描述现实世界的方法。

从概念模型到设计模型

(概念模型到设计模型图)

概念模型只是纸上谈兵,真正的对象世界行为是由java类、c++类、EJB、COM等这些可执行代码构成的,如果缺少了从概念模型到设计模型这个过程,java类和c++类的行为就无法得到验证。

也就是说,设计模型是概念模型在特定环境和条件下的“实例”化,实例化后的对象行为“执行”了概念模型描述的那些信息,因此设计模型得以通过概念模型追溯到原始需求来验证,对象世界是否正确反映了现实世界。

设计模型的工作就是建造零部件,组装汽车的过程。

这里从概念到设计模型的转化有个技巧,即概念模型的边界类可以被转化为操作界面或系统接口;控制类可以被转化为计算机程序或控制程序;,例如工作流、算法等;实体类可以转化为数据库表、XML文档或者其他带有持久化特征的类。

建模的核心元素

不论在需求分析、系统分析还是系统设计上,我们一定要学会采用面向对象的方法。

在面对问题领域的时候首先不要决定去通盘考虑,而是找出问题领域里包含的抽象角度。

如果你把抽象角度都找全了,并且这些角度都分析清楚了,问题领域也就解决了。

例如做需求时不要先去弄清楚业务是如何一步步完成的,而是要弄清楚,有多少业务参与者?每个参与者的目标是什么?参与者的目标就是做需求的抽象角度。

那建模的“模”到底是什么呢?作者给出了答案,模就是“人、事、物、规则”,它是由抽象角度确定的,需要由静态事物加上特定条件产生的场景。

也就是说,我们在建模时,是根据问题领域来建模的,针对问题领域抽象成N个角度,每个抽象角度都可以用一个业务用例来解释,每个业务用例都可以用N个特定场景来描述,每个特定场景都是由事物、规则、行为构成的。

如下图:

(问题领域图)

问题领域 = N个抽象角度

(抽象角度图)

抽象角度 = 参与者的业务目标 = 业务用例

(业务用例图)

业务用例 = N个特定场景

(用例场景图)

用例场景 = 事物 + 规则 + 行为

汇总起来:

问题领域 = N个抽象角度

抽象角度 = 参与者的业务目标 = 业务用例

业务用例 = N个用例场景

用例场景 = 事物 + 规则 + 行为

这个建模公式可以帮助我们理清思路。这在业务建模、分析建模、设计建模时都能遵循同样的公式。

这里我们来介绍下这里的一些元素,包括用例、抽象层次、抽象角度、视图、参与者。

用例

前面我们说过,用例代表了参与者想要做什么并且最终获得什么的模型,也就是说,用例是一种把现实世界的需求捕获下来的方法。

例如我们要描述一个系统的功能性需求,就要找到对这个系统有愿望的人,让他们来说明他们会在这个系统里做什么事,想要什么结果。

捕获功能性需求,就是用例的作用,而且通过用例,我们可以驱动软件架构的建立。

一个软件要实现的功能可以通过用例来捕获,接下来的所有分析、设计、实现、测试都可以由用例来驱动,也就是以实现用例为目标。一个用例就是一个分析单元、设计单元、开发单元、测试单元和部署单元。

(解释用例和用例场景图)

除了用例,还有用例场景,就是做一件事可以有很多不同的办法和步骤,也会遇到各种意外,因此这件事由很多不同情况的集合构成,这就是用例场景。

用例的颗粒度在建模的不同阶段有所不同。

在概念建模阶段,用例的粒度以每个用例能描述一个完整的事件流为宜。可理解为一个用例描述一项完整业务中的一个步骤。

在系统建模阶段,用例视角是针对计算机的,因此用例的粒度以一个用例能够描述操作者与计算机的一次完整交互为宜。

而且不论粒度如何选择,必须把握的原则是,在同一个需求阶段,所有用例的粒度应该是同一个量级的。

当我们使用用例来捕获需求时,可以从以下三个观点出发:

这个事物是什么?
这个事物能做什么?
人们能够用这个事物做什么?

对于正在准备开发的软件,最好的方法就是从使用者的观点去描述它。

包括使用者可以怎么使用它,得到什么样的利益。

至于功能性观点和结构性观点,则可以通过使用者观点推导出来。

抽象层次

抽象层次在面向对象方法中极其重要,学会站在不同抽象层次考虑问题是建立好模型的基础。

首先我们要明白,抽象层次越高具体信息越少,但概括能力就越强;反之,具体信息越丰富,结果越确定,但相应的概括能力越弱。

这个很好理解,就如公司里有员工、组长、部门长、总监、经理、总裁、总经理,越往上你越对公司整体的运作状况越清晰,但你对具体的执行细节知道的越少,相反你对整体的运作状况越模糊,细节则知道的越清楚。

(抽象层次图)

也就得到一条结论:

抽象的层次越高,被屏蔽的信息也就越多,信息量越少,也就越容易理解和处理。

也就说,反应到抽象对象上来说,在高层次抽象之上的对象,它的描述很粗略但适应能力大,在低层次抽象之上的对象,它的描述很精确但适应能力小。 而当抽象的层次太高,信息量就会过少,人们实施起来就会产生新的困难。

要关注一点,对象参与的场景越多,对象越有抽象的价值,反之则越没有抽象价值。因此在分析过程中,应该关注于那些参与了很多场景的对象,它们往往是分析设计中的重点以及成败的关键。

(抽象方法图)

抽象有两种方法,一种是自顶向下,一种是自底向上。

自顶向下的方法适用于人们从头开始认识一个事物。

例如刚开始学习引擎时,如果直接跟你讲引擎程序执行细节你就听不懂,但我换种方法,用比较高的层次的抽象概念来讲就会比较容易明白。

自底向上的方法则适用于在实践中改进和提高认识。

例如当我们迈过引擎的初级门槛后想要学习更多引擎知识时,就需要去拆解引擎渲染管线,拆解引擎的内存管理方式,拆解引擎资产管理等等,一个个拆解从底层开始学起了解整个引擎的运作方式。

视图

视图的准确应用是建立好模型的一个重要组成部分。

视角是人们观察事物的角度,不同的人或者同一个人出于不同的目的会对同一个信息从不同的角度来审视和评估。

视角是针对每个视图来说的,不同的视角展示了同样信息的不同认知角度,以便理解。

(视图的价值图)

从信息的展示角度来说,恰当的视角可以让观察者更容易抓住信息的本质;

另一方面,从观察者角度说,观察者只会关心信息中他感兴趣的那一部分,其他视角的信息对他是没有多少用处的。

为特定的信息选择正确的视图,为特定的干系人展示正确的视角并不容易,需要因时因地因人制宜。

这里提供两个问题,可以在制图时,帮助大家更好知道该做什么图以及该如何作图。

问题1,应该为哪些软件信息绘制哪些视图。
问题2,应该给哪些干系人展示哪些视角。

参与者

参与者在建模过程中处于核心地位,建模是从寻找参与者开始的,有了人就有了事,有了事就有了物,有了事和物就有了规则,这样整个问题就清晰了。

(参与者中心图)

将所有的参与者找出来,是建模的重要关键点,因为人是一切的中心。

在寻找参与者过程中,我们可以问自己以下问题来帮助确定参与者。

谁负责提供、使用或删除信息?
谁将使用此功能?
谁对某个特定功能感兴趣?
在组织中的什么地方使用系统?
谁负责支持和维护系统?
系统有哪些外部资源?
其他还有哪些系统将需要与该系统进行交互?

有助于检查发现参与者是否正确的问题:

是否已找到所有的参与者?
每个参与者是否至少涉及到一个用例?
能否列出至少两名可以作为特定参与者的人员?
是否有参与者担任与系统相关的相似角色?
是否有两个参与者担任与用例相关的同一角色?
特定的参与者是否将以几种完全不同的方式使用系统?
参与者是否有直观名称和描述性名称?

参考资料:

《Thinking in UML》作者 谭云杰

已发布在微信公众号上,点击跳转

· 读书笔记, 前端技术

感谢您的耐心阅读

Thanks for your reading

  • 版权申明

    本文为博主原创文章,未经允许不得转载:

    读书笔记(六十) 如何做软件分析1-建模

    Copyright attention

    Please don't reprint without authorize.

  • 微信公众号,文章同步推送,致力于分享一个资深程序员在北上广深拼搏中对世界的理解

    QQ交流群: 777859752 (高级程序书友会)