UML笔记
UML的主要内容就是画图和面向对象的分析设计。
画图的过程中我们要注意每种图的元素和每种图的作用。
面向对象的分析设计中我们要了解OA和OD的思想。
这些都是考试的重点
1. 用例图
1.1 什么是用例
用例是一个活动者(Actor)使用系统的一项功能时所进行的交互过程的一个文字描述序列。
用例的作用
- 用例是软件从需求分析到最终实现的最后一步,它显示了用户希望系统实现的功能,有利于用户和软件开发人员之间的沟通。
- 用例可视化地表达了用户的需求和系统功能,克服了纯文字的说明性不足。
- 用例是从外部定义系统,用户不用关心系统内部是如何实现各个功能的。
Actor的描述
Actor包括系统之外的需要使用系统与系统进行交互的事物,包括人、设备、外部系统等。
用例的读者
开发人员、用户、项目经理、测试人员等
用例中的关系
Actor与uc之间的关系
actor与use case之间是关联关系,所以他们之间用直线连接
uc与uc之间的关系
- 泛化
泛化关系是父子关系,用空心三角箭头表示。箭头指向父uc - 包含
包含关系是完成一个uc动作同时需要完成的动作,一般是多个uc做之前都需要完成这个动作我们把它抽象为一个包含关系,就是很多uc都包含这个uc。
包含关系使用虚线箭头表示,箭头指向被包含的这个uc。 - 扩展
扩展关系是我在当前uc上扩展出新的uc,但是我不一定要做这个uc的关系。
扩展关系也是用虚线箭头表示,箭头指向需要扩展的当前uc。
1.2 如何画一个用例图
确定系统
用例图本身就是为系统设计的,一般我要做的这个东西就是系统。
确定actors
识别活动者一般有以下几个思路
- 谁使用了系统的功能
- 谁修改了系统的数据
- 谁从系统处得到信息
- 谁需要对系统进行维护
- 系统需要哪些硬件设备的支持?
确定use case
用例实例
语法测试:【用户】使用系统来【用例】
用例实例是在系统中执行的一系列动作,这些动作将产生活动者想要的价值结果。一个用例是由多个用例实例组成的。
避免CURD
确定uc的时候很容易犯的错误是看到增加、修改、查阅和删除等操作就把他们列为use case,这样会花费自己很多精力但得不偿失。
1.3 案例:邮箱系统
目标:构建一个语音邮箱系统
邮箱系统描述:
语音邮箱系统中,可以为每个系统用户(邮箱主人)分配一个语音邮箱号码.
进行留言时, 拨打语音邮箱系统的主号码, 在听到提示音“请输入邮箱号”后,输入语音邮箱号,听到主人设定的问候语后,进行留言然后挂断电话.
邮箱主人拨打语音邮箱系统的主号码,在听到提示音“请输入邮箱号”后,输入语音邮箱号,听到主人设定的问候语后, 输入密码进行邮箱管理. 此时系统提供三种服务:1.接收信息; 2.更改问候语; 3.更改密码.其中接收留言包括收听新留言、存储留言、删除留言等。
确定系统:
邮箱系统
确定actor
邮箱主人和留言者
确定use case
共同:
- 拨打语音邮箱
邮箱主人:
得到语音邮箱号码- 接受信息
- 更改问候语
- 更改密码
留言人:
- 给邮箱主人留言
画出用例图
这张图很精妙,可以看出上面3步走的时候遇到的一些问题和对use case之间关系的理解。
总结
use case不单纯是动作,而应该是actor想要经过某一个操作达到某一个效果。(这句话有些别扭,但是在理)。在邮箱系统案例中:
邮箱主人想完成的交互:
- 接受信息
- 更改问候语
- 更改密码
留言人想完成的交互:
- 留言
这4个交互过程都可以抽象为use case,但是主人和留言人想要完成以上4个交互的时候都需要在语音邮箱系统拨号,所以这就是一个多个uc抽象出来的带有include属性的use case。邮箱主人想要修改系统信息,还需要登录系统。
以上”拨打邮箱号码“,”登录系统“就是分析过程中漏掉的两个use case
2. 类图
2.1 介绍类图
作用
- 为开发人员提供模仿现实世界的表达方式
- 以用户的术语与客户进行交流,方便促使客户说出需要解决的重要细节。
2.2 类与类之间的关系
- 泛化
泛化就是继承关系。实线三角箭头指向父类。 - 接口
interface嘛。虚线三角箭头指向父类。 - 组合
整体由部分组成,部分可以独立于整体而存在。实心菱形箭头指向整体类。 - 聚合
整体由部分组成,部分不可以独立于整体而存在。空心菱形箭头指向整体类。 - 关联
拥有的关系,一个类知道并可以调用另一个类的属性和方法。
双向关联:老师可以有多名学生,而一个学生也可以有多名老师。
单向关联:一个学生可以有多门课程,但是课程是抽象类无法拥有学生。 - 依赖
一个类的实现需要另一个类的帮助。
依赖关系用虚线箭头表示。箭头指向被依赖的对象。
2.3 类的版型
边界类
边界类是系统与外界的皎洁。如UI、窗体等类。
控制类
控制类用于体现应用程序的执行逻辑,提供相应的业务操作,将控制类抽象出来可以降低界面和数据库之间的耦合度。
控制类一般是由动宾结构的短语(动词+名词)转化来的名词,如增加商品对应有一个商品增加类,注册对应有一个用户注册类等
实体类
实体类保存要放进持久存储体(数据库/文件等)的信息。一般我们创建的类都是实体类,比如书类、计算机类等。
2.4 类图的抽象层次
2.5 如何构造类图
- 寻找案例中的名词
- CRC原则(Class、Responsibility、Collaboration)
案例:图书管理系统
2.6 对象图
3. 顺序图和协作图
3.1 顺序图
基本概念
顺序图显示的是对象之间交互的图,按照时间顺序排列。时序图是一个模型,用于描述对象如何随着时间在某些行为方面进行协作。
顺序图的命名
- objectName:className
- :className
- objectName
所以看到奇怪的命名方式不要感到害怕
构成要素
- 对象
- 生命线
- 控制焦点
案例:图书管理系统
案例描述:
课件里的案例比较奇怪,边界类比较少讲但是在这个案例里用了很多。会显得比较乱。
常见问题
- 消息循环发送
*[i:= 1..n ]:Message2() - 消息条件发送
1.[a>b]:message()
2.使用文字说明 - 类图和顺序图中对象的关系
需要相互参考、相互补充、相互协调
3.2 协作图
概念
协作图是表示对象间的交互图,侧重强调对象间的协作。
转换成协作图也比较容易,找到最开始的类,然后根据开始类延伸出一系列的类,类似画脑图一样的方式画出来就是协作图了。
如果一个协作图中的两个对象之间的交互超过了两个该怎么画这样的协作图?
答:一条状态线上可以有多个交互,也就是多个箭头。➡️➡️
3.3 顺序图&协作图
相同
- 都支持所有的消息类型
- 都确定了发送者和接受者的责任
- 都能在一定程度上衡量系统的耦合度
不同
- 协作图不能反映对象的创建、撤销等操作
- 顺序图强调的是时间顺序上的交互,协作图强调的是对象与对象之间的的交互
- 协作图必须要有消息的序列号
4. 状态图和活动图
4.1 状态图
定义
状态图描述的是一个对象生命周期中的状态序列,以及引起状态变化的事件和伴随状态变化后的动作。
组成状态图的元素
- 实心圆圈:初态
- 实心圆圈外一个圈:终态
- 圆角矩形:状态
- 箭头:转换。描述包括触发事件、监护条件和动作等。turnOn[water exists]/boil water
细节
- 可以设置某个状态进入和退出的时候执行什么:
entry/balabala和exit/balabala - H:
表示历史状态
4.2 活动图
定义
活动图是描述系统行为的模型,用来描述过程中活动及其迁移。
活动图中可以包含多个对象,在图中 有客户、系统和供应商三个对象。
组成活动图的元素
- 实心圆圈:初态
- 实心圆圈外一个圈:终态
- 椭圆矩形:活动节点
- 菱形:分支与监护条件
- 粗直线与箭头:分岔与汇合
- (泳道)(对象流)
4.3 状态图&活动图
相同
- 都是对系统的动态行为建模。
不同
- 状态图主要表示对象之间的交互关系,活动图主要表示过程中活动的交互关系。
- 状态图中能够表现出并发
5. 构件图与部署图
5.1 构件图&部署图
构件图用来建模系统的各个构件,包括源代码文件、脚本文件、可执行文件等。它们是通过关系或者包的形式组织在一起的。构件图可以帮助我们了构件实现了软件的哪些功能和构件位于软件的什么位置。
部署图帮助我们了解各个构件驻留在什么硬件位置,以及硬件节点之间的交互关系。
5.2 构件图
构件是系统中可更换的代码模块。
构件图的解读
- 构件可以由构件组成
- 构件之间的关系主要是依赖:include、import和implement等。
关系表示为虚线箭头上的文字 - 构件还可以和类图中的对象产生联系。
5.3 部署图
案例:web server
描述:
例 建模一个网上扫描系统的部署图。其详细的需求如下所示:
扫描仪通过内部的PCI总线连接到网卡。需要编写代码来控制扫描仪,代码驻留在扫描仪内部。
扫描仪通过无线网卡与插入到Web服务器KONG的无线hub通信,服务器通过HTTP协议向客户PC机提供Web页。
Web服务器安装定制的Web服务器软件,通过专用数据访问构件与产品数据库交互。
在客户的PC机上将提供专用的浏览器软件,它运行产品查询插件,只与定制的Web服务器通信。
画部署图的过程
确定节点
注意案例中出现的硬件中网卡和无限模块的关系。所以最后确定下来的节点有:
Scanner、NetworkCard、WirelessHub、KONG:WebServer和ClientPC。共5个
添加通信关系
根据案例的描述很容易确定通信关系
添加构件
在硬件中有什么软件基本上就是需要寻找的构件。本案例中把DB作为一个构件而不是一个硬件放在部署图中,可能是因为确实需要用到的地方实在比较小不作为一个硬件。
确定关联关系
easy
6. GRASP原则
GRASP(General Responsibility Assignment Software Patterns),中文名称为“通用职责分配软件模式”,GRASP一共包括9种模式,它们描述了对象设计和职责分配的基本原则。
案例:购物车、商品
6.1 基本模式
信息专家(Expert)
通俗点来讲,就是一个类只干该干的事情,不该干的事情不干。在系统设计时,需要将职责分配给具有实现这个职责所需要信息的类。信息专家模式对应于面向对象设计原则中的单一职责原则。
创造者(Creator)
如果符合下面的一个或者多个条件,则可将创建类A实例的职责分配给类B:
- B包含A;
- B聚合A;
- B拥有初始化A的数据并在创建类A的实例时将数据传递给类A;
- B记录A的实例;
- B频繁使用A。
此时,我们称类B是类A对象的创建者。如果符合多个条件,类B聚合或者包含类A的条件优先。
低耦合模式(Coupling)
耦合是评价一个系统中各个元素之间连接或依赖强弱关系的尺度
为了降低改变带来的影响和类与类之间的依赖,并提高重用性提出了低耦合的思想。
低耦合的基本原则
Don't Talk To Strangers
不需要通信的两个类就谁也别理谁好了。
Transfer
如果一个职能分配给A不合适(提高了耦合度或者不符合信息专家原则),可以把这个职能分配给和A有连接的B
no connection between inner class
两个不同模块的内部类之间不能直接连接
高内聚模式(Cohesion)
内聚是评价一个元素的职责被关联和关注强弱的尺度。
在一个低内聚的类中会执行很多互不相关的操作,操作与操作之间没有联系会显得这个类非常杂乱,难以理解。
控制器模式(Controller)
问题:谁应该负责处理一个输入系统事件?
分析:一个控制器是接受并处理系统事件的非图形用户界面的对象。通常一个控制器需要把接收的任务委托给其他对象,它只负责控制和协调,本身不完成太多的功能。控制器不用包含太多的业务逻辑,所以一个系统可以也应该设计多个控制器实现对不同模块的控制。
6.2 拓展模式
多态(Polymorphism)
就是我们学习的多态,可以通过不同的子类实现不同的功能。
纯虚构(Fabrication)
目的
纯虚构模式用来处理低耦合与高内聚之间的矛盾。高内聚需要拆分出更多数量的类,但是对象之间需要协作来完成任务,这又造成了高耦合,反之亦然。
定义
和C++中的纯虚函数类似,将实体类中的一部分功能抽象出来成为纯虚类。
中介模式(Indirection)
不影响耦合和情况下把两个类连接起来
受保护变化模式(Variation)
定义
预先找出不稳定的变化点,使用统一的接口封装起来,如果未来发生变化的时候,可以通过接口扩展新的功能,而不需要去修改原来旧的实现。也可以把这个模式理解为OCP(开闭原则)原则,就是说一个软件实体应当对扩展开发,对修改关闭。
6.3 补充:单一职责原则
“就一个类而言,应该仅有一个引起它变化的原因。”也就是说,不要把变化原因各不相同的职责放在一起,因为不同的变化会影响到不相干的职责。
案例:
图形绘制程序和Area()之间没有任何联系,违反了单一职责原则,应改为:
7. 面向对象设计原则
7.1 OO的原则
单一职责原则
应该只包含单一的职责,并且该职责被完整地封装在一个类中。
就一个类而言,应该只有一个引起它变化的原因。
开闭原则
软件实体应该对扩展开放,对修改关闭。
软件应该具有稳定的抽象层+灵活的具体层,找到系统的可变因素并将其封装起来。
里氏替换原则
所有引用基类的地方必须能够透明地使用其子类的对象。
在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和一异常,反过来则不成立。
我喜欢动物——>我喜欢狗
依赖倒转原则
高层模块不应该依赖低层模块,他们都应该依赖抽象。
在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中。
接口分离原则
客户端不应该依赖哪些它不需要的接口。
当一个接口太大的时候,可以将它分割成更小的接口。每一个接口应该承担一种相对独立的角色,不该做的事情不要做。
7.2 其他原则
合成复用原则
优先使用对象组合,而不是继承来达到复用的目的。
继承复用实现简单、易于拓展,但是破坏了系统的封装性。组合/聚合的耦合度较低,可以有选择性地调用对象的操作。
迪米特法则
一个软件实体应当尽可能少于其他实体发生相互作用来降低耦合度。
不要和陌生人说话就是迪米特法则的内容。
8. 面向对象的分析设计
OOA的主要构成
如何使用OOA进行分析
OOD的方法
7.1 为什么需要面向对象的分析设计?
由于现实世界中的问题通常较为复杂,分析过程中的交流又具有随意性和非形式化等特点,往往客户对软件的需求是站在用户的角度,而作为软件构架的工程师要按照软件的角度来分析客户的需求。为了保证软件需求规格说明的正确性、完整性和有效性就需要进一步验证,我们需要有一套完整并且具有相当可信力的分析设计体系,以便及时加以修正。所谓分析就是理解客户脑子中的概念,跟客户来沟通,分析出专业术语。设计就是对分析出来的专业术语进行归纳,让程序员能够思路更加清晰、更加顺利进行软件的开发活动。
7.2 OOA(Analysis)
面向对象分析方法(Object-Oriented Analysis,OOA),是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题。OOA所强调的是在系统调查资料的基础上,针对OO方法所需要的素材进行的归类分析和整理。
OOA的主要部分
OOA的主要部分分为需求模型、基本模型、辅助模型和模型规约。
需求模型的主要表现方式就是我们使用做多的用例图,这是我们进行面向对象分析的起点,我们设计人员根据客户的需求来创建和解释用例图,用来描述软件应具备哪些功能模块以及这些模块之间的调用关系,用例图包含了用例和参与者,用例之间用关联来连接以求把系统的整个结构和功能反映给客户,最终我们会得到软件的结构和功能分解。
基本模型的主要表现方式是我们经常用到的类图,而基本模型可以分为3个层次:对象层、特征层和关系层。对象层给出所有问题域和系统责任有关的对象,用对象类表示。特征层定义每个对象类的属性与服务。关系层通过一定义的关系描述对象类之间的关系。
辅助模式的主要表现方式包括交互图、活动图、状态图和包图等。这些图都能够帮助我们更好地理解和分析整个系统开发的业务和逻辑。
模型规约对模型中的所有元素进行详细说明。
OOA方法的基本步骤
首先确定需求和系统。系统是指事物的总体概貌和总体分析模型,对系统进行大致的规划是开始系统分析的第一步。
然后捕捉系统系统中的对象。这里所说的对象是对数据及其处理方式的抽象,它反映了系统保存和处理现实世界中某些事物的信息的能力。同时我们需要将捕捉到的对象进行分类为系统和系统之外的,分离出用户、用例并画出用例图。
之后划分类和类的属性。类是多个对象的共同属性和方法集合的描述,它包括如何在一个类中建立一个新对象的描述。属性就是数据元素,可用来描述对象或分类结构的实例,可在类图中给出,并在对象的存储中指定。
最后识别对象间的关系。结构是指问题域的复杂性和连接关系。类成员结构反映了泛化-特化关系,整体-部分结构反映整体和局部之间的关系。
如果有特殊需要,还可以建立详细说明,对模型中的成分进行规范的定义和文字说明,可以集中进行,也可以分散在各个活动中。
OOA的主要原则
(1)抽象:从许多事物中舍弃个别的、非本质的特征,抽取共同的、本质性的特征,就叫作抽象。抽象是形成概念的必须手段。抽象是面向对象方法中使用最为广泛的原则。抽象原则包括过程抽象和数据抽象两个方面。过程抽象是指,任何一个完成确定功能的操作序列,其使用者都可以把它看作一个单一的实体,尽管实际上它可能是由一系列更低级的操作完成的。数据抽象是根据施加于数据之上的操作来定义数据类型,并限定数据的值只能由这些操作来修改和观察。数据抽象是OOA的核心原则。它强调把数据(属性)和操作(服务)结合为一个不可分的系统单位(即对象),对象的外部只需要知道它做什么,而不必知道它如何做。
(2)封装:把对象的属性和服务结合为一个不可分的系统单位,并尽可能隐蔽对象的内部细节。
(3)继承:特殊类的对象拥有的其一般类的全部属性与服务,称作特殊类对一般类的继承
(4)分类:就是把具有相同属性和服务的对象划分为一类,用类作为这些对象的抽象描述。分类原则实际上是抽象原则运用于对象描述时的一种表现形式。
(5)聚合:又称组装,其原则是:把一个复杂的事物看成若干比较简单的事物的组装体,从而简化对复杂事物的描述。
(6)关联:是人类思考问题时经常运用的思想方法:通过一个事物联想到另外的事物。能使人发生联想的原因是事物之间确实存在着某些联系。
(7)消息通信:这一原则要求对象之间只能通过消息进行通信,而不允许在对象之外直接地存取对象内部的属性。通过消息进行通信是由于封装原则而引起的。在OOA中要求用消息连接表示出对象之间的动态联系。
(8)粒度控制:一般来讲,人在面对一个复杂的问题域时,不可能在同一时刻既能纵观全局,又能洞察秋毫。因此需要控制自己的视野:考虑全局时,注意其大的组成部分,暂时不详察每一部分的具体的细节;考虑某部分的细节时则暂时撇开其余的部分。这就是粒度控制原则。
(9)行为分析:现实世界中事物的行为是复杂的。由大量的事物所构成的问题域中各种行为往往相互依赖、相互交织。
7.3 OOD(Design)
OOD(Object Orient Design)是一种解决软件问题的设计范式,一种抽象的范式。抽象可以分成很多层次,从非常概括的到非常特殊的都有,而对象可能处于任何一个抽象层次上。另外,彼此不同但又互有关联的对象可以共同构成抽象:只要这些对象之间有相似性,就可以把它们当成同一类的对象来处理。
OOD设计
OOD的主要工作是使用OOA的结果,将OOA得出的问题,给予设计方案解决。这一阶段需要做的是对软件系统的设计和规划,包含的工作为:
①问题域部分的设计;
对OOA的对象和类的模型进行细致修改,详细考量类的属性、删除不必要的类、抽象需要的继承层次、组织对象的包含结构、对象间消息的传递方式和整体消息顺序安排;
②人机交互与应用控制部分的设计;
交互界面子系统的设计:与界面有关的类及类间结构的设计,以及有关算法的设计;
交互界面子系统和应用之间接口的设计;
应用控制部分的设计:这部分对象主要完成应用的驱动工作。这部分对象不同于从现实世界中抽象出来的对象,在现实世界和问题域中没有原型,它们同界面子系统中的对象及问题对象发生作用,控制系统的运行。
OOD阶段就需要细致到软件的具体实现,然后是人机交互方面,如何使UI合理的提供交互,是否便于后续的功能扩展等。
OOD的目标
OOD的目标是管理程序内部各部分的相互依赖。为了达到这个目标,OOD要求将程序分成块,每个块的规模应该小到可以管理的程度,然后分别将各个块隐藏在接口的后面,让它们只通过接口相互交流。比如说,如果用OOD的方法来设计一个服务器-客户端应用,那么服务器和客户端之间不应该有直接的依赖,而是应该让服务器的接口和客户端的接口相互依赖。
OOA和OOD的区别
OOA重点在于业务需求的分析,而OOD需在前述的问题基础上设计软件结构。