uml图介绍(自用版-非博文)
UML图形概念
类图最重要,其次是用例、时序、活动、部署
用例图 --(给用户看的)(重点掌握) * 三个基本组件 --参与者(Actor):与系统打交道的人或其他系统(即使用该系统的人或事物)。在UML中参与者用人形图标表示 --用例(Use Case):代表系统的某项完整的功能。再UML中使用一个椭圆来表示 --关系:定义用例之间的关系----泛化,包含,扩展 * 泛化关系:就是父子关系,表示一个业务目的(父用例)的不同技术实现(各个子用例)。 --在UML中,用例泛化关系用一个实线空心三角形从子用例指向父用例 * 包含关系:一个用例可以包含其他用例具有的行为,并把它包含的用例行为作为自身行为的一部分。 --在UML中包含关系用虚线箭头加"<<include>>",箭头指向被包含的用例 * 扩展关系:如果在完成某个功能的时候偶尔会执行另外一个功能,则用扩展关系表示。 --在UML中扩展关系用虚线箭头加"<<extend>>",箭头指向被扩展的用例
类图 --(给开发人员看的)(重点掌握)(最重要) * 基本概念: --类图是面向对象系统建模中最常用的图。是定义其他图的基础。 --类图主要是用来显示系统中的类,接口以及它们之间的关系。 --类图包含的主要元素有类,接口和关系。在类图中也可以包含注释和约束。其中关系----泛化,依赖,关联,实现。 * 泛化关系:用来表示类与类,接口与接口之间的继承关系 --在UML中,类间泛化关系用一条实线空心空心三角性由子类指向父类 * 依赖关系:A类中成员方法(函数)的参数或返回值用到了B类,并且成员变量中没有B类变量,那么A类就依赖与B类 --在UML中,依赖关系用带方向的虚线由A指向B * 关联关系:A类中成员方法(函数)中用到了B类(即成员变量中含有B类变量)那么A类就关联B类 --在UML中,关联关系用带方向的实线由A指向B --在关系上可以写注释和约束 --如果关联关系互相拥有,箭头可有可无 --聚合关系:是关联关系的一种,是更强的关联关系。关联关系所涉及的两个类处在同一个层次上,而聚合关系中,两个类处在不同的层次上,一个代表整体,一个代表部分 -比如汽车组成部分有引擎和轮胎,但引擎和轮胎可以是其他品牌的引擎和轮胎,而且汽车坏了,不一定是引擎和轮胎坏了 --组合关系:是关联关系的一种,是比聚合关系还要强的关系。代表整体的对象负责代表部分对象的生命周期。 -比如一个公司没了,那么这个公司的所有部门也一定没了 * 实现关系:用来表示类与接口之间的实现关系,也就是说接口类(抽象类)中只是提供了空的虚函数(接口/抽象),虚函数(接口/抽象)的具体功能由子类实现 --在UML中,实现关系用一条虚线空心三角形由子类指向父类
对象图 --(已被类图给彻底淘汰,不用) * 已经被类图给淘汰了,不记笔记了
时序图 --(重点掌握) * 基本概念: --时序图用于描述对象之间的传递消息的时间顺序,即用例中的行为顺序 --当执行一个用例时,时序图中的每条消息对应了一个类操作或者引起转换的触发事件 --在UML中,时序图表示为一个二维的关系图,其中,纵轴是时间轴,时间延竖线向下延伸;横轴代表在协作中各个独立的对象,当对象存在时,生命线用一条竖直的虚线表示,产生的消息用带方向的横实线表示(从一个对象的生命线到另一个对象的生命线的实线箭头表示,箭头以时间的顺序在图中上下排列)
活动图 --(重点掌握) * 其实就是流程图 --它用于描述系统的活动,判定点和分支等。 --开始变成实心圆,结束变成带空边框的实心圆 --分叉和汇合用一条粗实线表示 --引入泳道概念 * 动作状态:原子的,不可中断的动作,并在此动作完成之后向另一个动作转变。 --在UML中,动作状态用圆角矩形表示,动作状态所表示的动作写在圆角矩形内部 * 分支与合并:分支在软件系统中很常见。一般用于表示对象类所具有的条件行为。条件行为用分支和合并表达。 --在UML中,分支与合并菱形表示。分支包括一个入转换和两个带条件的出转换。合并包含连个代条件的入转换和一个出转换 * 分叉与汇合:分叉用来描述并发线程,每个分叉可以有一个输入转换和两个或多个输出转换,且每个转换都可以是独立的控制流;汇合代表两个或多个并发的控制流同步发生,当所有的控制流都达到汇合点后,控制才能继续往下进行,每个汇合可以有多个输入转换和一个输出转换。 --在UML中,分叉和汇合用一条粗实线表示 * 泳道:泳道将活动图中的活动划分为若干组,并将每一组指定给负责这组活动的业务组织。泳道区分负责活动的各个对象,明确地表示哪些活动是由哪些对象进行的,并且每个活动只能明确的属于一个泳道。 --在UML中,泳道用一条垂直实线绘出,垂直线的分割区域即为泳道。 --在UML中,也可理解为两线间为一个泳道-即一个泳道由两条垂直直线绘出
状态图 --(知道就行,最好能看懂) * 通过建立对象的生存周期模型来描述对象随时间变化的动态行为。 --这个非常难,并且容易越画越乱,要求能看懂即可。
协作图 --(也叫合作图,已被时序图替代)(不常用,不用) * 是一种交互图。可读性差,不要用。 --这个用的不多,主要时序图更好,用时序图就行。不要用协作图。
包图(Package) --(不是很重要) * 由包和包之间的关系组成。包的图标就如同一个带标签的文件夹。 --包提供了一种用于组织各种元素的分组机制。 --在UML中,包用来对元素进行分组,并为这些元素提供命名空间。包所拥有的或者引用的所有元素成为包的内容,包没有实例。
组件图 --(也不是很重要) * 组件图用来建立系统中各个组件之间的关系,各组件通过功能组织在一起。 --在UML中,组件使用在左侧有连个小矩形的大矩形来表示。 * 组件图可以用来设计系统的整体框架。
部署图 --(重点,主要给运维的人看的) * 部署图用来帮助开发者了解软件中的各个组件驻留在什么硬件位置,以及这些硬件之间的交互关系。 * 节点:用来表示一种硬件,可以是打印机,计算机等。节点的标记符号是一个三维框,在框的左上方包含了节点的名称。 * 通信关联:节点通过通信关联建立彼此的关系,采用从节点到节点绘制实线来表示关联。
设计模式
软件学习的内功和招式
内功:数据结构、算法、设计模式、重构、软件工程。
招式:各种编程语言;各种开发工具;各种框架技术。
设计模式相关的学习概念
有一个“简单工厂模式”不属于GoF23种设计模式,但大部分的设计模式书籍都会对它进行专门的介绍。 --设计模式目前种类:GoF的23种 + "简单工厂模式" = 24种 --因为简单工厂模式用的比较到也比较简单。
设计模式的基础是: 多态 * 初学者:积累案例,不要盲目的背类图。 * 初级开发人员:多思考,多梳理,归纳总结,尊重事物的认知规律,注意临界点的突破,不要浮躁。 * 中级开发人员: 合适的开发环境,寻找合适的设计模式来解决问题。多应用,要对经典设计模式大量,自由的运用。不断的追求。
面向对象设计原则:
1、依赖倒置原则(DiP) *高层模块(稳定)不应该依赖于底层模块(变化),二者都应该依赖于抽象(稳定)。 *抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 2、开放封闭原则(OCP) *对扩展开放,对更改封闭。 *类模块应该是可扩展的,但是不可修改。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 3、单一职责原则(SRP) *一个类应该仅有一个引起它变化的原因。 *变化的方向隐含着类的责任。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 4、Liskov替换原则 *子类必须能够替换他们的基类(IS-A)。 *继承表达类型抽象。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 5、接口隔离原则(ISP) *不应该强迫客户程序依赖他们不用的方法。 *接口应该小而完备。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 6、优先使用对象组合,而不是继承 *类继承通常为"白箱复用",对象组合通常为"黑箱服用" *继承在某种程度上破坏了封装性,子类父类耦合度高。 *而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 7、封装变化点 *使用封装来创建对象之间的分界层,让设计这可以在分解层一侧进行修改,而不会对另一侧产生 不良的影响,从而实现层次间的松耦合。 ////////////////////////////////////////////////////////////////////////////////////////////////////// 8、针对接口编程,而不是针对实现编程 *不将变量类型声明为某个特定的具体类,而是声明为某个接口。 *客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。 *减少系统中各部分的依赖关系,从而实现"高内聚、松耦合"的类型设计方案。
面向对象的重构关键技法
*静态->动态 *早绑定->晚绑定 *继承->组合 *编译时依赖->运行时依赖 *紧耦合->松耦合
常用的设计模式(重点)
/////////////////// 简单工厂
(第0种)
简单工厂模式(不被GoF承认的第24种设计模式) * 优点: --实现了对象创建和使用的分离。 --使用时接口不变,只是参数变化了,减少使用者记忆场景 * 缺点 --对工厂类职责过重,一旦不能工作,系统受到影响 --增加系统中类的个数,复杂度和理解度增加 --违反"开闭原则",添加新产品需要修改工厂逻辑,工厂越来越复杂 * 为什么使用频率高 --因为简单,只有一层抽象。在工厂类负责创建的对象比较少,不会造成工厂类方法中的业务逻辑太复杂时使用。
图例:
1 | @startuml |
(简单工厂模式完)
/////////////////// 工厂
(第一种)
工厂模式 * 简单工厂模式 + "开闭原则" = 工厂模式 * 优点: --使用时只需要使用抽象接口指针,获得唯一方法来直接搞定具体业务。 --实现了对象创建和使用的分离 --系统的可扩展性非常好,无需修改接口和原类 * 缺点: --增加了系统中类的个数,复杂度和理解度增加 --增加了系统的抽象性和理解难度。
图例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60@startuml
abstract class 水果
{
+{abstract} getname()
}
class 苹果
{
*getname()
}
class 香蕉
{
*getname()
}
class 梨子
{
*getname()
}
abstract class 工厂
{
+{abstract} createFruit()
}
class 苹果工厂
{
+createFruit()
}
class 香蕉工厂
{
+cretaeFruit()
}
class 梨子工厂
{
+createFruit()
}
苹果工厂 --|> 工厂 : 继承
香蕉工厂 --|> 工厂 : 继承
梨子工厂 --|> 工厂 : 继承
苹果 --|> 水果 : 继承
香蕉 --|> 水果 : 继承
梨子 --|> 水果 : 继承
工厂 ..> 水果 : 依赖
苹果工厂 .. n苹果 : 创建
香蕉工厂 .. n香蕉 : 创建
梨子工厂 .. n梨子 : 创建
@enduml(工厂模式完)
/////////////////// 抽象工厂
(第二种)
抽象工厂模式 * 优点: --拥有工厂方法模式的优点 --当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族的对象 --增加新的产品族很方便,无须修改已有系统,符合"开闭原则" * 缺点 --如果增加新的产品等级结构,需要对原有系统进行较大的修改,甚至需要修改系统的抽象层代码,违背了"开闭原则" * 使用场景 --系统中有多余一个的产品族 --产品等级结构稳定 --(比如电脑厂商很多,而且电脑主板的接口稳定CPU、内存、显卡) * 所以使用时多思考是否符合"开闭原则"
抽象工厂图例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102@startuml
abstract class 水果
{
+{abstract} getname()
}
class 美国苹果
{
*getname()
}
class 美国香蕉
{
*getname()
}
class 美国梨子
{
*getname()
}
class 中国苹果
{
*getname()
}
class 中国香蕉
{
*getname()
}
class 中国梨子
{
*getname()
}
class 日本苹果
{
*getname()
}
class 日本香蕉
{
*getname()
}
class 日本梨子
{
*getname()
}
abstract class 工厂
{
+{abstract} createFruit(string)
}
class 美国工厂
{
+createFruit(string)
}
class 中国工厂
{
+cretaeFruit(string)
}
class 日本工厂
{
+createFruit(string)
}
美国苹果 --|> 水果 : 继承
美国香蕉 --|> 水果 : 继承
美国梨子 --|> 水果 : 继承
中国苹果 --|> 水果 : 继承
中国香蕉 --|> 水果 : 继承
中国梨子 --|> 水果 : 继承
日本苹果 --|> 水果 : 继承
日本香蕉 --|> 水果 : 继承
日本梨子 --|> 水果 : 继承
工厂 ..> 水果 : 依赖
美国工厂 --|> 工厂 : 继承
中国工厂 --|> 工厂 : 继承
日本工厂 --|> 工厂 : 继承
@enduml(抽象工厂完)
/////////////工厂三兄弟总结简单工厂模式 + "开闭原则" = 工厂方法模式
——————————————————————————————————————————————————————————————工厂方法模式 + "产品族" = 抽象工厂方法模式
//////////////// 单例(Singleton)
第3种
单例模式(Singleton) * 使用大致步骤 --构造函数私有化。 --在类中定义一个静态指针,指向本类的唯一实例。 --提供一个全局的静态方法(全局访问点)来获取单例对象。 * 优点: --单例模式提供了对唯一实例的受控访问。 --节约系统资源。 * 缺点: --扩展略难。单例模式中没有抽象层。 --单例类的职责过重。 * 使用场景 --系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。 --客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
单例图例
1
2
3
4
5
6
7
8
9
10
11
12@startuml
class Singleton
{
+static getSingleton() : Singleton*
-Singleton()
-Singleton(const Singleton &)
-static Singleton *p
}
Singleton o-- Singleton
@enduml(单例完)
//////////////// 代理
第4种
代理模式 --角色和职责: * 抽象 主题角色 --真实 的 泛化 , 代理 的 泛化和关联 * 真实 主题角色 --定义了真实对象(被代理角色),定义了代理对象所代表的目标对象。 * 代理 主题角色 --含有对 真实 主题角色的引用,从而可以操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。 注意:代理对象通常在将客户端调用传递给真实的主题对象之前或者之后执行某些操作,而不是单纯的返回真实对象。 * 使用 者(main) 通过代理来帮助获得 真实 主题角色,通过代理可实现对之前之后附加增值业务。避免了给真实对象前后添加代码。
代理模式图例
1 | @startuml |
(代理模式完)
https://www.blog.srackhall.top/2021/05/22/introduction-to-uml-diagram-(self-use-version---non-blog)/
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 道远日暮时不存!