UML概览
🥰名片:
🐳作者简介:乐于分享知识的大二在校生
🌳本系列专栏: (点击直达)统一建模语言UML
🫣致读者:欢迎评论与私信,对于博客内容的疑问都会尽量回复哒!!!
本文序:
⛰️本文介绍:本文是整个统一建模语言UML专栏的总起,由于之后的文章都是对于本文内容的更详细介绍,所以之后本专栏的文章都会在本文的相应地方附上跳转链接(注:因编者时间有限,所以有一些图片来源于网络,为了尊重原作者,所以选择保留水印)
⚓本文食用方法:本专栏可用于从0学习,考试,临时抱佛脚等等
❤️🔥如果本文对您有所帮助的话,请三连鸭UWU
一:介绍
1.1 定义
UML指的是统一建模语言(Unified Modeling Language)。它是一种用于软件开发的标准图形化建模语言,用于描述软件系统的结构和行为。 UML提供了一系列的图形符号和建模概念,帮助开发人员更好地理解、设计和沟通关于软件系统的各种方面。
(UML是一个庞大的图形化表示法体系)
1.2 应用UML的三种方式
草图:在项目开始之前,为开发人员、设计师和客户等,提供沟通的统一语言
蓝图:在项目完成之后,为整个项目做一个描述,便于维护人员等,理解该软件系统
编程语言:这里的编程语言并不是指C++之类的,它的应用场景包括如下
正向工程:从 UML 模型生成代码(如类图 → Java 类)。
例子:用 UML 工具设计一个订单类(包含属性、方法),工具自动生成对应的 Java 类代码框架。
逆向工程:从代码反向生成 UML 模型(如 Java 类 → 类图)。
例子:通过 IDE 插件将已有代码转换为 UML 类图,辅助理解系统结构。
可执行 UML:直接运行 UML 模型(如状态图驱动系统行为)。
例子:用 UML 状态图定义“电梯运行逻辑”,工具模拟电梯行为并生成可执行程序。
1.3 UML的要素
表示法 - 图形
过程 - (UML与过程无关,但最好用RUP)
工具 - 如Ratinal Rose
1.4 UML包括
UML包括: 事物 关系 图 扩展机制
事物:
结构:类、接口、构件、节点等等
行为:交互(消息)、状态等等
分组:包、子系统等等
注释:注释
关系
泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)
各种关系的强弱顺序
: 泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖
(聚合的例子:删掉某一个功能,其下游功能应该全部失效)
图
静态建模:类图
动态建模:顺序图(协作图),用例图,活动图,状态图
扩展机制:Stereotype(构造型)、Tagged Value(标记值)、Constraint(约束)
1.5 例子
◇ 骰子游戏:软件模拟游戏者投掷两个骰子,如果总点数是7则赢得游戏,否则为输
◇ 过程:定义用例—>定义领域模型—>定义交互图—>定义设计类图
◇定义用例(用例是需求分析的一种工具,它是一些情节的描述)
骰子游戏:
1、游戏者请求骰子2
2、系统展示结果:如果骰子的总点数是7,则游戏者赢;否则游戏者输回
◇ 定义领域模型(OOA)—识别问题中的概念,它是对真实世界领域中的概念和想像可视化,与具体实现的软件技术无关(比如java或C#):游戏者 骰子 骰子游戏
UML的图形表示法的简单例子
二:事物&&关系&&图&&扩展机制
2.1 事物
事物(Things)
- 结构事物
- 类:
学生
类,包含属性(学号、姓名)和方法(选课、查询成绩)。- 接口:
支付接口
,定义支付()
方法,具体实现由支付宝或微信支付类完成。- 构件:一个可复用的
登录模块
,封装用户身份验证功能。
- 行为事物
- 交互:用户点击登录按钮时,系统发送
验证请求
消息给服务器。- 状态:订单从
待付款
→已发货
→已完成
的状态变化。
- 分组事物
- 包:将电商系统的所有支付相关类(支付宝、微信、银联)放入
支付包
中管理。
- 注释事物
- 注释:在类图旁标注“此模块需在2023年底前完成重构”。
2.2 关系
关系(Relationships)
- 依赖(Dependency)
- 一种临时关系,比如变量传递等
- 例子:
学生选课
方法需要传入课程
对象参数。若课程类结构改变,选课方法可能受影响。
- 关联(Association)
- 关联:
学生
和课程
之间是“选课”关系,学生可选多门课程。(关联是类的实例之间的关系)- 聚合(Aggregation):
汽车
(整体)由轮胎
(部分)组成,但轮胎可独立存在(换到另一辆车)。- 组合(Composition):
订单
(整体)包含多个订单项
(部分),删除订单时订单项一并删除。 即,部分不能独立整体而存在
- 泛化(Generalization)
- 例子:
猫
和狗
继承自动物类,复用eat()
和sleep()
方法。
- 实现(Realization)
- 例子:
MySQL数据库类
实现数据库接口
中定义的connect()
方法。
- 操作契约是对系统行为进行更加详细和精确的描述,操作契约有操作,交叉引用,前置条件与后置条件
2.3图
图(Diagrams)
- 用例图:描述用户与系统的交互。
- 参与者:
顾客
,用例:下单
、支付
、查看物流
。
- 类图:展示系统静态结构。
学生
类关联课程
类,依赖成绩单
类。
- 顺序图:显示对象间消息传递顺序。
- 用户点击登录 → 界面层调用
验证服务
→ 服务返回结果。
- 活动图:描述业务流程。
- 购物流程:浏览商品 → 加入购物车 → 结算 → 支付 → 生成订单。
- 状态图:对象状态变化。
- 订单状态:
待付款
→已支付
→已发货
→已完成
。
2.4 扩展机制
扩展机制
- 构造型(Stereotype)
定义元素的角色或类别
- 例如用
<<Controller>>
标记用户管理控制器
类,表示它在MVC模式中的角色。格式如下:直接在类名上方用<<Stereotype>>标注 <<Entity>> User -------------- - id: String - name: String --------------- + save(): void
- 标记值(Tagged Value)
为元素添加自定义属性(如版本号、作者)
- 例如为类添加
版本号
标记,如{version=2.1}
。
- 约束(Constraint)
定义元素的规则或限制
- 例如在属性旁标注
{年龄 >= 18}
,限制用户注册时的年龄条件。
常见构造型示例
构造型 | 用途 | 示例 |
---|---|---|
<<Entity>> | 表示持久化数据或业务实体 | User , Order |
<<Boundary>> | 处理系统与外部的交互(如UI、API,如打印机API) | LoginForm , APIClient |
<<Control>> | 封装业务逻辑或流程控制 | OrderManager , AuthController |
<<Service>> | 提供通用服务(如日志、事务管理) | Logger , PaymentService |
<<Interface>> | 标记接口(与UML标准接口不同,标准接口写的就是interface,而这里的可以自定义,比如写REST表表示该接口是REST风格的API) | <<Repository>> ,<<gRPC>> ,<<REST>> |
<<template>> | 模板类 |
三:十三类图
3.1 类图
类图(Class diagram)以反映类的结构(属性、方法或操作),以及类之间的关系为主要目的,描述了软件系统的结构,是一种静态建模方法。
(uml使用类图表示类,接口及其关联,用于静态对象建模)
3.1.1 类的基本表示法
- 类的名字(Name):骰子
- 类的属性(Attribute,Property):点数
- 类的操作(方法)(Method,Operation):获取点数
(注:对于参数如何标注private或者public等等:把下面的-,+号等标注在参数旁边)
3.1.2 接口的基本表示法
- 圆形表示法
[类] ———⚪<接口>
- 构造型表示法
接口用一个类矩形框表示,顶部标注构造型标记«interface»
«interface»
+接口名称
-------
+方法1()
+方法2()
实现关系用虚线空心箭头(从类指向接口)或实线(带三角箭头)表示
[实现类] ————▷ «interface» 接口名称
3.1.3 包
作用:可以用来表示层次结构(子系统);可以用来组织各种内容
(图中为呈现层,业务逻辑层等)
(呈现层:通过多种图表(如用例图、类图、序列图)共同描述系统的用户界面和交互逻辑)
3.1.4 类之间的关系
(注:以下对于“关系”的解释,也适用于其他图)
依赖(一个事物的变化影响另外一个事物)
关联(关联名、导航、角色、多重性、聚合、组合)
泛化(extends):就是继承
实现(implements)
3.1.4.1 依赖关系
该组件需要用到什么,你就是依赖什么,下图为代码和图
如图,User Action依赖于UserManager,那么UserAction所在的模块《呈现层》和UserManager所在的《业务逻辑层》也是有相应的依赖关系
注:
依赖的作用:拥有提供者类型的属性,向提供者发送信息,接收提供者类型的参数,提供者是超类或接口
依赖标签有call和create:
call含义:表示一个模块、函数或组件在运行时会调用另一个模块或函数,依赖于对方提供的服务或功能。
create含义:表示一个模块、函数或组件在运行时创建另一个模块或对象,它依赖于这个创建过程
依赖关系的核心特点
临时性:Driver 只在 drive 方法中临时使用 Car,并不长期持有它(没有将 Car 作为属性)。
代码体现:依赖通常出现在方法的参数、局部变量或返回值中(如 drive(Car car))。
UML 表示:虚线箭头从依赖类(Driver)指向被依赖类(Car)。
注:低耦合:表示要降低跟不稳定的其他对象之间的依赖关系(而稳定的,比如相应API等等,并不是要低耦合的目标,是没必要也是不可能的) 补充:IOC与DI
3.1.4.2 关联关系
-
关联名
-
导航性
一般是实体间的联系
在代码中:表示引用关系
导航:如student.name(单向,双向)
CTRL+d:删除箭头
上图中user的角色名creator已经用关联表示,所以左上图的document没有creator字段
creator前面的符号:+为public,-为private,#为protect
- 多重性
学生和class之间是多个学生“关联于”一个class,对应*号和1符号
一个message对应于2个user,但是我们一般不这么画
注:若有关联则不需要依赖
3.1.4.3聚合与组合
聚合:飞机场是整体,飞机是部分(aggregate),飞机可以脱离飞机场独立存在
组合:是一种聚合,一个窗口包含按钮和菜单,按钮不可脱离窗口独立存在
3.1.4.4 实现关系
一般通过接口interface实现
3.1.4.5 操作契约
定义:操作契约(Operation Contract)定义了在某个操作执行时,输入条件、预期行为以及输出结果的规范。操作契约通常用于精确定义类的操作如何执行,并且描述它的前置条件、后置条件以及操作本身的行为。
要素:
前置条件(Preconditions)
:描述操作开始之前必须满足的条件。它规定了操作在执行前的系统状态或输入条件。
后置条件(Postconditions)
:描述操作执行完后,系统应该达到的状态或输出结果。它规定了操作执行后的效果。
交叉引用
:在描述操作时,引用与之相关的其他部分(如其他操作契约、类的状态、业务规则等),它有助于确保操作契约与系统其他部分的一致性
不变式(Invariants)
:描述在操作执行过程中始终保持的条件,不会因为操作执行而改变的系统状态。
操作本身的行为(Behavior)
:描述该操作的具体行为,可能会涉及多个步骤、条件判断、消息发送等。
例子:
操作名称:用户登录(Login)
前置条件:
1. 用户已经注册,并拥有有效的用户名和密码。
2. 用户的账户没有被禁用。
3. 系统处于正常运行状态,能够进行身份验证。(见系统状态,第2节)
后置条件:
1. 用户成功登录后,系统创建一个用户会话,并分配一个唯一的会话ID。
2. 用户能够访问自己的个人主页。
操作本身的行为:
1. 用户输入用户名和密码,点击“登录”按钮。
2. 系统验证用户名和密码。
3. 如果验证通过,系统创建一个用户会话,并分配会话ID;否则,返回错误消息。
4. 用户被重定向到个人主页。
不变式:
1. 用户的账户信息(用户名和密码)在系统中保持一致性。
2. 系统每次验证时都使用加密算法处理密码。
其中,“(见系统状态,第2节)”即交叉引用
3.1.5 补充
3.1.5.1 分析类图
类图是一个宽泛的概念,根据开发阶段的不同,可以细分为:
- 分析类图(需求分析阶段)
- 设计类图(系统设计阶段)
- 实现类图(编码阶段)
分析类图:是类图的一种具体应用形式,聚焦于需求分析阶段,用于描述系统需要处理的核心业务概念及其关系,不涉及技术实现细节。它本质上是类图的早期简化版本,属于类图的子集。
其中,UserAction为边界类,User为实体类(稳定),UserManager为控制类(活跃)
分析类图与类图(一般指设计类图)核心区别
维度 | 分析类图 | 类图(一般指设计类图) |
---|---|---|
目的 | 捕获业务需求中的核心实体和关系,明确“系统需要什么” | 描述系统具体实现结构,明确“系统如何构建” |
抽象层级 | 高抽象,仅关注业务概念,与技术无关 | 低抽象,包含技术细节(如方法、接口实现) |
内容元素 | - 类名、核心属性(无类型) - 简单关系(关联、聚合) | - 类名、完整属性(含数据类型) - 方法签名、接口实现 - 复杂关系(组合、依赖等) |
使用阶段 | 需求分析阶段(早期) | 系统设计阶段(中后期) |
示例 | 订单 类可能仅包含订单号 、金额 等属性,不定义具体方法 | 订单 类会定义calculateTotal() 方法,并关联到具体数据库操作类 |
需求分析阶段:
[用户] -- 1..* [订单]
[订单] -- 1..* [商品]
设计阶段:
class 用户 {- userId: string- name: string+ createOrder(): Order
}
class 订单 {- orderId: string- total: double+ calculateTotal(): void
}
3.1.5.2 控制类&边界类&实体类
控制类(Control Class)、边界类(Boundary Class)和实体类(Entity Class) 是三种常见的类分类方式,主要用于描述系统的不同职责和交互层次。
1. 基本概念与职责
类类型 | 定义 | 核心职责 | 特点 | 示例 |
---|---|---|---|---|
实体类 | 代表系统中持久化的业务实体,与数据库或数据存储直接关联。 | - 存储和管理业务数据 - 封装业务实体的属性和基本行为 -关注数据(业务实体的属性和持久化) | - 对应现实世界的对象(如用户、订单) - 通常与数据库表结构映射 - 独立于用户交互和技术实现 | User (用户)、Order (订单)、Product (商品) |
边界类 | 描述系统与外部参与者(用户、其他系统)的交互接口。 | - 处理输入/输出(如用户界面、API接口) - 转换外部请求为内部可处理的数据格式 -关注交互(系统与外部的输入/输出) | - 关注交互细节 - 与用户或外部系统直接接触 - 通常不包含业务逻辑 | LoginPage (登录页面)、OrderAPI (订单接口)、FileUploader (文件上传组件) |
控制类 | 协调多个类之间的操作,封装复杂的业务逻辑或流程控制。 | - 实现用例的流程逻辑(如订单创建流程) - 协调实体类和边界类的协作 - 处理事务和异常管理 -关注逻辑(业务流程和规则) | - 聚焦业务流程 - 通常无状态(不保存数据) - 与技术实现解耦 | OrderController (订单控制器)、PaymentProcessor (支付处理器) |
2. 详细对比
维度 | 实体类 | 边界类 | 控制类 |
---|---|---|---|
与用例的关系 | 被动参与用例,提供数据支持 | 直接对接参与者(如用户界面) | 驱动用例的执行,协调多类协作 |
技术依赖 | 依赖数据库或ORM框架 | 依赖UI框架、网络协议(如HTTP) | 通常与技术无关,依赖业务规则 |
生命周期 | 长期存在(如用户信息持久化存储) | 短期存在(如页面关闭后销毁) | 临时存在(如订单创建完成后释放) |
代码示例 | java<br>class User {<br> private String id;<br> private String name;<br>} | java<br>class LoginPage {<br> public void showLoginForm() {}<br>} | java<br>class OrderController {<br> public void createOrder() {}<br>} |
3. 协作关系
- 典型交互流程(以“用户下单”为例):
- 边界类(如
OrderPage
)接收用户输入(商品选择、地址等)。 - 控制类(如
OrderController
)校验数据,调用实体类(User
、Product
)计算价格,生成Order
实体。 - 实体类(
Order
)持久化数据到数据库。
- 边界类(如
3.1.6
关键字 | 用途说明 |
---|---|
<<interface>> | 表示接口 |
<<abstract>> | 表示抽象类 |
<<include>> | 表示一个用例被另一个用例所包含 |
<<extend>> | 表示一个用例是对另一个用例的扩展 |
<<enumeration>> | 表示枚举类型 |
<<actor>> | 用例图中的参与者 |
<<singleton>> | 表示单例设计模式(非标准但常用) |
3.2 用例图
3.2.1 用例的编写
用例是文本形式的情节描述(是文本文档而不是图形),是一组成功和失败的场景集合,用来描述参与者如何使用系统来实现目标。
参与者是具有行为的实物。
场景是参与者和系统之间的一系列特定的活动和交互也称为用例实例(即下图的基本路径(主成功场景Happy Path)和扩展点(扩展场景,包含异常处理、分支选择等变体))
3.2.2 发现用例
3.2.3 用例图
左上角为用例的包含关系,右下角为泛化关系
3.2.4 用例命名
商品搜索×
搜索商品√
结账功能×
结账√
3.3 顺序图
- 顺序图(Sequence Diagram)是交互图的一种(交互图还包括协作图)
顺序图是强调消息时间顺序的交互图
协作图则是强调接收和发送消息的对象的结构组织的交互图 - 如何对动态方面建模?
所谓动态方面,即随着时间的推移,一些对象被创建,属性值的改变,以及其中一些对象的销毁,对象之间的互相调用!
对象
对象生命线
消息(实际上就是方法调用)
对象的创建与销毁
例子
注:在生命线上的小长方形条叫做,执行规格条(也叫激活条、控制条) ,表示控制期。
控制期就是对象处于“主动控制”状态的时间段,也就是说,它正在执行某个方法或在处理中。
一旦收到一个消息(比如 grade(answer)),控制开始;
执行完这个方法后,控制结束;
如果该对象在执行过程中还调用了其他对象的方法,会有嵌套的执行条出现。
3.4 状态图
3.4.1 基础
3.4.2 要素
3.4.3 示例(门禁卡)
3.4.4 历史状态
H*代表深度历史状态,即可以跳到任意一个活动状态,比如从暂停到选择。(图中是H)
3.5 活动图
3.5.1 基础
3.5.2 要素
3.5.3 相关概念
概览
部分概念的详细解释:
3.6 构件图
3.7 部署图
3.7.1 基础
3.7.2 示例
3.9系统顺序图SSD
3.9.1 定义
系统顺序图是用于描述系统的外部交互,通常用于捕捉系统和外部用户(或外部系统)之间的交互。它通常侧重于系统的边界,表现系统接收外部请求并作出响应的流程。
SSD与SD的区别:SSD主要注重操作与行为,主要展示系统与外部实体(例如用户或其他系统)之间的交互;SD主要注重消息,不仅显示外部与系统的交互,还可以包括内部对象间的消息传递
3.9.2 用途
对用例的主成功场景,以及频繁发生的或者复杂的替代场景绘制SSD
3.10数据流图DFD
3.10.1 基础
数据流图(DFD)是一种用于表示信息系统中数据流动情况的图形工具。它通过图形方式,展示了系统中数据的输入、处理、输出和存储过程,不涉及具体的编程逻辑或控制流程。
它主要包括四个基本元素:
图形符号 | 元素名称 | 作用 |
---|---|---|
○ | 过程(Process) | 描述对数据的处理,如“计算总分” |
→ | 数据流(Data Flow) | 表示数据在各部分之间流动,如“学生信息” |
▭ | 数据存储(Data Store) | 表示静态数据存储,如“成绩数据库” |
□ | 外部实体(External Entity) | 表示系统外部的参与者或系统,如“教师”、“教务系统” |
3.10.2 类比
你可以把一个DFD图理解成一个快递流程图:
- 外部实体:比如“顾客”发出一个快递订单,这是一个外部输入。
- 数据流:快递单、包裹等信息在不同部门之间流动。
- 处理过程:快递公司收件、分拣、派送等环节都属于“处理过程”。
- 数据存储:快递公司的数据库中存储了订单信息,这就是“数据存储”。
整个过程不关注具体谁在操作(程序或人),而是关注数据从哪来,到哪去,中间做了什么处理。
3.11 交互图
包括顺序图(已讲)和通信图。
顺序图在绘图时,新增对象需要往右边扩展,不太方便;通信图加对象更灵活。但顺序图可以清楚表示消息顺序,通信图对顺序表达不够直观
3.12 常见符号
决策符号(Decision):菱形
合并符号(Merge):表示多个选择路径最终归并为一条路径,也是菱形
耙子符号(Fork/Join):表示“并行处理或合并处理”(fork表示分叉)
异步消息:使用刺形箭头,就是→的直线穿过了折线的箭头,即
逻辑架构是软件类的宏观组织结构。不涉及具体的实现技术、部署方式或运行时环境。
“软件类”指的是构成系统的各种模块、组件、类、服务等抽象单元。
你正在做一个自动阅卷系统,其逻辑架构可能包括以下模块
【阅卷系统逻辑架构】
├── 试卷管理模块
├── 答题识别模块(OCR)
├── 答案评估模块(AI/规则)
├── 分数汇总与反馈模块
├── 教师审阅模块
├── 用户身份与权限管理
四:九大模型(核心建模领域)
4.1 总结
UML模型通常按系统设计的不同维度分类,涵盖从需求到实现的完整生命周期:
-
结构模型
- 描述:系统静态结构(如类、组件、部署节点)。
- 关键图:类图、组件图、部署图。
-
行为模型
- 描述:动态交互与流程(如用户操作、状态变化)。
- 关键图:用例图、活动图、状态机图。
-
交互模型
- 描述:对象间消息传递(如接口调用、时序逻辑)。
- 关键图:序列图、通信图、时序图。
-
用例模型
- 描述:用户需求与系统功能边界。
- 关键图:用例图。
-
领域模型
- 描述:业务核心实体与规则(如MDA中的PIM)。
- 关键图:类图(领域类图)。
-
设计模型
- 描述:技术实现方案(如MDA中的PSM)。
- 关键图:类图(设计类图)、组件图。
-
部署模型
- 描述:软硬件部署架构(如服务器、容器配置)。
- 关键图:部署图。
-
组件模型
- 描述:模块化功能单元及其依赖。
- 关键图:组件图。
-
状态模型
- 描述:对象生命周期状态迁移。
- 关键图:状态机图。
4.5 领域模型(业务模型)
4.5.1 定义
领域模型(domain model),也称为概念模型、领域对象模型、分析对象模型,是对问题域中的对象及其关系的抽象描述,目的是帮助开发者更好地理解业务需求和业务规则。领域模型关注的是业务实体及其之间的关系,而不涉及具体的实现方式或软件架构。
软件类通常是面向实现的,它包含属性(数据)和方法(行为),领域模型不是软件类。
4.5.2 领域模型包括:
构建:寻找概念类 绘制uml图 添加关联与属性
概念:即名称
关联:指概念之间的业务关系
属性:指概念的详细特征或状态,描述实体的具体信息。
(领域模型是一种概念模型,不考虑方法)
4.5.3 例子
注:领域模型中关联用的是实直线,类图中用的是→
4.5.4 如何创建领域模型?
寻找概念类(名词短语、分析模式)绘制类图
添加关联和属性 属性的表示法
普通数据类型表示为属性
不要把复杂的领域概念建模为属性,建模为关联等就好
五:面向对象分析与设计OOA/D
5.1 介绍
面向对象分析object-oriented analysis:在问题域内发现和描述对象
面向对象设计object-oriented design:如何定义软件对象以及他们之间如何协作以实现需求
解释一下OO OOA OOD与OOP
OO(面向对象Object-Oriented):
面向对象(OO)指的是基于“对象”概念的编程范式,对象可以包含数据(属性)和代码(方法)。对象是类的实例,类定义了对象的结构(属性)和行为(方法)。
OOA(面向对象分析):
面向对象分析(OOA)是检查问题领域、识别对象(实体)并定义它们的关系和行为的过程。它专注于理解将要开发的系统的需求和约束。
OOP(面向对象编程Object-Oriented Programming):
面向对象编程(OOP)是紧随OOA之后的实施阶段。它涉及在支持对象和类的编程语言中编写代码。OOP允许编写模块化和可重用的代码,以及更准确地建模复杂的现实系统。
OOD(面向对象设计):
面向对象设计(OOD)是介于OOA和OOP之间的中间步骤。它涉及根据OOA的结果设计软件系统的架构。这包括定义类、它们的属性、方法和关系,同时考虑封装、继承和多态等原则。
总之,OOA专注于理解问题领域,OOD将分析转化为软件实现的设计,而OOP使用对象和类将这一设计转化为可执行代码。
5.2 Liskov Substitution Principle(LSP,里氏替换原则)
在使用基类(父类)的地方,应当能够透明地使用其子类的对象,而不影响程序的正确性。
你可以把“父类”看作是一种“通用契约”,比如一个“鸟类”类。这个类中有一个 fly() 方法,代表鸟可以飞。那么你创建一个“子类”比如“麻雀(Sparrow)”,继承“鸟类”,它照样可以 fly(),没有问题。
但是,如果你创建一个“企鹅(Penguin)”子类,也继承了“鸟类”,但企鹅不能飞。如果你还保留 fly() 方法,调用它会导致程序运行错误或语义混乱。这个时候,“企鹅”虽然是“鸟”的子类,但却不能替代“鸟”的对象来使用,这就违反了里氏替换原则。
5.3 开闭原则
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
实现开闭原则的关键是通过抽象和多态来实现模块的可扩展性。常见的实现方式包括使用抽象类和接口定义可扩展的行为,然后使用具体的实现类来实现具体的行为。
5.4 层
层是对类、包或子系统的分组,具有对系统主要方面加以内聚的职责。
以评分系统为例:
【表示层】Presentation Layer
├── Web前端(教师/学生端)
├── RESTful API接口层└── 处理请求路由、参数校验、认证【业务逻辑层】Business Logic Layer
├── 阅卷控制器(GradingController)
├── 评分策略调度器(GradingStrategySelector)
│ ├── 简答题评分(LLM评分 + 规则引擎)
│ ├── 公式处理模块(Math Parser)
│ ├── OCR答案处理模块(图像转文本)
│ └── 多题型评分接口(选择题、填空题、主观题)
├── 成绩汇总与反馈生成模块
├── 权限与角色校验(教师/学生/管理)【数据访问层】Data Access Layer
├── 考生与答案数据存储(AnswerRepository)
├── 试卷与题库存储(QuestionRepository)
├── 分数与报告存储(ScoreRepository)
oo系统通常包含的层有:用户界面层,应用逻辑层和领域对象层,技术服务层。分别负责交互、控制、业务建模与系统支撑。
六:开发的结构化框架
6.1 什么是RUP(Rational Unified Process)
RUP(rational统一过程)强调使用迭代和增量开发方法,旨在通过一系列预定义的阶段来管理软件项目的各个方面,包括需求、设计、编码和测试。
RUP的主要特点包括:
迭代性:项目被分成多个迭代周期(称为“阶段”),每个阶段都专注于特定的任务,如需求收集、设计、实现和测试。
增量性:在每个迭代周期内,开发团队会完成一部分工作,并逐步向最终产品靠近。
可视化:RUP使用各种图表和模型来描述软件项目的不同方面,如用例图、类图和序列图。
文档化:RUP强调文档的重要性,要求团队在项目的各个阶段都生成相应的文档,以确保沟通和理解的一致性。
灵活性:RUP允许项目团队根据项目的具体需求调整过程的某些方面,而不是强制使用一个固定的流程。
RUP通常与UML(统一建模语言)结合使用,因为UML提供了一套标准化的图形表示法,可以帮助团队更好地理解和沟通软件的设计和结构。
6.2 RUP的四个阶段和九种工作流程
6.2.1 RUP的4个阶段
RUP将项目划分为四个顺序阶段,每个阶段以里程碑结束,强调迭代开发:
-
初始阶段(Inception)
- 目标:明确项目范围、可行性、核心需求和高层架构。
- 关键任务:
- 定义业务目标和成功标准。
- 识别主要干系人和风险。
- 创建初步用例模型和项目计划。
-
细化阶段(Elaboration)
- 目标:建立稳定的系统架构,消除高风险问题。
- 关键任务:
- 完善需求(用例细化)。
- 设计系统架构(如类图、组件图)。
- 构建可执行的原型验证架构。
-
构造阶段(Construction)
- 目标:通过迭代开发完成可交付的软件产品。
- 关键任务:
- 编码实现功能模块。
- 持续集成和测试。
- 确保代码符合架构要求。
-
交付阶段(Transition)
- 目标:将产品部署到用户环境并完成验收。
- 关键任务:
- 用户培训与文档编写。
- 修复遗留缺陷和性能优化。
- 正式发布并移交维护。
6.2.2 RUP的9种工作流程
RUP将开发活动划分为 6个核心工程流程 和 3个支持流程,贯穿整个项目周期:
核心工程流程
- 业务建模(Business Modeling)
- 分析业务目标、流程和规则,确保软件与业务对齐。
- 需求(Requirements)
- 收集、分析需求,通过用例(Use Case)描述功能场景。
- 分析与设计(Analysis & Design)
- 将需求转化为系统设计(类、接口、组件、架构)。
- 实现(Implementation)
- 编码、单元测试、构建可执行系统。
- 测试(Test)
- 验证功能、性能、可靠性(集成测试、系统测试等)。
- 部署(Deployment)
- 安装、配置软件,支持用户迁移和运维。
支持流程
7. 配置与变更管理(Configuration & Change Management)
- 管理代码版本、变更请求和基线。
- 项目管理(Project Management)
- 规划、监控进度、风险和资源分配。
- 环境(Environment)
- 搭建开发工具链(IDE、测试工具等)和流程规范。
6.3
up核心思想:短时间定量迭代进化和可适应性开发
up阶段:初始inception细化elaboration构造construction移交transition
需求就是系统必须提供的能力和必须遵从的条件
需求按照FURPS+模型分类
furps+模型:功能性functional可用性usablity可靠性reliablity性能performance可支持性supportablity+实现接口操作包装授权
up需求制品:用例模型 supplementary补充性说明词汇表glossary设想vision业务规则
6.4 敏捷开发Agile Development
6.4.1 基础
敏捷开发通常通过迭代和增量的方式来进行项目管理与开发,主要过程如下:
迭代(Iterations):
- 计划阶段(Planning):确定每个迭代的目标、范围和优先级。
- 执行阶段(Execution):在每个迭代内进行设计、开发、测试和部署工作。
- 评审和回顾(Review and Retrospective):团队评估每个迭代的成果,查看进展和问题,并提出改进建议。
增量(Increments):
每个迭代都产生一个完整的、可部署的软件增量,这意味着每个迭代都会将新功能或改进的功能集成到系统中,从而逐步完善产品。
增量式开发使得软件在每个迭代之后都有更多的功能可用,同时也更容易应对变化和客户需求的变更。
6.4.2 敏捷开发知识补充
敏捷开发 是一种以 快速响应变化 和 持续交付价值 为核心的软件开发方法。
核心理念
-
价值观(敏捷宣言):
- 个体与互动 > 流程与工具
- 可工作的软件 > 详尽的文档
- 客户协作 > 合同谈判
- 响应变化 > 遵循计划
-
原则:
- 迭代开发:短周期(2-4周)交付可运行的功能增量。
- 持续反馈:客户全程参与,快速验证需求。
- 自组织团队:扁平化管理,强调协作与自主决策。
- 简化设计:仅实现当前需求,避免过度设计。
客户协作优先
特点:
-客户作为团队成员全程参与,而非“甲方乙方”对立关系。(客户参与每日站会、迭代评审会,直接提供反馈)
-需求优先级可随反馈动态调整,合同条款保留灵活性(如按迭代付费)。
-价值:快速响应变化,交付真正解决用户问题的功能
常见方法
- Scrum:
用 Sprint(迭代周期)、Product Backlog(需求池)、每日站会 管理进度。 - Kanban(看板):
可视化工作流,限制在制品数量,优化效率。 - 极限编程(XP):
强调 持续集成、测试驱动开发(TDD)、结对编程 等技术实践。
优势
- 快速交付:小步快跑,尽早提供可用功能。
- 降低风险:通过迭代验证需求,减少方向偏差。
- 灵活适应:需求变化可随时纳入后续迭代。
适用场景
- 需求不明确或频繁变化的项目(如互联网产品)。
- 需要快速试错、验证市场的创新业务。
- 团队协作紧密且追求高效沟通的环境。
6.4.3 RUP与敏捷开发
RUP强调文档和架构设计,但可通过裁剪(如减少文档)接近敏捷开发,属于“重型方法论”的灵活化应用。
1. 共同点
- 迭代式开发(Iterative Development)
- 两者均通过多次迭代(Iterations)逐步完善系统,而非一次性交付完整产品。
- 增量式交付(Incremental Delivery)
- 每个迭代交付部分功能(增量),逐步积累形成最终产品。
2. 核心差异
特性 | RUP | 敏捷开发(Agile) |
---|---|---|
本质定位 | 结构化过程框架(方法论) | 价值观与原则集合(如敏捷宣言) |
核心目标 | 控制复杂性和风险,强调文档化与架构设计 | 快速响应变化,专注于交付客户价值 |
灵活性 | 可裁剪但相对规范(“重型方法论”) | 高度灵活,轻量级(如Scrum、XP) |
流程规范性 | 定义明确的阶段、工作流程和工件(Artifacts) | 依赖具体实践(如Sprint、用户故事) |
文档要求 | 重视文档(用例、架构文档等) | 轻文档,强调可工作的软件和面对面沟通 |
适用场景 | 复杂系统、高风险项目(如金融、航天) | 需求变化频繁、创新性项目(如互联网产品) |
3. 迭代与增量的实现方式对比
RUP的迭代与增量
- 阶段化迭代:
- 迭代分布于四个阶段(初始、细化、构造、交付),不同阶段迭代目标不同。
- 例如:细化阶段迭代聚焦架构验证,构造阶段迭代聚焦功能实现。
- 增量规划:
- 增量通过用例(Use Case)或功能模块划分,但需严格遵循架构设计。
4. 敏捷开发的迭代与增量
- 均质化迭代:
- 每个迭代(如Scrum的Sprint)目标一致:交付可工作的软件增量。
- 迭代周期固定(通常2-4周),持续反馈和调整优先级。
- 增量驱动:
- 增量由用户故事(User Story)或最小可行功能(MVP)定义,直接对应客户价值。
用户故事(User Story)是以用户为中心的需求描述方式,通过简洁的语言表达用户对系统功能的期望及其背后的价值。其核心格式通常为:
作为(角色),我希望(功能),以便(价值)。
示例:
“作为普通用户,我希望通过邮箱注册账户,以便快速开始使用平台的核心服务。”
最小可行功能(Minimum Viable Feature, MVF) 是某个功能模块的最小实现版本,仅包含核心必要特性,旨在快速验证其可行性或用户接受度。即,仅实现核心功能,避免过度开发,通过用户反馈验证功能是否满足需求,后续逐步完善功能细节。它是最小可行产品(MVP)的组成部分。
示例:
在一个电商平台的MVP中,“支付功能”的MVF可能仅支持支付宝付款,而暂不包含信用卡、分期等复杂功能。
6.5 传统流程
6.5.1
传统“流程”通常指线性步骤(如瀑布模型),而RUP是框架性方法论,包含动态的、可裁剪的实践集合。
6.5.2 瀑布生命周期
七:设计模式
7.1 基础
OO设计原则:GRASP和GoF(四人帮)设计模式,其建模基于职责驱动设计(RDD, Responsibility-Driven Design)
,即考虑怎样给协作中的对象分配职责,职责
指的是一个对象在系统中应该承担的角色和任务。RDD通过为每个对象明确分配职责,确保对象之间的交互(或协作)是自然且符合实际需求的
设计模式是软件开发中常用的解决问题的方法和思想的总结。它们描述了在特定情境下如何解决重复出现的设计问题。设计模式不是具体的代码,而是一种通用的解决方案,可以应用于不同的情况。设计
强调的是满足需求的概念上的解决方案。实现
则表达了真实完整的设计。
7.2 GRASP原则
7.2.1 定义
GRASP (General Responsibility Assignment Software Patterns,通用责任分配软件模式)是一个面向对象设计的责任分配指导原则集,旨在帮助开发者在设计系统时,合理地将“责任”分配给合适的对象。它并非一个具体的设计模式,而是一组指导思想。
7.2.2 GRASP九大设计原则
.
- 创建者(Creator):
- 定义:负责创建另一个对象的类,通常是该对象的“容器”类或拥有该对象的重要数据的类。
- 含义:如果类A记录类B,或者A紧密使用B,或者A包含/组合/聚成B,或A拥有类B对象创建所需的信息,那么A应当负责创建B的实例。
- 示例:在客户端应用开发时,主程序创建一个窗口对象,由窗口对象来负责创建它内部的各种菜单,按钮,而不是由主程序创建完在设置到窗口里面去。
-
信息专家(Information Expert):
- 定义:将职责分配给拥有完成该职责所需信息的对象。
- 含义:当一个操作需要使用某些数据时,最合适的做法是将该操作分配给拥有这些数据的对象。这样可以减少对象之间的通信和耦合。
- 示例:如果一个订单需要计算其总价,
Order
类就是一个信息专家,因为它包含了所有关于订单的必要信息。
-
低耦合(Low Coupling):
- 定义:尽量减少类之间的依赖关系,保持类之间的耦合度低。
- 含义:解决如何降低依赖性减少变化带来的影响,即提高重用性,就是去掉那些高度依赖于其他类的类,分配降低耦合性的职责,高耦合不是问题,问题是与不稳定元素的高耦合带来的不稳定性与不确定性(所以没人讲与JDK低耦合,因为JDK是稳定的)
- 示例:通过接口或抽象类来减少类与类之间的直接耦合。
-
高内聚(High Cohesion):
-
控制器(Controller):
-
多态(Polymorphism):
- 定义:通过多态性,类的不同子类可以实现相同接口的方法,从而使得代码能够以相同的方式操作不同类型的对象,实现为变化的行为类型分配职责。
- 示例:在一个支付系统中,
CreditCardPayment
和PayPalPayment
可以都实现Payment
接口,而系统调用统一的processPayment()
方法。(解决如何处理基于类型的选择 如何创建可插拔的软件构件,当相关选择或行为随类型不同时,使用多态操作为变化的行为类型分配职责)
-
纯虚构(Pure Fabrication):
- 定义:为避免在设计时强迫一个对象承担不合适的职责,使用“纯虚构”模式引入一个额外的类来承担职责。
- 含义:纯粹虚构类并不直接对应任何业务实体(即不代表任何问题领域的概念),其解决不想违背高内聚低耦合,但是专家模式又不合适,该如何选择对象承担职责的问题,即虚构一种中介类,对该人为制造的类分配一组高内聚的职责,用以支持高内聚低耦合和复用。(所有的GOF设计模式都是纯虚构的)
- 示例:一个
OrderFactory
类负责创建Order
对象,而不是将创建逻辑交给Customer
或Order
类。
-
间接性(Indirection):
- 定义:通过引入中介对象来避免直接交互,减少对象间的耦合。
- 含义:中介模式的应用可以将对象之间的依赖关系隔离,将职责分配给中介对象,将其作为其他构件或服务之间的媒介。
- 示例:
Event
类通过一个EventManager
类来管理和通知不同的订阅者,而不是让Event
直接与每个订阅者进行交互。(大量GOF模式,如适配器,外观等,都是间接性的体现)
-
防止变异 protected variations:
一些补充:
可见性:属性可见性,参数可见性,局部可见性,全局可见性
若对象a能向对象b发送信息 则对于a而言b必须是可见的
若b是a的属性 则存在由a到b的属性可见性
若b作为参数传递给a的方法 则存在由a到b的参数可见性
若b被声明为a的方法内的局部变量 则存在由a到b的局部可见性
当b对于a是全局时 则存在由a到b的全局可见性
7.3 GoF
7.3.1 定义
GoF(Gang of Four)指的是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides,他们在经典著作中整理了 23 个面向对象设计模式。这些模式被分为三大类:创建型(Creational)、结构型(Structural)、行为型(Behavioral)。
7.3.2 23 个面向对象设计模式
1. 创建型 (Creational)
模式 (中文 / English) | 意图 | C++ 代码示例 | 代码解释 |
---|---|---|---|
单例 Singleton | 仅允许一个实例并提供全局访问点 | cpp class Singleton{ public: static Singleton& instance(){ static Singleton s; return s; } private: Singleton()=default; Singleton(const Singleton&)=delete; }; | C++11 局部静态对象天然线程安全;构造与拷贝方法私有并且删除,确保唯一 |
工厂方法 Factory Method | 让子类决定实例化哪种产品 | cpp struct Product{virtual std::string name()=0;}; struct A:Product{std::string name()override{“A”;}}; struct Creator{virtual std::unique_ptr< Product> create()=0;}; struct CreatorA:Creator{auto create()override{return std::make_unique();}}; | 客户端依赖抽象 Creator ;每个派生工厂返回不同具体产品 |
抽象工厂 Abstract Factory | 创建相关对象族且不指定具体类 | cpp struct Button{virtual void paint()=0;}; struct WinBtn:Button{void paint()override{}}; struct Checkbox{virtual void paint()=0;}; struct WinChk:Checkbox{void paint()override{}}; struct GUIFactory{virtual std::unique_ptr< Button> mkBtn()=0; virtual std::unique_ptr mkChk()=0;}; struct WinFactory:GUIFactory{auto mkBtn()override{return std::make_unique();} auto mkChk()override{return std::make_unique< WinChk>();}}; | 工厂接口统一生成“一家族”控件;具体工厂保证产品风格一致 |
建造者 Builder | 按步骤构造复杂对象 | cpp struct Meal{std::vector< std::string> parts; void add(std::string p){parts.push_back§;} }; struct Builder{virtual void partA()=0; virtual void partB()=0; virtual std::unique_ptr< Meal> result()=0;}; struct ConcreteB:Builder{std::unique_ptr< Meal> m=std::make_unique< Meal>(); void partA()override{m->add(“Burger”);} void partB()override{m->add(“Drink”);} auto result()override{return std::move(m);} }; | 将构造逻辑与产品分离;同一流程可产出不同配置 |
原型 Prototype | 通过克隆原型快速复制对象 | cpp struct Shape{virtual std::unique_ptr clone()const=0;}; struct Circle:Shape{int r; Circle(int r):r( r ){} auto clone()const override{return std::make_unique | 复制已存在实例而非新建,避免依赖具体构造过程 |
注:
工厂:解决当为了改良内聚而分离创建职责时应该由谁来负责创建对象的问题,可以创建称为工厂的纯虚构对象来处理创建职责(有点类似GRASP的纯虚构,一种是改良现有设计,一种是在原有规则不适合的情况下的选择)(工厂的改变:简单工厂(静态方法或独立函数)->工厂方法->抽象工厂->依赖注入 / IoC 容器)
角色 | 作用 |
---|---|
抽象工厂(Abstract Factory) | 定义一组产品的创建接口(通常是抽象类或接口) |
具体工厂(Concrete Factory) | 实现抽象工厂接口,负责创建具体的产品族对象 |
🛠 实际应用举例:数据库驱动工厂
你写一个数据库访问层,可以定义抽象工厂:
interface DBFactory {Connection createConnection();Statement createStatement(); }
然后具体工厂根据不同数据库来实现:
class MySQLFactory implements DBFactory {public Connection createConnection() { return new MySQLConnection(); }public Statement createStatement() { return new MySQLStatement(); } } class PostgreSQLFactory implements >DBFactory {public Connection createConnection() { return new PostgreSQLConnection(); }public Statement createStatement() { return >new PostgreSQLStatement(); } }
客户端只需要拿到一个工厂,就可以一致地使用它:
DBFactory factory = new MySQLFactory(); // 或 PostgreSQLFactory Connection conn = factory.createConnection(); Statement stmt = factory.createStatement();
单实例类:解决单实例类的对象全局可见性和单点访问,可以对类定义静态方法以返回单实例,类似私有变量通过定义静态方法返回变量值。右上角角标1,表示仅能创建一个实例。
2. 结构型 (Structural)
模式 (中文 / English) | 意图 | C++ 代码示例 | 代码解释 |
---|---|---|---|
适配器 Adapter | 将现有接口转换为客户端期望接口 | cpp class Target{public: virtual void req()=0;}; class Adaptee{public: void specific(){} }; class Adapter:public Target,private Adaptee{void req()override{specific();}}; | 继承目标抽象并复用被适配者功能,实现接口兼容 |
桥接 Bridge | 分离抽象与实现,独立扩展 | cpp struct DrawAPI{virtual void circle(int)=0;}; struct OpenGL:DrawAPI{void circle(int)override{}}; class Shape{protected: std::shared_ptr< DrawAPI> api; public: Shape(std::shared_ptr< DrawAPI> a):api(a){} virtual void draw()=0;}; class Circle:public Shape{int r; Circle(int r,std::shared_ptr< DrawAPI> a):Shape(a),r®{} void draw()override{api->circle®;} }; | 抽象 Shape 与实现 DrawAPI 通过组合解耦;新增维度不互相影响 |
组合 Composite | 统一处理单个对象与组合 | cpp struct Component{virtual void op()=0; virtual void add(std::shared_ptr< Component>){} }; struct Leaf:Component{void op()override{}}; struct Composite:Component{std::vector<std::shared_ptr< Component>> kids; void add(std::shared_ptr c)override{kids.push_back©;} void op()override{for(auto&k:kids)k->op();}}; | Composite 递归聚合子节点,使客户端对叶/组合一视同仁 |
装饰 Decorator | 运行期动态附加职责 | cpp struct Coffee{virtual double cost()const=0;}; struct Simple:Coffee{double cost()const override{return 2;}}; struct Decor:Coffee{std::shared_ptr< Coffee> c; Decor(std::shared_ptr< Coffee> c):c©{} }; struct Milk:Decor{using Decor::Decor; double cost()const override{return c->cost()+0.5;} }; | 装饰对象持有被装饰对象指针,职责可层层叠加 |
外观 Facade | 提供统一入口简化子系统使用 | cpp class A{public:void fa(){}}; class B{public:void fb(){}}; class Facade{A a; B b; public:void op(){a.fa(); b.fb();}}; | 隐藏内部流程,客户端仅与高层接口交互 |
享元 Flyweight | 共享细粒度对象,节省内存 | cpp struct Glyph{char ch; Glyph(char c):ch©{}}; class Factory{std::unordered_map<char,std::shared_ptr< Glyph>> pool; public: std::shared_ptr< Glyph> get(char c){auto& g=pool[c]; if(!g) g=std::make_shared< Glyph>©; return g;} }; | 工厂使用哈希表缓存字符对象,多处重复使用同实例 |
代理 Proxy | 控制访问或延迟创建真实对象 | cpp struct Image{virtual void show()=0;}; struct Real:Image{std::string f; Real(std::string f):f(f){} void show()override{}}; struct Lazy:Image{std::string f; std::unique_ptr r; Lazy(std::string f):f(f){} void show()override{ if(!r) r=std::make_unique(f); r->show(); }}; | Lazy 仅在首次 show() 时才实例化 Real ,实现按需加载 |
注:
组合:解决处理一组对象或具有组合结构的对象,可以定义组合和原子对象的类,使他们实现相同的接口
外观:解决对一组完全不同的实现或接口需要公共统一接口的问题,可以使用外观对象封装子系统,外观对象提供唯一和统一的接口,并负责与子系统构件进行协作
适配器模式与其他结构型模式的关系:
模式 | 主要目的 | 与适配器的关系 / 差异 |
---|---|---|
适配器(Adapter) | 让两个原本不兼容的类协同工作 | 聚焦于接口不兼容的问题,强调接口转换 |
桥接(Bridge) | 将抽象和实现解耦,使它们可以独立变化 | 两者都用于解耦, 但桥接是预先设计的双层抽象结构, 适配器是事后补救的转换器 |
装饰器(Decorator) | 动态扩展对象的功能,不改变原有对象 | 装饰器强调功能增强,不改变接口; 适配器强调接口变换,保持语义一致 |
代理(Proxy) | 控制对对象的访问(如懒加载、安全控制) | 代理和适配器都使用“包装”(wrapper), 但代理不改变接口,适配器改变接口 |
组合(Composite) | 统一对单个对象和组合对象的处理 | 可与适配器结合:适配旧结构后统一纳入组合结构中 |
外观(Facade) | 提供简化接口,隐藏复杂子系统 | 外观用于简化多个类的使用, 适配器用于解决接口不一致问题, 二者可联合使用 |
3. 行为型 (Behavioral)
模式 (中文 / English) | 意图 | C++ 代码示例 | 代码解释 |
---|---|---|---|
责任链 Chain of Responsibility | 多对象按链式顺序处理请求 | cpp class Handler{std::shared_ptr< Handler> next; public:void set(std::shared_ptr< Handler> n){next=n;} virtual bool handle(int lvl){return next?next->handle(lvl):false;}}; class H1:public Handler{bool handle(int lvl)override{return lvl<=1?true:Handler::handle(lvl);} }; | 每个处理者决定是否处理,否则转交给下一节点,实现解耦 |
命令 Command | 将请求封装为对象 | cpp struct Rec{void act(){}}; struct Cmd{virtual void exe()=0;}; struct Concrete:Cmd{Rec& r; Concrete(Rec& r):r®{} void exe()override{r.act();}}; | 将操作与接收者封装为命令对象,支持撤销、排队等 |
迭代器 Iterator | 提供遍历聚合的统一接口 | cpp template< typename T> struct Agg{std::vector v; void add(T x){v.push_back(x);} auto begin(){return v.begin();} auto end(){return v.end();}}; | 封装 begin/end ,使客户端可用范围 for 遍历 |
中介者 Mediator | 封装同事对象间通信 | cpp struct Col; struct Med{virtual void send(const std::string&,Col*)=0;}; struct Col{Med& m; Col(Med& m):m(m){} virtual void notify(const std::string&)=0;}; struct MedC:Med{Col c1,c2; void set(Col a,Col b){c1=a;c2=b;} void send(const std::string& msg,Col* s)override{(sc1?c2:c1)->notify(msg);} };== | 所有消息通过中介转发,减少对象之间直接耦合 |
备忘录 Memento | 捕获并恢复对象状态 | cpp class Originator{int st;public:struct Mem{int s;}; void set(int s){st=s;} Mem save()const{return{st};} void load(const Mem& m){st=m.s;} }; | 备忘录保存内部状态;外界可回滚而不破坏封装 |
观察者 Observer | 状态变化时通知依赖者 | cpp struct Obs{virtual void up(int)=0;}; struct Sub{int st=0; std::vector<Obs*> os; void att(Obs* o){os.push_back(o);} void set(int s){st=s; for(auto* o:os) o->up(st);} }; | Sub 变化时遍历调用 Obs::up ,实现发布/订阅 |
状态 State | 对象在状态改变时改变行为 | cpp struct C; struct S{virtual void h(C&)=0;}; struct C{std::unique_ptr< S> s; C(std::unique_ptr< S> s)😒(std::move(s)){} void req(){s->h(*this);} void set(std::unique_ptr | 具体状态类更改上下文所持状态对象,实现行为切换 |
策略 Strategy | 定义可互换算法族 | cpp struct Strat{virtual void sort(std::vector< int>&)=0;}; struct Quick:Strat{void sort(std::vector< int>&)override{/…/}}; struct Ctx{std::unique_ptr< Strat> s; Ctx(std::unique_ptr< Strat> s): s(std::move(s)){} void sort(std::vector< int>& v){s->sort(v);} }; | 算法封装为对象并注入上下文,运行期可替换 |
模板方法 Template Method | 定义算法骨架,将可变步骤留给子类 | cpp class Game{public:void play(){init(); while(!over()) turn(); finish();} protected:virtual void init()=0; virtual void turn()=0; virtual bool over()=0; virtual void finish()=0;}; | 流程固定在基类 play() ;子类实现具体步骤 |
访问者 Visitor | 在不修改类的前提下为结构加新操作 | cpp struct Circle; struct Rect; struct Visitor{virtual void v(Circle&)=0; virtual void v(Rect&)=0;}; struct Shape{virtual void acc(Visitor&)=0;}; struct Circle:Shape{void acc(Visitor& vi)override{vi.v(this);}}; struct Area:Visitor{void v(Circle&)override{/…/} void v(Rect&)override{/…*/}}; | 结构元素通过 accept() 将自身传入访客,实现操作集中管理 |
八:Rational Rose
◇ Rational Rose 是一种建模工具
◇ 用例视图
需求分析阶段的利器
◇ 逻辑视图
设计阶段,用例的实现
◇ 组件(构件)视图
构件表示封装了其内容的系统模块;构件是相对独立的模块
◇ 部署视图
表示软件元素在物理架构上的部署,以及物理元素之间的通信
九:实战
题目
请根据以下描述,画出相应的UML图:
◇ 神州六号飞船是神州飞船系列的一种,它由轨道舱、返回舱、推进舱和逃逸救生塔等组成;航天员可以在返回舱内驾驶飞船,轨道仓则是航天员工作和休息的场所。在紧急的情况下,可以利用逃逸救生塔逃生。在飞船两侧有多个太阳能电池翼,可以为飞船提供电能:
4.1 正向工程、逆向工程与MDA
- 正向工程:从UML图形生成JAVA代码
- 逆向工程:从JAVA代码生成UML图形
- 不要依赖于正向或逆向工程!仅是一种辅助手段。画图的目的不是为了生成代码!写代码的目的也不是为了生成图形!
- MDA?
模型驱动架构
Platform IndependentModels (PIMs)和 Platform Specific Models (PSMs)
MOF—(UML>元模型>元—元模型)
二、十三类图(UML 2.x标准图表)
UML 2.x定义了13种标准图表,分为结构图与行为图两大类:
1. 结构图(静态结构)
图表名称 | 核心用途 | 示例场景 |
---|---|---|
类图 | 定义类、接口、关联与继承关系 | 领域模型、数据库设计 |
对象图 | 展示类在某一时刻的实例化状态 | 调试对象关系、快照分析 |
组件图 | 描述系统模块化组件及其接口依赖 | 微服务架构设计、模块划分 |
部署图 | 展示软硬件节点部署及通信路径 | 云环境部署、服务器拓扑规划 |
包图 | 组织代码包层级与依赖关系 | 项目模块化管理、代码分包 |
组合结构图 | 描述复杂类的内部结构(如部件协作) | 复合组件设计、子系统集成 |
2. 行为图(动态交互)
图表名称 | 核心用途 | 示例场景 |
---|---|---|
用例图 | 定义用户目标与系统功能边界 | 需求分析、功能范围确认 |
活动图 | 描述业务流程或算法逻辑(类似流程图) | 订单处理流程、审批流程建模 |
状态机图 | 展示对象状态迁移及触发事件 | 订单状态机、设备生命周期管理 |
序列图 | 按时间顺序描述对象间消息交互 | API调用时序、事务处理逻辑 |
通信图 | 强调对象协作关系(类似序列图但侧重结构) | 分布式系统交互、角色职责划分 |
时序图 | 结合时间约束展示状态变化时序 | 实时系统设计、硬件信号时序分析 |
交互概览图 | 组合活动图与交互图的复合视图 | 复杂交互流程的全局概览 |
八:附录
附录一:图形表示一览
相关文章:
UML概览
🥰名片: 🐳作者简介:乐于分享知识的大二在校生 🌳本系列专栏: (点击直达)统一建模语言UML 🫣致读者:欢迎评论与私信,对于博客内容的疑问都会尽量回复哒!!! 本文序: ⛰️本文介绍&…...
影刀填写输入框(web) 时出错: Can not convert Array to String
环境: 影刀5.26.24 Win10专业版 问题描述: [错误来源]行12: 填写输入框(web) 执行 填写输入框(web) 时出错: Can not convert Array to String. 解决方案: 1. 检查变量内容 在填写输入框之前,打印BT和NR变量的值ÿ…...
LLMs可在2位精度下保持高准确率
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
C语言高频面试题——结构体和联合体区别
在 C 语言中,结构体(struct) 和 联合体(union) 是两种重要的复合数据类型,用于组织和管理多个相关的变量。尽管它们在语法上有些相似,但在内存布局、用途和行为上有显著的区别。以下是详细的对比…...
App爬虫工具篇-mitmproxy
mitmproxy 是一个支持 HTTP 和 HTTPS 的抓包程序,类似 Fiddler、Charles 的功能,它通过控制台的形式和ui界面的方式 此外,mitmproxy 还有两个关联组件,一个是 mitmdump,它是 mitmproxy 的命令行接口,利用它可以对接 Python 脚本,实现监听后的处理;另一个是 mitmweb,它…...
配置openjdk调试环境
先决条件 首先在Ubuntu 18.04上编译SlowDebug版本的openjdk。注意,这里我选择的是x86处理器的电脑。苹果M系列属于ARM芯片,指令集不一样。由于我在苹果上进行垃圾回收调试的时候会报SIGILL错误。为了了解JVM的内部工作原理,不要在这种问题上…...
加油站小程序实战教程13充值规则配置
目录 1 创建数据源2 搭建管理功能最终效果 我们目前已经实现了会员的注册以及登录功能,有了基础的认证之后就进入到了业务部分的开发。会员的话首先是可以进行充值,在充值的时候通常会有一定的赠送,本篇我们来开发一下充值规则的配置功能。 1…...
jQuery — 总结
介绍 jQuery是一款高效、轻量级的JavaScript库,旨在简化网页开发中的常见任务。自2006年发布以来,它凭借直观的语法和强大的功能迅速成为前端开发的标配工具。其核心设计理念是“写更少,做更多”,通过封装复杂的原生JavaScript操作…...
【信息安全工程师备考笔记】第二章 网络信息安全概述
第二章 网络攻击原理与常用方法 2.1 网络攻击概述 概念:损害网络 系统安全属性 的危害行为 危害行为基本类型 信息泄露攻击(机密性)完整性破坏攻击(完整性)拒绝服务攻击(可用性)非法使用攻击…...
国家自然科学基金答辩ppt制作案例模板下载
国家自然科学基金 National Natural Science Foundation of China 支持基础研究,坚持自由探索,发挥导向作用,发现和培养科学技术人才,促进科学技术进步和经济社会协调发展,逐渐形成和发展了由研究项目、人才项目和环境…...
代码随想录第三十七天|华为秋季笔试真题230823
刷题小记: 主要偏向扎实编码基础的考察,但貌似近些年题目难度有所提高,仅供参考。 卡码网136.获取连通的相邻节点列表(卡码网136.获取连通的相邻节点列表) 题目分析: 题目描述: 存在N个转发…...
KUKA机器人KR 3 D1200 HM介绍
KUKA KR 3 D1200 HM是一款小型机器人,型号中HM代表“Hygienic Machine(卫生机械)用于主副食品行业”,也是一款并联机器人。用于执行高速、高精度的抓取任务。这款机器人采用食品级不锈钢设计,额定负载为3公斤ÿ…...
从零开始创建MCP Server实战指南
一、MCP协议核心概念 1.1 什么是MCP? MCP(Model Context Protocol) 是一个标准化的“沟通规则”,由公司Anthropic提出,专门用于让大语言模型(LLM,比如通义千问、ChatGPT等)与外部工…...
C语言教程(十二):C 语言数组详解
一、引言数组的基本概念 数组是一组具有相同数据类型的元素的集合,这些元素在内存中连续存储。通过一个统一的数组名和下标来访问数组中的每个元素。使用数组可以方便地处理大量相同类型的数据,避免为每个数据单独定义变量。 二、一维数组 2.1 数组的…...
Linux[基础指令][2]
Linux[基础指令][2] cp(复制) 格式:cp [-rf] 源文件 {普通文件,目录} 拷贝 cp -r 递归拷贝目录 蓝色为目录,白色为具体文件 拷贝后面加一个不存在的文件会新建文件再拷贝 cp -ir -i是覆盖的时候询问 如果目标文件存在就会覆盖原有文件 mv(重命名/剪切) 格式:mv 源文件…...
MySQL_MCP_Server_pro接入cherry_studio实现大模型操作数据库
大模型直接与数据库交互,实现基本增删改查操作。首先贴下代码地址: https://github.com/wenb1n-dev/mysql_mcp_server_pro 安装环境:win10 1、下载代码 git clone https://github.com/wenb1n-dev/mysql_mcp_server_pro 2、使用conda创建…...
linux命令集
命令 grep -r --includeAndroid.bp libcfs ./ 参数说明 选项/参数作用-r递归搜索子目录。--includeAndroid.bp仅搜索名为 Android.bp 的文件(精确匹配文件名)。libcfs要搜索的关键字(单引号包裹特殊字符如 以避免被 Shell 解析ÿ…...
数据结构:链表
链表的概念及结构: 链表的概念: 链表是一种物理储存结构上非连续的储存结构,数据元素的逻辑顺序是通过引用链接次序实现的 那物理存储结构连续是什么意思? 之前我们讲过顺序表,顺序表的底层是数组,如下…...
【高并发内存池】从零到一的项目之高并发内存池整体框架设计及thread cache设计
个人主页 : zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 文章目录 前言1. 高并发内存池整体框架设计2. 高并发内存池--thread cache2.1 定长内存池的问题2.2 整体框架2.3 自由链表2.4 thread cache哈希桶的对齐规则2.5…...
电气动调节单座V型球阀带阀杆节流套沟槽孔板的作用-耀圣
电气动调节单座V球阀杆节流套是阀门中的一个重要组件,主要用于调节和控制流体介质的流量、压力或流速,同时兼具导向、密封和稳定阀杆运动降低流速减少冲刷的作用。以下是其具体功能和应用场景的详细说明: 1. 节流与流量控制** 作用原理**&am…...
vscode使用笔记
文章目录 安装快捷键 vscode是前端开发的一款利器。 安装 快捷键 ctrlp # 查找文件(和idea的双击shift不一样) ctrlshiftf # 搜索内容...
基于 SpringAI 整合 DeepSeek 模型实现 AI 聊天对话
目录 1、Ollama 的下载配置 与 DeepSeek 的本地部署流程 1.1 下载安装 Ollama 1.2 搜索模型并进行本地部署 2、基于 SpringAI 调用 Ollama 模型 2.1 基于OpenAI 的接口规范(其他模型基本遵循) 2.2 在 IDEA 中进行创建 SpringAI 项目并调用 DS 模型 3、基…...
Idea创建项目的搭建方式
目录 一、普通Java项目 二、普通JavaWeb项目 三、maven的JavaWeb项目 四、maven的Java项目 一、普通Java项目 1. 点击 Create New Project 2. 选择Java项目,选择JDK,点击Next 3. 输入项目名称(驼峰式命名法),可选…...
【MATLAB第115期】基于MATLAB的多元时间序列的ARIMAX的预测模型
【MATLAB第115期】基于MATLAB的多元时间序列的ARIMAX的预测模型 一、简介 ARIMAX(Autoregressive Integrated Moving Average with eXogenous inputs)模型是一种结合自回归(AR)、差分(I)、移动平均&a…...
【以太网安全】——防护高级特性配置总结
目前网络中以太网技术的应用非常广泛、然后、各种网络攻击的纯在(例如针对ARP DHCP 等攻击)不仅造成了网络合法用户无法正常访问网络资源、而且对网络信息安全构成严重威胁、以下配置是对局域网安全配置命令做详解 主要的安全威胁 MAC攻击:泛洪、欺骗 …...
微信小程序 van-dropdown-menu
点击其他按钮,关闭van-dropdown-menu下拉框 DropdownMenu 引入页面使用index.wxmlindex.scssindex.ts(重点)index.ts(全部) DropdownMenu 引入 在app.json或index.json中引入组件 "usingComponents": {"van-dropdown-menu": "vant/weapp…...
再见 Smartdaili,你好 Decodo!
我们将翻开新的篇章,推出新的名称以及更好的代理和刮擦解决方案。了解我们如何帮助全球用户构建、测试和扩展他们的公共网络数据项目。 Smartproxy,即后来的Smartdaili,由一个行业专业人士和企业家团队于2018年创立,其使命是创建一…...
海量文本中的词语距离:在 O(n) 时间内找到最近的词对
想象一个巨大的日志文件、一部鸿篇巨著或者网络爬虫抓取的数据——它们可能达到 TB 级别。现在,假设你需要找出两个特定的词(比如 词语1 和 词语2)在这段庞大文本中出现时,彼此“靠得最近”的距离是多少。 挑战: …...
TextCNN 模型文本分类实战:深度学习在自然语言处理中的应用
在自然语言处理(NLP)领域,文本分类是研究最多且应用最广泛的任务之一。从情感分析到主题识别,文本分类技术在众多场景中都发挥着重要作用。最近,我参与了一次基于 TextCNN 模型的文本分类实验,从数据准备到…...
前台调用接口的方式及速率对比
一、引言 在现代 Web 开发中,前台与后台的数据交互至关重要,而调用接口是实现这一交互的关键手段。不同的接口调用方式在速率上可能存在差异,这会影响用户体验和应用性能。本文将详细介绍几种常见的前台调用接口方式,并对它们的速…...
高级java每日一道面试题-2025年4月21日-基础篇[反射篇]-如何使用反射获取一个类的所有方法?
如果有遗漏,评论区告诉我进行补充 面试官: 如何使用反射获取一个类的所有方法? 我回答: 在Java中,反射是一种强大的机制,允许程序在运行时检查或“反射”自身,从而动态地操作类、字段、方法和构造函数等。这在需要动态调用方法…...
tomcat集成redis实现共享session
中间件:Tomcat、Redis、Nginx jar包要和tomcat相匹配 jar包:commons-pool2-2.2.jar、jedis-2.5.2.jar、tomcat-redis-session-manage-tomcat7.jar 配置Tomcat /conf/context.xml <?xml version1.0 encodingutf-8?> <!--Licensed to the A…...
2.6 递归
递归 特性: >.一递一归 >.终止条件 一般为:0 1 -1 #测试函数的返回值为函数 def test_recursion():return test_recursion() print(test_recursion()) RecursionError: maximum recursion depth exceeded #案例:计算 …...
鸿蒙应用开发:如何修改APP名称与APP的图标
如何修改APP的名称? 修改APP的名称需要修改entry/src/main/resources/base/element/string.json文件 将EntryAbility_label的value修改为“需要修改成的名字”。 文件目录: 代码修改: {"string": [{"name": "modu…...
AI 模型在前端应用中的典型使用场景和限制
典型使用场景 1. 智能表单处理 // 使用TensorFlow.js实现表单自动填充 import * as tf from tensorflow/tfjs; import { loadGraphModel } from tensorflow/tfjs-converter;async function initFormPredictor() {// 加载预训练的表单理解模型const model await loadGraphMod…...
前端性能优化全攻略:JavaScript 优化、DOM 操作、内存管理、资源压缩与合并、构建工具及性能监控
1 为什么需要性能优化? 1.1 性能优化的核心价值:用户体验与业务指标 性能优化不仅是技术层面的追求,更是直接影响用户体验和业务成败的关键因素。 用户体验(UX): 响应速度:用户期望页面加载时…...
使用 acme.sh 自动更新 SSL 证书的指南
上篇文章讲了一下 如何利用acme.sh来申请ssl,但没有讲3个月到期后 如何续期,续期的时候会碰到什么问题? 1.查看当前的当前签发域名的到期时间 acme.sh list 2.重新申请ssl acme.sh --issue --dns dns_namesilo -d xxx.ai -d *.xxx.ai --dns…...
查看Spring Boot项目所有配置信息的几种方法,包括 Actuator端点、日志输出、代码级获取 等方式,附带详细步骤和示例
以下是查看Spring Boot项目所有配置信息的几种方法,包括 Actuator端点、日志输出、代码级获取 等方式,附带详细步骤和示例: 1. 使用Spring Boot Actuator Actuator是Spring Boot提供的监控和管理工具,包含/configprops端点可查看…...
C++与C
文章目录 C与C命令空间const关键字new/delete表达式引用(重点)概念引用的本质引用的使用场景引用作为函数的参数引用作为函数的返回值 总结 强制转换函数重载extern "C"默认参数 bool类型inline(内联)函数异常处理&…...
Nginx中间件的解析
目录 一、Nginx的核心架构解析 二、Nginx的典型应用场景 三、Nginx的配置优化实践 四、Nginx的常见缺陷与漏洞 一、Nginx的核心架构解析 事件驱动与非阻塞IO模型 Nginx采用基于epoll/kq等系统调用的事件驱动机制,通过异步非阻塞方式处理请求,…...
Ansys Zemax | 在 MATLAB 中使用 ZOS-API 的技巧
附件下载 联系工作人员获取附件 本文将介绍一些在MATLAB中使用 ZOS-API 的技巧,以提高您的工作效率并充分利用 ZOS-API 的功能。 简介 OpticStudio开发了应用程序接口 (API) ,用户可以使用API与不同的脚本环境进行连接和交互。使用API,用…...
js 生成pdf 并上传文件
js 生成pdf 并上传文件 使用 JsPDF html2Canvas 代码直接使用 注意注释 import JsPDF from jspdf import html2Canvas from html2canvas // 上传文件的方法 import { handleUploadImage } from /utils/uploadQuillEditdownPDF() {// 要打印元素的idconst cloneDom document.…...
刷刷刷刷刷sql题
NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过,但为了学习sql,整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判断注入类型(数字型或字符型) 传1 …...
JavaScript 中的 this 及 this 指向的改变方法
在 JavaScript 的世界里,this是一个既强大又容易让人困惑的概念。它的指向在不同的函数调用场景下会动态变化,而call()、apply()和bind()这三个方法则为我们提供了精确控制this指向的能力。本文将从基础概念出发,结合具体案例,带大…...
安卓模拟器绕过检测全解析:雷电、MuMu、蓝叠、逍遥、夜神与WSA完整指南
安卓模拟器绕过检测全解析:雷电、MuMu、蓝叠、逍遥、夜神与WSA完整指南 模拟器过检测合集雷电mumu蓝叠逍遥夜神WSA 转自风车2025 前言 随着手机游戏和应用的普及,越来越多的用户选择在PC上通过模拟器来运行安卓应用。然而,许多应用和游戏为…...
VSCode中安装GitGraph
前提是先安装git,官方下载地址:Git - Downloads 1. 在VSCode中安装GitGraph插件 2. 文件->首选项->设置,打开设置界面,在设置界面搜索git path 3. 打开配置文件配置git安装路径: 4. 打开源代码管理,…...
StartAI「万物迁移」功能设计师实操教程:模特换衣场景应用
一、功能核心优势解析 智能识别与场景融合 基于迁移学习算法,精准定位服装轮廓(支持复杂材质如蕾丝、镂空设计),自动匹配目标场景的光影方向与色温。 效率革命 传统PS手动换衣需2-3小时,使用万物迁移可压缩至2-5分…...
【RK3588 嵌入式图形编程】-SDL2-扫雷游戏-放置标记
放置标记 文章目录 放置标记1、概述2、更新Globals.h3、放置标记4、渲染标记5、标记计数6、完整代码7、改进建议8、总结在本文中,我们实现标记放置和跟踪以完成的扫雷游戏项目。 1、概述 在我们扫雷游戏文章系列的最后部分中,我们将添加玩家在可疑的地雷位置放置标记的功能。…...
【Python】Selenium切换网页的标签页的写法(全!!!)
在使用selenium做网站爬取测试的时候,我们经常会遇到一些需要点击的元素,才能点击到我们想要进入的页面, 于是我们就要模拟 不断地 点点点击 鼠标的样子。 这个时候网页上就会有很多的标签页,你的浏览器网页标签栏 be like: 那…...
Spring Boot多环境配置详解
一、为什么需要多环境配置 在实际项目开发中,我们通常需要将应用部署到不同的环境中,比如: 开发环境(dev) - 开发人员本地开发调试使用测试环境(test) - 测试人员功能测试使用生产环境&#x…...