做需求的时候,首要目标不是要弄清楚业务是如何一步一步完成的,而是要弄清楚有多少业务的参与者?每个参与者的目标是什么?参与者的目标就是你的抽象角度。实际上这就是用例!
一个软件要实现的功能通过用例来捕获,接下来的所有分析、设计、实现、测试都由用例来驱动,即以实现用例为目标。一个用例就是一个分析单元、设计单元、开发单元、测试单元甚至部署单元。
系统只有一个逻辑视图。逻辑视图以图的方式说明关键的用例实现、子系统、包和类,它们包含在架构方面具有重要意义的行为,即“人”、“事”、“物”、“规则”是如何分类组织的。
系统只有一个进程视图,也就是说的分析设计视图。进程视图以图形的方式说明了系统中进程的详细组织结构,其中包括类和子系统到进程和线程的映射,即“人”、“事”、“物”、“规则”是如何交互的,它们的关系如何。
系统只有一个部署视图。部署视图以图形的方式说明了处理活动在系统中各个节点的分布,包括进程和线程的物理分布,即“人”、“事”、“物”、“规则”是如何部署在物理节点(主机、网络环境)上的。
实施视图的作用是获取为实施制定的架构决策。实施视图用于:
- 为个人、团队或分包商分配实施工作。
- 估算要开发、修改或删除的代码数量。
- 阐明大规模复用的理由。
- 考虑发布策略。
也就是“人”、“事”、“物”、“规则”如何构成系统的“零部件”,以及我们如何组织人力生产和组装这些“零部件”以建成最终系统。
抽象层次越高,具体信息越少,但是概括能力越强;反之,具体信息越丰富,结果越确定,但相应的概括能力越弱。从信息的表达能力上说,抽象层次越高表达能力越丰富,越容易理解。 如果抽象层次太高,信息量过少的话,人们实施起来又会产生新的困难———信息量不足。因此在面向对象的分析过程中,在适当的时候采用适当的抽象层次是十分重要的。 用例粒度选择的困难本质上是由于没有找准抽象层次而产生的。
抽象有两种方法:
- 自顶向下:适用于让人们从头开始认识一个事物。
- 自底向上:适用于在实践中改进和提高认知。
在软件开发过程中,主体上应当采用自顶向下的方法,通过总结在较低抽象层次的实践经验来改进较高层次的概念以提升软件质量。
抽象层次与边界的选择总是相生相伴的。
人们认识一件事物的时候,只有在了解了很多个方面后才能对这个事物真正的理解。
视角是人们观察事物的角度。视角是针对每一个视图来说的,不同的视角展示了同样信息的不同认知角度以便于理解。 从信息展示角度来说,恰当的视角可以让观察者更容易抓住信息的本质;从观察者角度来说,观察者只会关心信息中他感兴趣的那一部分视角,其他视角的信息对他是没有多少用处的。因此在展示信息时选择适当的视角并展示给适当的观察者时十分重要的。 建立一个好的模型关键的两个概念:视图和视角。为特定的信息选择正确的视图,为特定的干系人展示正确的视角,需要因时因地因人制宜。
工作中要经常思考这两个问题:
- 应该为哪些软件信息绘制哪些视图?
- 应该给哪些干系人展示哪些视角?
在面向对象的眼里,一切有名字的东西都是对象,都应当使用对象的观点来看待它、分析它。哪怕这个东西的名字叫某某业务流程,它也应当看作是一个对象,而不是一个过程。
- 对象是独立的,是离散的。场景中的对象只是对象“映射”到该场景中的一个侧面,我们称之为对象实例。通过一个场景,我们仅能得到对象的一个侧面信息。要深入了解对象,我们要经常分析很多个盖对象的实例所参与的场景,以获得对象的多个侧面,再通过归纳整理这些对象的多个实例抽象出对象的一般特性。这就是对象的分析方法。对象的独立行带来的正是对象的可抽象能力和可扩展能力。
- 对象是原子的。无论什么时候,在同一抽象层次上,在分析过程中都应当将对象视为一个不可分割的院子,哪怕这个对象的规模很大。原子性是抽象层次有意义的重要保证,一旦破坏了原子性,则表示在同一抽象层次上的对象不具备同样的力度,这是的分析工作陷入混乱。
- 对象是可抽象的。在分析过程中,应当关注那些参与了很多场景的对象,它们往往是分析设计中的重点以及成败的关键。
- 对象都有层次性。具体分多少抽象层次应视问题领域的复杂程度而定。
在软件项目里,业务范围和系统范围是不同的。业务范围指的这个项目所涉及的所有客户业务,这些业务有没有计算机系统参与都客观存在。系统范围是指软件将要实现的那些对应于业务功能的系统功能,从功能性需求来说系统范围是业务范围的一个自己。但是一些系统功能则会超出业务范围。
建立业务模型、查找业务用例都必须使用业务主角,而不是普通的参与者。 参与者是涉众的代表;参与者通过代理给其他用户或将自身实例化成用户来使用系统;参与者的职责可以用角色来归纳,用户被指定扮演哪个或那些角色因此来获得参与者的职责。参与者对系统的要求,对系统的表述完全决定了系统的功能性。
一个完整的用例定义是由参与者、前置条件、场景、后置条件构成。
- 在业务建模阶段用例的粒度以每个用例能够说明一件完整的事情为宜,即用一个用例可以描述一项完整的业务流程。这将有助于明确需求范围。
- 在用例分析阶段,即概念建模阶段,用例的粒度以每个用例能描述一个完整的事件流为宜。可理解为一个用例描述一项完整业务中的一个步骤。
- 在系统建模阶段,用例视角是针对计算机的,因此用例的粒度以用一个用例能够描述操作者与计算机的一次完整交互为宜。可理解为一个操作页面或一个页面流。
对于一个还不存在的事物,我们既不能从结构上去解释他,也不能够确定他到底能做什么。所以对于创造一种还不存在的事物,最好的方式是从使用者的观点出发,描述希望这个事物使用者能用它做什么,获得什么。
边界是可大可小的,有建模者主观臆定。
在建模过程中,如果对建模结果感到疑惑,就可以试着改变边界设定,得到不同的参与者和用例,在通过相互印证的方式得到更好的结果。
业务实体是来自现实世界的,在我们建模的问题领域里一定能够找到与它相对应的事物,并且这个事物是参与者在完成其业务目标的过程中使用到的或者创建出来的。业务实体一定是在分析业务流程的过程当中发现的,业务流程实际上就是业务用例的场景。
分包有这样一些指导性原则:
- 高内聚。被分入同一个包中的那些元素应当是相互练习紧密,甚至不可分割的。同时这些元素又具有某些相同的性质,使得包可以抽象出一些接口来代表包内事物与包外的事物交互,以避免包外的事物频繁地范围包内的元素。
- 低耦合。修改包中的任意一个元素,其他任何一个包中的的内容都不受到影响。他们直接可以保持消息通信。
- 如果实际情况难以做到完全解除依赖关系,那么至少应当保证包之间的依赖关系不会被传递。
- 包之间的依赖关系应当是单向的,应当尽量避免双向依赖和循环依赖。
分析类是用户获取系统中主要的“职责簇”。它代表系统的原型类,是系统必须处理的主要抽象概念的“第一个关口”。分析类还可以产生系统设计的主要抽象——系统的设计类和子系统。 分析类就是跨越需求到设计实现的桥梁。 分析类包括:
- 边界类
- 控制类
- 实体类
任何两个有交互的关键对象之间都应当考虑建立边界类。 边界类的常用场景:
- 参与者与用力之间应当建立边界类
- 用例与用力之间如果有交互,应当为其建立边界类
- 如果用例与系统边界之外的非人对象有交互,例如第三方系统,应当为其建立边界类。
- 在相关联的业务对象有明显的独立性要求,即它们可能在各自的领域内发展和变化,但又希望互不影响时,应当为它们建立边界类。
从架构的角度上来说,边界类主要位于展现层。 控制类将用例的特有行为进行封装。 实体类源于业务模型中的业务实体。很多时候可以直接把业务实体转化为实体类。
从架构角度上来书,实体类主要位于数据持久层。 分析类的三高:
- 高于设计实现
- 高于语言实现
- 高于实现方式
由于分析类的抽象层次较高,概括能力很强,所以比设计和实现要稳定。
- 关联关系描述不同类对象之间的结构关系,是一种静态关系,通常与运行状态无关。
- 依赖关系描述一个对象在运行期间会使用到另一个对象的关系,是一种临时性的关系,它通常是在运行期产生的,并随着运行场景不同,依赖关系也可能发生变化。 被依赖的对象改变会导致依赖对象的修改。
我们应当保持单向依赖,杜绝双向依赖关系的产生。
扩展用例是带有抽象性质的,它表示了用例场景中的某个“支流”,由特定的扩展点出发而被启动。严格来说扩展用例应当用在概念用例模型中,通过分析业务用例场景抽象出关键的可选核心业务而形成扩展用例。不过,在业务模型当中使用也是可以接受的,它可以更显示地表示出一个复杂业务用例的各个“分支”。
- 扩展表示的是“可选”,而不是“必须”。这意味着即使没有扩展用例,基本用例也是完成的;如果没有基本用例,扩展用例是不能单独存在的;如果又多个扩展用例,同一时间用例实例也只会使用其中的一个。
- 包含用例表示的是“必须”,而不是“可选”。这意味着如果没有包含用例,基本用例是不完整的,同时如果没有基本用例,包含用例是不能单独存在的。
- 实现所代表的含义是,基本用例描述了一个业务目标,但是该业务目标又多种可能的实现途径,每一种实现途径可以用用例实现来表示,而用例实现与基本用例之间就构成了实现关系。
- 精化关系表示某个模型是通过精化领域给模型而得来的。一个基本用例可以分解出许多更小的关键精化用例。精化关系表示由某个基本对象可以分解为更明确、精细的对象,这些子对象并没有增加、减少、改变基本对象的行为和属性,仅仅是更加细致和明确化了。 精化关系仅仅用于建模阶段,在实现语言中是没有精化这一语义的。
- 聚合关系表达整体由部分构成的语义,即使整体不存在了,部分仍然存在。
- 组合关系表达整体拥有部分的语义,如果整体不存在了,部分也将消亡。