1.计组与体系结构
1.数据的表示
1.1进制转换
按权展开求和 n进制 -> 十进制 每一位八进制数与三位二进制数对应
除n取余法 十进制 -> n进制 每一位十六进制数与四位二进制数对应
计算机的基本单位
位(比特)bit b 字节byte B 千字节 KB 兆字节 MB 吉字节 GB 太字节 TB
1 B = 8 b 1 KB = 1024 B 1 MB = 1024 KB 1 GB = 1024 MB 1 TB = 1024 GB
最小的数据单位:bit,最小的存储单元:byte
进制加减法
加法:逢n进1(两位相加>n则减n进一)
减法:借一当n
进制范围区间的值算法:大减小+1
1.2原反补移码
- 原码快速转补码(补码转原码)的方法:从右往左找第一个1,这个1左边的所有“数值为”按位取反
- 补码的合法表示范围比原码多一个负数
- [B]补->[-B]补:全部位按位取反、末位+1
- 补码->移码(只能整数):符号位取反
- ±0的移码和补码相同
- 补码的补码[[B]补]补就是原码
- 采用补码可以简化计算机运算部件的设计
1.3 浮点数的表示和运算
浮点数的表示
:N=M*RE,其中M表示尾数,E表示阶数,R表示基数
-
阶码常用补码或移码表示的定点整数,尾数常用原码或补码表示的定点小数
-
阶码表示范围,尾数表示精度
-
规格化表示要求将尾数的绝对值限定在区间[0.5,1]
-
定点表示法常分为定点整数和定点小数,小数点不需要占用存储位,只是表示而已
-
浮点数:当机器字长为n时,定点数的补码和移码可表示2n个数,而其原码和反码只能表示2n-1个数
-
如果浮点数的阶码(包括1位阶符)用R位的移码表示,尾数(包括1位数符)用M位的补码表示,则这种浮点数所能表示的数值范围如下:
浮点数的运算
运算步骤:
- 对阶:阶数更小的向阶数更大的对齐
- 尾数加减
- 规格化:左规:阶码减一;右规阶码加一
2.cpu组成
CPU主要由运算器、控制器、寄存器组和内部总线等部件组成。
1.运算器
:执行算术运算和逻辑运算的功能,由算术逻辑单元(ALU)、累加寄存器(AC)、数据缓冲器(DR)和状态条件寄存器(PSW)组成
算术逻辑单元(ALU):负责处理数据,实现数据的算术运算和逻辑运算
累加寄存器(AC):为ALU提供一个工作区,将被x数暂存在AC中
数据缓冲器(DR):作为CPU和内存、外部设备之间数据传送的中转站
状态条件寄存器(PSW):状态标志和控制标志
2.控制器
控制器用于控制整个 CPU 的工作,它决定了计算机运行过程的自动化。它不仅要保证程序的正确执行,而且要能够处理异常事件。
指令由操作码(加减乘除)和地址码(数)组成
指令寄存器(IR):CPU执行一条指令时,从内存到缓冲寄存器中,再送到IR暂存,ID根据IR的内容产生相关指令,所以操作码和地址码在IR
指令寄存器是对用户完全透明的
程序计数器(PC):内容是程序的一条指令的地址,取指令
地址寄存器(AR):保持当前CPU所访问的内存单元的地址,使用AR保存地址信息
指令译码器(ID):识别操作码
寻址:
- 立即寻址:操作数就包含在指令中。1
- 直接寻址:操作数存放在内存单元中,指令中直接给出操作数所在存储单元的地址。3
- 寄存器寻址:操作数存放在某一寄存器中,指令中给出存放操作数的寄存器名。2
- 寄存器间接寻址:操作数存放在内存单元中,操作数所在存储单元的地址在某个寄存器中。4
- 间接寻址:指令中给出操作数地址的地址。5
- 相对寻址:指令地址码给出的是一个偏移量(可正可负),操作数地址等于本条指令的地址加上该偏移量。
- 变址寻址:操作数地址等于变址寄存器的内容加偏移量。
3.CISC与RISC
4.流水线技术
指令流水线
1.顺序执行方式:T=n3t
2.一次重叠执行方式:T=3t+(n-1)2t=(1+2n)t
3.二次重叠执行方式:T=3t+(n-1)t=(2+n)t
性能
1.吞吐率
吞吐率是指在单位时间内流水线所完成的任务数量,或是输出结果的数量。设任务数为n;处理完成n个任务所用的时间为Tk
2.加速比
加速比:完成同样一批任务,不使用流水线所用的时间与使用流水线所用的时间之比,设任务数为n;处理完成n个任务所用的时间为Tk
3.效率
:流水线的设备利用率称为流水线的效率
5.层次化存储
- SRAM(静态随机存储器)如:cache
- DRAM(动态随机存储器)如:主存
- RAM(读/写存储器)
- ROM(只读存储器)
- PROM(可编程的只读存储器)
按寻址方式分类
- 随机存储器(RAM)如:内存
- 顺序存储器(SAM)如:磁带
- 直接存储器(DAM)如:磁盘
按访问方式分类
相联存储器是一种按内容访问的存储器
笔记
- 虚拟存储器由主存-辅存两级存储器组成
- 时间局部性:现在访问的地址,不久后也很有可能被再次访问;
- 空间局部性:现在访问的地址,其附近的地址也很有可能即将被访问
cache
高速缓存对于程序员来说时透明的,cache与主存地址的映射时由硬件自动完成的
地址映像方法
1.直接映像 冲突大,导致重写
2.全相联映像 调入任何一块空间 冲突最小
3.组相联映像 组与组映射 冲突较小
中断
中断向量:提供中断服务程序的入口地址
中断响应时间:发出中断请求开始,到进入中断服务程序
保存现场:返回执行源程序
6.I/O数据传输控制方式
- 程序查询方式
-
CPU和 I/O(外设)只能串行工作 CPU需要一直轮询检查,长期处于忙等状态。CPU 利用率低
-
一次只能读/写一个字
-
由 CPU 将数放入内存
- 中断驱动方式
- I/O 设备通过中断信号主动向 CPU 报告 I/O 操作已完成
- CPU 和 I/O(外设)可并行工作
- CPU 利用率得到提升
- 一次只能 读/写 一个字
- 由 CPU 将数据放入内存
- 直接存储器方式(DMA)
- CPU 和 I/O(外设)可并行工作
- 仅在传送数据块的开始和结束时才需要 CPU 的干预
- 由外设直接将数据放入内存
- 一次读写的单位为”块“而不是字
总线
计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。
概念:
- PCI总线是并行内总线,SCSI总线是并行外总线
7.可靠性分析
计算机可靠性模型
串联系统:
$R= R₁R₂···Rn$
并联系统:
$R = 1-(1-R₁)(1-R₂)···(1-Rn)$
8.校验方法
码距:指一个编码系统中任意两个合法编码之间至少有多少个二进制位不同,码距为2只能检错,码距大于等于3,才有可能纠错
- 奇偶校验码:只能检测奇数出错,偶数出错无法检测,只能检错,不能纠错,码距为2
- 海明码:利用奇偶性来检错和纠错的校验方法
- 数据位是n位,校验位是k位,n和k的关系:2k-1>=n+k
- 循环冗余校验码: 可以检错,但不能纠错,采用模二除法运算的只有循环几余检验CRC。其编码长度为k+r
易错概念:
1.奇偶校验码:
- 所有奇/偶数位出错;eg:1010:两个1分别是:1和3的奇数;两个0分别是2和4的偶数位
- 奇/偶数个数据位出错,这种说法才是正确的
9.计算机性能指标
2.操作系统
地位
杂记:
-
CPU是在一个总线周期结束时响应DMA请求的。
-
CPU依据指令周期的不同阶段来区分在内存中以二进制编码形式存放的指令和数据
-
程序计数器的作用是保存待读取指令在内存中的地址,累加器是算逻运算单元中用来暂存源操作数和计算结果的寄存器,指令寄存器暂存从内存读取的指令,地址寄存器暂存要访问的内存单元的地址。
-
字长为32位即要求数据总线的宽度为32位,因此地址总线和数据总线的宽度都为32
-
为了便于实现多级中断嵌套,使用堆栈来保护断点和现场最有效
-
在二进制情况下,溢出时符号位将变反,即两个正数相加,结果的符号位是负数,或者两个负数相加,结果的符号位是正数
-
存储器数据寄存器(MDR) 和存储器地址寄存器(MAR)用于对内存单元访问时的数据和地址暂存,也是由系统使用的,程序员不能访问。
程序计数器 (PC) 用于存储指令的地址,CPU根据该寄存器的内容从内存读取待执行的指令,程序员可以访问该寄存器
进程管理
1.程序与进程
1.程序顺序执行
主要特征包括:顺序性、封闭性和可再现性。
1.1前趋图
信号量按p下标顺序填写
2.程序并发执行
(1)失去了程序的封闭性。
(2)程序和机器的执行程序的活动不再一一对应。
(3)并发程序间的相互制约性。
2.1 前驱图
I2与C1并行执行;I3、C2与P1并行执行:C3与P2并行执行
2.同步与互斥
同步是合作进程间的直接制约问题,互斥是申请临界资源进程间的间接制约问题
3.信号量与PV操作
3.1整型信号量与 PV 操作
信号量是一个整型变量,根据控制对象的不同被赋予不同的值信号量分为如下两类:
1.公用信号量。实现进程间的互斥,初值为 1 或资源的数目
2.私用信号量。实现进程间的同步,初值为 0 或某个正整数
信号量 S的物理意义:S>=0 表示某资源的可用数若; S<0,则其绝对值表示阻塞队列中等待该资源的进程数。
p操作表示申请一个资源,v操作表示释放一个资源
P 操作的定义:S=S-1,则执行 P 操作的进程继续执行;若 S<0,则置该进程为阻塞状态(因为无可用资源),并将其插入阻塞队列
V 操作定义:S=S+1,若 S>0,则执行 V操作的进程继续执行;若S<=0,则从阻塞队列中唤醒一个进程,并将其插入就绪队列,然后执行 V操作的进程继续。
3.2PV操作实现进程互斥
互斥情况,pv成对存在
3.2PV操作实现进程同步
最典型的同步问题:单缓冲区的生产者和消费者
4.死锁问题
当有 n 个进程,m个资源,且每个进程所需要的资源数为k,并且系统采用的分配策略是轮流地为每个进程分配资源时,判断是否发生死锁的公式如下:
$$
m >= n * (k-1)+1
$$
死锁的处理策略主要有4种:鸵鸟策略(即不理睬策略)、预防策略、避免策略和检测与解除死锁。
5.进程资源图
6.线程
线程作为调度和分配的基本单位,进程作为独立分配资源的单位
7.三态模型
在多道程序系统中,进程在处理器上交替运行,状态也不断地发生变化,因此进程一般有3种基本状态:运行、就绪和阻塞。
进程 | CPU | 资源 |
---|---|---|
运行 | √ | √ |
就绪 | × | √ |
阻塞 | × | × |
3.分页存储管理
4.段页式存储
5.单缓冲和双缓冲区
5.1单缓冲区
(T+M)*n+C
5.2双缓冲区
T*n+M+C
4.磁盘调度
- 先来先服务(FCFS) :根据进程请求访问磁盘的先后次序进行调度。
- 最短寻道时间优先(SSTF):该算法选择这样的进程,其要求访问的磁道与当前磁头所在的磁道距离最近,使得每次的寻道时间最短。
- 扫描算法/电梯调度算法(SCAN):扫描算法不仅考虑到要访问的磁道与当前磁道的距离,更优先考虑的是磁头的当前移动方向。
- 循环扫描算法/单向扫描调度算法(CSCAN):为了减少这种延迟,算法规定磁头只做单向移动。
旋转调度算法
6.多级索引地址
https://www.bilibili.com/video/BV1AY411E7GC?p=83&vd_source=f835e71c23aafc81ef096ae00d5c28d4
5.文件管理
5.1文件目录
5.2目录结构
全文件名:绝对路径+文件名
绝对路径:从始至终
相对路径:当前路径开始
5.3位示图
:用二进制的一位来表示一个物理块的使用情况
这种方法的主要特点是位示图的大小由磁盘空间的大小(物理块总数)决定位示图的描述能力强,适合各种物理结构。
算物理块号:n*字的位数(32)~~(n+1)*字的位数-1
杂记:
1.在同一进程中的各个线程都可以共享该进程所拥有的资源,
- 如访问进程地址空间中的每一个虚地址,
- 访问进程所拥有的已打开文件、定时器、信号量机构等,
- 但是不能共享进程中某线程的栈指针
3.数据库系统
结构数据模型
:层次、网状、关系和星状
关系模型:采用二维表结构表达实体类型及实体间联系的数据模型,多个关系模式组成,而一个关系模式是对关系的描述(关系名(属性1,…,属性n)),一个关系是一个二维表
关系模式
R<U,D,dom,F>:
:R是关系名;
U是一组属性;
F为属性组U上的一组数据依赖;A->B: A决定B或者B依赖A
1.函数依赖
(1)函数依赖。则称X函数决定Y或Y函数依赖于X,记作X→Y。
(2)非平凡的函数依赖。如果X→Y,但Y¢X,则称X→Y是非平凡的函数依赖。一般情况下,总是讨论非平凡的函数依赖。
(3)平凡的函数依赖。如果X→Y,但Y包含X,则称XY是平凡的函数依赖。
(4)完全函数依赖。在R(U)中,如果X→Y,并且对于X的任何一个真子集X'都有X'不能决定 Y,则称Y对X完全函数依赖,记作X-→Y。
例如,给定一个学生选课关系SC(Sno,Cno,G),可以得到F=((Sno,Cno)→G),对(Sno,Cno)中的任何一个真子集Sno或Cno都不能决定G,所以,G完全依赖于Sno、Cno。
(5)部分函数依赖。如果X→Y,但Y不完全函数依赖于X,则称Y对X部分函数依赖◇记作XY。部分函数依赖也称为局部函数依赖。
(6)传递依赖。在R(U,F)中,如果X→Y,Y不包含于X,Y→Z,则称Z对X传递依赖,记作:X->Z。
(7)函数依赖的公理系统(Armstrong公理系统)。设关系模式R(U,F),其中U为属性集,F是U上的一组函数依赖,那么有以下推理规则。
A3传递律:若X→Y,Y→Z为F所蕴涵,则X→Z为F所蕴涵。
根据上述3条推理规则又可推出下述3条推理规则。
合并规则:若X→Y,X→Z,则X→YZ为F所蕴涵。
伪传递率:若X→Y,WY→Z,则XW→Z为F所蕴涵。
分解规则:若X→Y,Z(Z是Y的子集)包含于Y,则X→Z为F所蕴涵。
2.属性闭包计算
找函数依赖中左边没有被决定的属性(就是右边没有出现过的属性),大概率是候选键
候选码(或候选键):属性或属性组合,其值能够唯一地标识一个元组。
主码(或主键):在一个关系中可能有多个候选码,从中选择一个作为主码。
主属性:包含在任何候选码中的属性称为主属性,不包含在任何候选码中的属性称为非码属性。
外码(或外键):如果一个关系(sc)中的属性(sno)或属性组并非该关系(sc)的码,但它们是另外一个关系(s)的码,则称其为该关系的外码。
全码: 关系模式的所有属性组是这个关系模式的候选码,称为全码。
超码(超键):一个包含码的属性集称为超码,例如学号是码,则(学号,姓名)就是一个超码。
数据库的三级模式结构
- 概念模式(模式) -> 基本表
- 外模式(用户模式或子模式) -> 视图
- 内模式(存储模式) -> 存储文件
两级映像
数据库系统在三级模式之间提供了两级映像:模式/内模式映像、外模式/模式映像。
保证了数据库中的数据具有较高的逻辑独立性和物理独立性。
- 模式/内模式映像:实现了概念模式和内模式之间的相互转换。
- 外模式/模式映像:实现了外模式和概念模式之间的相互转换。
- 数据的物理独立性:需要修改概念模式和内模式之间的映像。
- 数据的逻辑独立性:需要修改外模式和概念模式之间的映像。
完整性约束
实体完整性:关系中主码不能为空,主码中属性即主属性不能取空置值
参照完整性:外码
关系代数
(1)关系的并
关系R和关系S的所有元组合并,再删去重复的元组,组成一个新关系,称为R和S 的并,记为RUS。
(2)关系的差
关系R和关系S的差是由属于R而不属于S的所有元组组成的集合,即关系R中删去与关系S中相同的元组,组成一个新关系,记为R-S。
(3)关系的交
关系R和关系S的交是由既属于R又属于S的元组组成的集合,即在两个关系R与S 中取相同的元组,组成一个新关系,记为RNS。有RNS=R-(R-S)成立。
(4)笛卡尔积
两个关系R和S的笛卡尔积记为RxS;列:n+m;行:nxm
投影和选择
投影(Projection)
投影运算是从关系的垂直方向进行运算,
选择(Selection)
选择运算是从关系的水平方向进行运算,
连接
1.theat连接
theat连接可以由基本的关系运算笛卡尔积和选取运算导出
2.等值连接
3.自然连接
:除去重复属性的等值连接,它是连接运算的一个特例
具体计算过程:
①计算RxS;
②设R和S的公共属性(多个公共属性并且&&)是A1…Ak,挑选RxS中满足R.A1=S.A1…R.Ak=S.Ak的那些元组;
③去掉S.A1…S.Ak这些列(保留RA1…RAk)。
4.左外连接(右表不匹配填空null)、右外连接(左表不匹配填空null)
全外连接:左外连接和右外连接拼接
转sql:
投影和选择:sql语言不支持列的序号
笛卡尔积:RxS->from R,S
自然连接: 选择和投影的复合使用
SQL语言的分类
DDL (Data Definition Language,
数据定义语言):在数据库系统中,每一个数据库、数据库中的表、视图和索引等都是数据库对象。要建立和删除一个数据库对象,都可以通过 SQL 语言来完成。DDL 包括CREATE、ALTER和DROP等。DML(Data Manipulation Language,数据操纵语言):DML是指用来添加、修改和删除数据库中数据的语句,包括 INSERT(插入)、DELETE(删除)和 UPDATE (更新)等。
DQL(Data Query Language,数据查询语言):查询是数据库的基本功能,查询操作通过SQL数据查询语言来实现,例如,用SELECT 查询表中的内容。
DCL(Data Control Language,数据控制语言):DCL包括数据库对象的权限管理和事务管理等。
投影查询
选择查询
排序查询
order by
聚合查询和数据分组
1.条件有聚合函数要用having,where后面不能跟聚合函数
2.select后有几个字段,若要分组(group by)则就要将前面几个字段分组
3.当 WHERE子句、GROUP BY子句、HAVING子句和聚合函数同时出现在一个查询中时,
SELECT命令的执行顺序如下:
①执行WHERE子句,从表中选取行。
②由GROUP BY对选取的行进行分组。
③执行聚合函数。
④执行HAVING子句选取满足条件的分组。
内连接和外连接
子查询
先子查询后主查询
一般子查询
相关子查询
exists子查询
查询结果并、交和差运算
union:并;intersect:交;except:差
SQL控制语句
1.授权的语句格式
GRANT<权限>[,<权限>]…[ON<对象类型><对象名>]TO<用户>[,<用户>]…[WITH GRANT OPTION]
eg;GRANT ALL PRIVILEGES ON TABLE S.PJ TO Userl, User2;
with grant option:获得了权限的用户还可以将权限赋给其他用户
视图
1.视图的创建
语句格式:
CREATE VIEW 视图名(列表名)AS SELECT 查询子句
[WITH CHECK OPTION]
注意在视图的创建中必须遵循以下规定。
1)子查询可以是任意复杂的SELECT 语句,但通常不允许含有 ORDER BY子句和DISTINCT短语。
2)WITH CHECK OPTION 表示对UPDATF、INSERT、DELETE 操作时保证更新、插入或删除的行满足视图定义中的谓词条件(即子查询中的条件表达式)
3)"WITH GRANT OPTI ON"子句,那么获得了权限的用户,还可以将该权限赋给其他用户。
2.视图的删除
语句格式:DROP VIEW 视图名
索引
内模式:定义所有的内部记录类型、索引和文件的组织方式
规范化
候选码中包含的属性称为主属性、不包含在候选码中的属性称为非主属性。若候选码多于一个,可以选定其中的一个为主码。
1NF(第一范式)
定义:若关系模式 R 每一个分量是不可再分的数据项,则关系模式 R 属于第一范式。
1NF 不能排除数据冗余和更新异常(修改、插入:实体完整性;删除异常)等问题,因为其中可能存在部分函数依赖。
2NF(第二范式)
定义:若关系模式 R∈1NF,且每一个非主属性完全依赖于候选码,则关系模式 R∈2NF。
当 1NF 消除了非主属性对候选码的部分函数依赖,则称为 2NF。
可能存在数据冗余和更新异常等问题,因为其中可能存在传递依赖。
3NF(第三范式)
2NF 消除了非主属性对码的传递函数依赖,则称为 3NF。
属于 3NF 的关系模式 R 可能存在主属性对码的部分依赖和传递依赖。
BC范式(BCNF)
3NF消除了主属性对候选码的部分依赖和传递依赖
由BCNF的定义可以得到结论,一个满足BCNF的关系模式有:
1)所有非主属性对每一个码都是完全函数依赖。
2)所有的主属性对每一个不包含它的码,也是完全函数依赖。
3)没有任何属性完全函数依赖于非码的任何一组属性。
一个满足BCNF的关系模式R已消除了插入和删除异常。
判断部分函数依赖技巧
- 找出候选码
- 若候选码由多个属性组成,很可能存在部分依赖,若候选码只有单个属性组成,不可能存在部分依赖
数据库设计
1.需求分析阶段
:收集用户需求,确定系统边界,逻辑设计和物理设计以及应用程序的设计以我们的需求分析的一个结果为依据
需求分析的成果是系统需求说明书,主要包括数据流图、数据字典和需求说明文档
2.概念设计阶段
:选择局部应用、逐一设计分E-R图(属性、命名和结构冲突)和E-R图合并(解决冲突)
分E-R图之间的冲突分为
①属性冲突,同一属性可能会存在于不同的分E-R图,由于设计人员不同或是出发点不同,对属性的类型、取值范围和数据单位等可能会不一致
②命名冲突。相同意义的属性在不同的分 E-R 图上有着不同的命名,或是名称相同的属性在不同的分E-R图中代表着不同的意义,这些也要进行统一。
③结构冲突。同一实体在不同的分E-R图中有不同的属性,同一对象在某一分 E-R图中被抽象为实体,而在另一分E-R图中又被抽象为属性,需要统一。
E-R图
1.实体和弱实体
在E-R模型中,实体用矩形表示,通常矩形框内写明实体名。实体是现实世界中可以区别于其他对象的"事件"或"物体"。
弱实体:即一个实体的存在必须以另一个实体为前提。eg:家属的存在必须以职工为前提
2.联系
在E-R模型中,联系用菱形表示,通常菱形框内写明联系名,并用无向边分别与有关实体连接起来,同时在无向边旁标注上联系的类型(1:1、1:n或m:n)。
3.属性
属性是实体某方面的特性。E-R模型中的属性有以下分类:
(1)简单属性和复合属性。简单属性是原子的、不可再分的,复合属性可以细分为更小的部分(即划分为别的属性)。
(2)单值属性和多值属性。在前面所举的例子中,定义的属性对于一个特定的实体都只有单独的一个值。
(3)NULL属性。当实体在某个属性上没有值或属性值未知时,使用NULL值,表示无意义或不知道。
(4)派生属性。派生属性可以从其他属性得来。那么"工作年限"的值可以由当前时间和参加工作时间得到。这里,"工作年限"就是一个派生属性。
3.逻辑设计阶段
:E-R图->关系模型,规范化
1)实体向关系模式的转换
将E-R 图中的实体逐一转换成为一个关系模式,实体名对应关系模式的名称,实体的属性转换成关系模式的属性,实体标识符就是关系的码(键)。
2)联系向关系模式的转换
E-R图中的联系有3种:一对一联系(1:1)、一对多联系(1:n)和多对多联系(m:n),针对这3种不同的联系,转换方法如下。
(1)一对一联系的转换。一对联系有两种方式向关系模式进行转换。一种方式是将联系转换成一个独立的关系模式,关系模式的名称取联系的名称,关系模式的属性包括该联系所关联的两个实体的码及联系的属性,关系的码取自任一方实体的码;
另一种方式是将联系归并到关联的两个实体的任一方,给待归并的一方实体属性集中增加另一方实体的码和该联系的属性即可,归并后的实体码保持不变。
(2)一对多联系的转换。一种方式是将联系转换成一个独立的关系模式,关系模式的名称取联系的名称,关系模式的属性取该联系所关联的两个实体的码及联系的属性,关系的码是多方实体的码;
另一种方式是将联系归并到关联的两个实体的多方,给待归并的多方实体属性集中增加一方实体的码和该联系的属性即可,归并后的多方实体码保持不变。
(3)多对多联系的转换。多对多联系只能转换成一个独立的关系模式,关系模式的名称取联系的名称,关系模式的属性取该联系所关联的两个多方实体的码及联系的属性,关系的码是多方实体的码构成的属性组。
多对多
事务管理
事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(urability)。这4个特性也称事务的ACID性质。
备份方法
恢复的基本原理是"建立数据冗余"(重复存储)。建立冗余数据的方法是进行数据转储和登记日志文件。数据的转储分为静态转储和动态转储、海量转储和增量转储、日志文件。
(1)静态转储和动态转储。静态转储是指在转储期间不允许对数据库进行任何存取、修改操作:动态转储是在转储期间允许对数据库进行存取、修改操作。因此,转储和用户事务可并发执行。
(2)海量转储和增量转储。海量转储是指每次转储全部数据:增量转储是指每次只转储上次转储后更新过的数据。
(3)日志文件。在事务处理的过程中,DBMS把事务开始、事务结束以及对数据库的插入、删除和修改的每一次操作写入日志文件。当系统正常运行时,按一定的时间间隔,把数据库缓冲区内容写入数据文件;一旦发生故障,DBMS的恢复子系统利用日志文件撤销事务对数据库的改变,回退到事务的初始状态。因此,DBMS利用日志文件来进行事务故障恢复和系统故障恢复,并可协助后备副本进行介质故障恢复。
据库
恢复
事务恢复有以下3个步骤。
(1)反向扫描文件日志(即从最后向前扫描日志文件),查找该事务的更新操作。
(2)对事务的更新操作执行逆操作。
(3)继续反向扫描日志文件,查找该事务的其他更新操作,并做同样的处理,直到事务的开始标志。
并发控制技术
并发控制的主要技术是封锁。基本封锁的类型有排它锁(简称X锁或写锁)和共享锁(简称S锁或读锁)。
1)封锁
(1)排它锁。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他事务都不能再对 A 加任何类型的锁,直到T释放A上的锁。
(2)共享锁。若事务T对数据对象A加上S锁,则只允许T读取A,但不能修改A,其他事务只能再对A加S锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T 释放A上的S锁之前不能对A进行任何修改。
分布式数据库
分片透明:指用户或应用程序不需要知道逻辑上访问的表具体是怎么分块存储的
复制透明:指采用复制技术的分布方法,用户不需要知道数据是复制到哪些节点,如何复
制的。
位置透明:指用户无须知道数据存放的物理位置
逻辑透明:指用户或应用程序无需知道局部场地使用的是哪种数据模型
共享性:指数据存储在不同的结点数据共享
自治性:指每结点对本地数据都能独立管理
可用性:指当某一场地故障时,系统可以使用其他场地上的副本而不至于使整个系统瘫痪
分布性:指数据在不同场地上的存储
试题二(15分)
数据库设计:
- 需求分析
- 概念模型 问题1 :E-R图
- 逻辑结构 问题2:关系模式
- 题型结构
- 说明
- 需求分析
- 概念模型(E-R图)
- 逻辑结构
- 问题
实体
实体用矩形表示,通常矩形框内写明实体名。
联系
联系用菱形表示,通常菱形框内写明联系名。
1:1
:
- 转关系模式
- 归并到任一方实体
1:*
:
- 转关系模式
- 归并到多方实体,把1方实体主键加到多方实体的关系模式属性
*:*
:转关系模式
属性
- 简单属性和复合属性。简单属性是原子的、不可再分的,复合属性可以细分为更小的部分(即划分为别的属性)。
- 单值属性和多值属性。单独的一个值。
- 派生属性。派生属性可以从其他属性得来。
实体-联系方法
超类和子类实体
职员实体是飞行员、机械师和管理员实体的超类,超类和子类之间具有继承关系。
超类和子类的转换
超类、子类实体都可转换为一个关系,并将超类实体的主码加到子类实体中。
基本概念
- as:取别名 eg:as xx;在视图中,as:是代表紧跟其后的select语句
计算机网络
网络的设备
- 物理层:中继器、集线器(多端口的中继器)
- 数据链路层:网桥、交换机(多端口的网桥)
- 网络层:路由器
- 应用层:网关
√是可隔离,×是不可隔离
广播域 | 冲突域 | |
---|---|---|
物理层 | × | × |
数据链路层 | × | √ |
网络层 | √ | √ |
协议簇
网络层协议
1.IP
IP所提供的服务通常被认为是无连接的和不可靠的
把差错检错和流量控制之类的服务授权给其他的各层协议
2.ARP和RARP
ARP的作用是将IP地址转换为物理地址(MAC地址),RARP的作用是将物理地址转换为IP地址
广播发送请求(ARP Request);单播发送响应(ARP Response)
传输层协议
- TCP(三次握手)
可靠传输、连接管理、差错校验和重传、流量控制、拥塞控制、端口寻址,其中流量控制采用的是:可变大小的滑动窗口协议。
- UDP(不可靠,无连接)
- DHCP(动态主机配置协议)
DHCP协议的功能是:集中的管理、分配IP 地址,使网络环境中的主机动态的获得IP 地址、Gateway 地址、DNS 服务器地址等信息,并能够提升地址的使用率。
DHCP 客户端可以从 DHCP 服务器获得本机IP 地址、DNS 服务器地址、DHCP 服务器地址和默认网关的地址等.
Windows 无效地址: 169.254.XX Linux 无效地址:0.0.0.0
169.254.XX是 Windows 系统在 DHCP 信息租用失败时自动给客户机分配的 IP 地址
电子邮件服务(C/S 模式)
- SMTP:传输 ASCII 文本和文字附件
- MIME:邮件附件扩展类型
- PEM:私密邮件保护协议
- POP:POP2 和 POP3(接收邮件)
浏览器
-
输入网址后:DNS 域名查询的次序是:本地的 hosts 文件一>本地 DNS 缓存一>本地 DNS 服务器-->根域名服务器
-
主域名服务器在接收到域名请求后,查询顺序是本地缓存、本地 hosts 文件、本地数据库、转发域名服务器。
-
HTTP请求过程:
-
当在 Web 浏览器的地址栏中输入某 URL 并按下回车
-
对 URL 进行 DNS 域名解析,得到对应的IP地址
-
根据这个 IP,找到对应的服务器,进行三次握手
-
建立TCP 连接后发起 HTTP 请求
-
服务器响应 HTTP 请求,浏览器得到 HTML代码:
-
通信完成,断开 TCP 连接
-
浏览器解析收到的数据并显示
-
3.IP地址和子网划分
在IP地址中,全0代表的是网络,全1代表的是广播
IP地址
A类地址
A 类网络地址占有 1 个字节(8 位),定义最高位为 0 来标识此类地址,余下7 位为真正的网络地址,支持 1~126 个网络。后面的 3 个字节(24 位)为主机地址,共提供 224-2 (全0的主机地址和全1的广播地址)个端点的寻址。A 类网络地址第一个字节的十进制值为 000~127。
B类网络
B 类网络地址占有两个字节,使用最高两位为 10 来标识此类地址,其余 14 位为真正的网络地址,主机地址占后面的两个字节 (16 位),所以 B 类全部的地址有(214-2) X (216-2)=16382X 65534 个。B 类网络地址第一个字节的十进制值为 128~191。
C类网络
C 类网络地址占有 3 个字节,它是最通用的 Internet 地址。使用最高三位为 110 来标识此类地址,其余 21 位为真正的网络地址,因此 C 类地址支持 221-2 个网络。主机地址占最后 1个字节,每个网络可多达 28-2 个主机。C 类网络地址第一个字节的士进制值为 192~223。
子网掩码
在一个字段内,1的出现表明一个字段包含所有或部分网络地址,0表明主机地址位置
4.常用的网络命令
windows命令
ipconfig/release: DHCP 客户端手工释放 IP 地址
ipconfig/flushdns: 清除本地 DNS 缓存内容
ipconfig/displaydns:显示本地 DNS 内容
ipconfig/registerdns: DNS 客户端手工向服务器进行注册
ipconfig: 显示所有网络适配器的IP 地址、子网掩码和缺省网关值ipconfig/al: 显示所有网络适配器的完整 TCP/IP 配置信息,包括 DHCP 服务是否已启动
ipconfig/renew: DHCP 客户端手工向服务器刷新请求(重新申请IP 地址)
Linux命令
5.URL
协议名://主机名.域名.域名后缀.域名分类/目录/网页文件
IPv6 128位地址空间、IPv4 32位地址空间。
路由
当 Windows 服务器收到一个IP数据包时,先查找主机路由,这些路由查找再查找网络路由(直连网络和远程网络),失败时,最后才查找默认路由
管理距离:如果路由器收到了由多个路由协议转发的、关于某个目标的多条路由路由的管理距离,并采用管理距离小的路由来源提供的路由信息。
HTML
程序设计语言概述
1.程序设计语言的基本概念
解释器:翻译源程序时不生产独立的目标程序。
- 解释程序和源程序要参与到程序的运行过程中。
编译器:翻译时将源程序翻译成独立保存的目标程序。
- 机器上运行的是与源程序等价的目标程序,源程序和编译程序都不再参与目标程序的运行过程。
许多程序设计语言规定,程序中的数据必须具有类型,其作用是:
- 便于为数据合理分配存储单元
- 便于对参与表达式计算的数据对象进行检查
- 便于规定数据对象的取值范围及能够进行的运算
2.程序设计语言的基本成分
函数定义
函数的定义包括两部分:函数首部和函数体。函数的定义描述了函数做什么和怎么做。
函数定义的一般形式为:
返回值的类型 函数名(形式参数表) //函数首部 函数名(实参表);
{
函数体;
}
(1)值调用(Call by Value)。若实现函数调用时将实参的值传递给相应的形参, 则称为是传值调用。在这种方式下形参不能向实参传递信息。
(2)引用调用(Call by Reference)。引用是 C++ 中引入的概念,当形式参数为引用类型时,形参名实际上是实参的别名,函数中对形参的访问和修改实际上就是针对相应实参所做的访问和改变。
传值调用:
将实参的值传递给形参,实参可以是变量、常量和表达式。
不可以实现形参和实参间双向传递数据的效果。
传引用(地址)调用:
将实参的地址传递给形参,形参必须有地址,实参不能是常量(值,无存储单元),表达式。可以实现形参和实参间双向传递数据的效果,即改变形参的值同时也改变了实参的值。
3.编译程序基本原理
编译方式:词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成
解释方式:词法分析、语法分析、语义分析
编译器和解释器都不可省略词法分析、语法分析、语义分析且顺序不可交换
即词法分析、语法分析、语义分析是必须的。
编译器方式中中间代码生成和代码优化不是必要,可省略。
即编译器方式可以在词法分析、语法分析、语义分析阶段后直接生成目标代码
符号表:不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入符号表中。记录源程序中各个字符的必要信息,以辅助语义的正确性检查和代码生成。
1)词法分析
输入:源程序
输出:记号流
词法分析阶段的主要作用是 分析构成程序的字符及由字符按照构造规则构成的符号,是否符合程序语言的规定。
2)语法分析
输入:记号流
输出:语法树(分析树)
语义分析阶段可以发现程序中所有的语法错误
语法分析阶段的主要作用是 对各条语句的结构进行合法性分析,分析程序中的句子结构是否正确。
自上而下语法分析法:递归下降分析法和预测分析法
自下而上语法分析法:移进-归约分析法
3)语义分析
输入:语法树(分析树)
语义分析阶段的主要作用是进行类型分析和检查
语义分析阶段不能发现程序中所有的语义错误
语义分析阶段可以发现静态语义(语法制导翻译)错误,不能发现动态语义错误,动态语义错误(除0或者死循环)运行时才能发现
4)中间代码生成
常见的中间代码有:后缀式、三地址码、三元式、四元式和树(图)等形式。
中间代码与具体的机器无关(不依赖具体的机器),可以将不同的高级程序语言翻译成同一种中间代码。
中间代码可以跨平台。
因为与具体的机器无关,使用中间代码有利于进行与机器无关的优化处理和提高编译程序的可移植性。
最常用的一种中间代码
是与汇编语言的指令非常相似的三地址码,其实现方式常采用四元式,后缀式、树等形式的中间代码。
6)目标代码生成
目标代码生成阶段的工作与具体的机器密切相关
寄存器的分配工作处于目标代码生成阶段
正规式
*:代表出现0次或多次
有限自动机
是词法分析的一个工具,它能正确地识别正规集
确定的有限自动机( DFA ):对每一个状态来说识别字符后转移的状态是唯一的
不确定的有限自动机( NFA ):对每一个状态来说识别字符后转移的状态是不确定的
上下文无关文法
:是大多数程序设计语言的语法规则
大写字母是非终结符号,小写字母是终结符号
中缀和后缀(逆波兰式)表达式
中缀表达式:a?b
后缀表达式:ab?
优先级(优先级相同,从右往左):1.() 2.x / 3.+ -
中缀转后缀:a?b->ab?
后缀转中缀:从左往右找操作数(栈的方式,数字入栈,遇到操作数,先把入了栈的数字拿出来放在右边,在往栈里拿一个数字放在左边,把操作数放在中间,然后再把这个组合当作一个整体放回栈里面,依次反复)
语法树中后序遍历
就是数据结构的中后序遍历
基本概念:
-
常量可分为:整型常量、字符常量和字符串常量
-
静态类型指编译器在编译源程序期间执行类型检查,声明了一个变量之后,不能改变其类型的语言,是静态语言。
-
动态类型指编译器 (虚拟机) 在程序运行时执行类型检查。简单地说,在声明了一个变量之后,能够随时改变其类型的语言,是动态语言。
-
弱类型相对于强类型来说类型检查更不严格;eg:python语言
-
汇编程序先将源程序中的伪指令翻译成机器代码,然后再翻译汇编指令语句
-
链表中的结点空间需要程序员根据需要申请和释放,因此,数据空间应采用堆存储分配策略。
-
栈区和堆区也称为动态数据区。全局变量的存储空间在静态数据区。
-
编译过程中为变量分配存储单元所用的地址是逻辑地址,程序运行时再映射为物理地址。
-
在翻译程序的过程中为数据合理分配存储单元;
对参与表达式计算的数据对象进行检查;
规定数据对象的取值范围及能够进行的运算 -
LISP是第一个声明式系内函数式程序设计语言,有别于命令式系内过程式的C、Fortran和面向对象的Java、C#等结构化程序设计语言。
数据流图
数据流图的基本图形元素包括数据流、加工、数据存储和外部实体。
外部实体:当前系统之外的人、物、外部系统
人:学生、老师、员工、主管、医生、客户、供应商......
物:传感器、控制器、单车、车辆、采购部门......
外部系统:支付系统、车辆交易系统、库存管理系统、道闸控制系统......
数据存储:存储数据和提供数据,存储加工的输出数据和提供加工的输入数据
- 例子:客户表、订单表、学生表
- 巴士列表文件、维修记录文件、课表文件
加工:将输入数据处理后得到输出数据
- 一个加工至少有一个输入数据流和一个输出数据流
- 加工只有输入没有输出称为:黑洞
- 加工只有输出没有输入称为:白洞
- 加工的输入数据不足以产生输出数据:灰洞
数据流的起点或终点必须有一个是加工
问题一(易)
找实体名称
写法: E1 :病人 E2:护理人员 E3:医生
问题二(易)
找数据存储名称
写法:D1:XX 表 XX 文件
D1:销售订单表 D2:库存表 D3:生产计划表 D 4:配方表 D5:采购订单表
问题三(难)(15~20分钟)
题型:
- 根据说明和图中术语,补充图1-2中缺失的数据流及其起点和终点(三条即可)。
三个解题方法
- 父图子图平衡
- 加工既有输入数据流也有输出数据流
- 数据守恒
答题格式和注意事项
数据流名称:生产计划
① 起点:D3 终点:3
②起点:生产计划表 终点:生产
如何保持数据流图平衡
父图中加工的输入输出数据流必须与子图中的输入输出数据流在数量上和名字上相同;
父图中的一个输入(输出)数据流对应子图中几个输入(输出)数据流,而子图中组成这些数据流的数据项全体正好是父图中的这一条数据流。
知识产权
著作权
:指作者对其创作的作品享有的人身权和财产权;
人身权:包括发表权(时间受限)、署名权、修改权和保护作品完整权(时间不限)
财产权:除人身权外的权都是财产权
地域性
计算机软件著作权
计算机软件著作权的主体和客体
主体:享有著作权的人
- 《中华人民共和国著作权法》和《计算机保护条例》
客体:指著作权法保护的计算机软件著作权的范围(受保护的对象)
客体分为:
- 计算机程序:源程序和目标文件
- 计算机软件的文档:程序设计说明书、流程图和用户手册
职务作品
:工作所需得出的项目或者使用单位的设备完成的作品都归单位所有,本人只享有署名权
委托开发
:有合同就按合同走,无书面合同著作权归受委托方享有
计算机软件著作权的侵犯
商业秘密
专利权的申请
:先申请先得,同一天申请则协商
商标权
注册后10年有效期限,在期限到期前六个月可以续展后又是10年有效期
商标权注册
谁先注册商标权归谁,同一天注册谁先使用商标权归谁
面向对象技术
面向对象基础
面向过程和面向对象
面向对象 = 对象(Object)+ 分类(Classification)+ 继承(Inheritance)+通过消息的通信
类
:实体类、接口类(边界类)和控制类。实体类的对象表示现实世界中真实的实体。接口类(边界类)的对象为用户提供一种与系统合作交互的方式,分为人和系统两大类,控制类的对象用来控制活动流,充当协调者。
对象
对象通常可由对象名、属性和方法 3 个部分组成。
消息
对象之间进行通信的一种构造叫作消息。当一个消息发送给某个对象时,包含要求接收对象去执行某些活动的信息。接收到信息的对象经过解释,然后予以响应。这种通信机制称为消息传递。发送消息的对象不需要知道接收消息的对象如何对请求予以响应。
例如,假设ml是类Manager的一个实例(或对象),当外界要求把这个对象所代表的那位经理的级别改变为2时,就应以下面的方式向这个对象发出一条消息:ml.ChangeLevel(2);
重载
方法名相同 参数个数不同/参数类型不同/参数类型顺序不同
三大特征
封装
一个对象把属性和行为封装为一个整体。封装是一种信息隐蔽技术,它的目的是使对象的使用者和生产者分离,使对象的定义和实现分开。
继承
1.子继承父
2.子加入自己的属性和方法
3.子重写覆盖父
多态
不同的对象收到同一消息可以产生完全不同的结果,这一现象称为多态(Polymorphism)。在使用多态的时候,用户可以发送一个通用的消息,而实现的细节则由接收对象自行决定。这样,同一消息就可以调用不同的方法。
多态的实现受到继承的支持,利用类的继承的层次关系,把具有通用功能的消息存放在高层次,而不同的实现这一功能的行为放在较低层次,在这些低层次上生成的对象能够给通用消息以不同的响应。
- 父类 对象名 = new 子类名
- 编译看左边,运行看右边
- 父类引用指向了子类对象
多态有不同的形式,分为了四类:
- 通用的:
- 参数多态:应用比较广泛的多态,被称为最纯的多态。
- 包含多态:在许多语言中都存在,最常见的例子就是子类型化,即一个类型是另一个类型的子类型。
- 特定的:
- 过载多态:同一个名字在不同的上下文中所代表的含义不同。
- 强制多态:通过强制类型转换(也称为强制转型)将一个对象或变量视为另一个类型的操作。
静态和动态绑定
静态绑定是在编译时进行的,动态绑定则是在运行时进行的。
动态绑定是和类的继承以及多态相联系的。
因此在运行过程中,当一个对象发送消息请要根据接收对象的具体情况将请求的操作与实现的方法进行连接,即动态绑定。
面向对象设计的原则
方法中的五大原则:
- 单一责任原则:就一个类而言,应该仅有一个引起它变化的原因。
- 开放-封闭原则:软件实体应该是可以扩展的,即开发的;但是不可修改的,即封闭的。(扩展开放、修改关闭)
- 里氏替换原则: 子类型必须能够替换掉他们的基类型。(基(父)类出现的地方,子类一定可以出现)
- 依赖倒置原则:抽象不应该依赖于细节,细节应该依赖于抽象。(依赖于抽象,而不依赖于细节[实现])
- 接口分离原则:不应该强迫客户依赖于它们不用的方法。(依赖于抽象,不依赖于具体)
共同封闭原则:包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响。
共同重用原则:一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。
面向对象分析
分析软件(系统)做什么
包含5个活动:认定对象(名词短语)、组织对象、描述对象间的相互作用、确定对象的操作(动词短语)、定义对象的内部信息。
面向对象设计
理解解决方案,实现系统的实现
面向对象设计的活动(OOD在复用OOA模型的基础上,包含与OOA对应如下五个活动):
- 识别类及对象
- 定义属性
- 定义服务
- 识别关系
- 识别包
面向对象测试
面向对象软件的测试可分为4个层次:算法层、类层、模板层和系统层
UML
事物
UML中有4中事物:
-
结构事物:结构事物是UML模型中的名词,通常是模型的静态部分,描述概念或物理元素。
-
行为事物:行为事物是UML模型的动态部分,它们是模型中的动词,描述了跨越时间和空间的行为。
-
分组事物:分组事物是UML模型的组织部分,是一些由模型分解成“盒子”。
-
注释事物:注释事物是UML模型的解释部分。这些注释事物用来描述、说明和标注模型的任何元素。
关系
UML中有4种关系:依赖、关联、泛化和实现。
-
依赖:依赖是两个事物间的语义关系,其中一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义。
-
关联:关联是一种结构关系,它描述了一组链,链是对象之间的连接。组合和聚合都是关联的特殊种类
- 聚合:部分和整体的生命周期不一致,整体消失了,部分仍然存在,部分可以脱离整体存在。
- 组合:部分和整体的生命周期一致,整体消失了,部分也消失了,部分不可以脱离整体存在。
-
泛化:泛化是一种特殊/一般关系,特殊元素(子元素)的对象可替代一般元素(父元素)的对象。子元素共享了父元素的结构和行为。
-
实现(了解):实现是类元之间的语义关系,其中一个类元指定了由另一个类元保证执行的契约。
UML 中的图
类图
对系统的静态方面进行建模
类图(Class Diagram)展现了一组对象、接口、协作和它们之间的关系。
符号:
+
: public 公有的
-
: private 私有的
#
: protected 受保护的
~
: package 包的
通常以下述3种方式之一使用类图:
- 对系统的词汇建模。
- 对简单的协作建模。
- 对逻辑数据库模式建模。
对象图
对象图(Object Diagram)展现了某一时刻一组对象以及它们之间的关系,描述了在类图中所建立的事物的实例的静态快照。
对象图给出系统的静态设计视图或静态进程视图。
用例图(P368)
用例图(Use Case Diagram)展现了一组用例、参与制(Actor)以及它们之间的关系。
一个用例执行的时候,可能会发生一些特殊的情况或可选的情况,这种情况就是这个用例的扩展用例。
参与者:参与者是与系统交互的外部实体,可能是使用者,也可能是与系统交互的外部系统、基础设备等。
用例:用例是从用户角度描述系统的行为,它将系统的一个功能描述成一系列的事件,这些事件最终对操作者产生有价值的观测结果。用例是一个类,它代表一类功能而不是使用该功能的某一具体实例。
之间的关系:
-
包含关系(用例与用例之间)
-
扩展关系(用例与用例之间)
特殊的情况
可选的情况
-
关联关系(参与者和用例之间)
-
泛化关系(父类泛化子类)(用例与用例以及参与者与参与者之间)
用例图用于对系统的静态用例视图进行建模。
可用以下两种方式来使用用例图:
- 对系统的语境建模。
- 对系统的需求建模。
交互图
交互图用于对系统的动态方面进行建模。一张交互图表现的是一个交互,由一组对象和它们之间的关系组成。包含它们之间可能传递的消息。
-
序列图(顺序图、时序图) :序列图是场景的图形化表示,描述了以时间顺序组织的对象之间的交互活动。
序列图有两个不同于通信图的特征:-
序列图有对象生命线(虚线)
-
序列图有控制焦点(瘦高的矩形)交互
-
-
通信图(协作图):通信图强调收发消息的对象的结构组织,在早期的版本中也被称作协作图。
通信图有两个不同于序列图的特性:- 通信图有路径
- 通信图有顺序号
-
交互概览图
-
计时图
状态图
状态图(State Diagram)展现了一个状态机,它由状态、转换、事件和活动组成。
可以用状态图对系统的动态方面建模。当对系统、类或用例的动态方面建模时,通常是对反应型对象建模。
定义的状态主要有:初态(即初始状态)、终态(即最终状态)和中间状态。一张状态图中只能有一个初态,而终态可以没有,也可以有多个
三种标准事件:entry、exit、do
-
entry:入口动作,进入状态立即执行
-
exit:出口动作,退出状态立即执行
-
do:内部活动,占有限时间,并可以中断的工作
事件是在某个特定时刻发生的事情,它是对引起系统做动作或(和)从一个状态转换到另一个状态的外界事件的抽象。
转换包括两个状态(源状态,目标状态)
事件,监护条件,动作
事件触发转换(迁移)
活动由若干个动作组成
活动(动作)可以在状态内执行,也可以在状态(迁移)转换时执行。
监护条件是一个布尔表达式。
活动图
活动图(Activity Diagram)是一种特殊的状态图,它展现了在系统内从一个活动到另一个活动的流程。
活动图一般包括活动状态和动作状态、转换和对象。
通常有两种使用活动图的方式:
- 对工作流建模。
- 对操作建模。
构件图(组件图)
构件图(Component Diagram)展现了一组构件之间的组织和依赖。
构件图专注于系统的静态实现试图。
部署图
部署图(Deployment Diagram)是用来对面向对象系统的物理方面建模的方法,展现了运行时处理结点以及其中构件(制品)的配置。通常一个节点包含一个或多个组件,其依赖关系类似于包图。
部署图展现了系统的软件和硬件之间的关系,在实施阶段使用。
UML图汇总
- 静态建模:类图、对象图、用例图
- 动态建模:序列图(顺序图、时序图)、通信图(协作图)、状态图、活动图
- 物理建模:构件图(组件图)、部署图
- 交互图:序列图(顺序图、时序图)、通信图(协作图)
杂记:
- UML中接口可用于:声明对象类所需要的服务,而服务具体如何执行,有实现它的具体类完成。
设计模式
设计模式的要素
设计模式的核心在于提供了相关问题的解决方案,使得人们可以更加简单方便地复用成功的设计和体系结构。
设计模式基本要素:
- 模式名称(Pattern Name)
- 问题(Problem)
- 解决方案(Solution)
- 效果(Consequences)
创建型设计模式(5种)
1. Simple Factory(简单工厂)
简单工厂模式属创建型模式,但不属于23种设计模式之一。
定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method)。
/**
* 简单工厂模式
*/
public class SimpleFactory {
public static void main(String[] args) {
Product productA = Factory.createProduct("A");
productA.info();
Product productB = Factory.createProduct("B");
productB.info();
}
}
class Factory{
public static Product createProduct(String type){
Product product =null;
switch (type){
case "A":
product = new ProductA();
break;
case "B":
product = new ProductB();
break;
default:
System.out.println("没有 " + type + " 类型的产品!");
return null;
}
return product;
}
}
abstract class Product{
public abstract void info();
}
class ProductA extends Product{
@Override
public void info() {
System.out.println("产品的信息:A");
}
}
class ProductB extends Product{
@Override
public void info() {
System.out.println("产品的信息:B");
}
}
2. Factory Method(工厂方法)
1)意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
2)结构
/**
* 工厂方法模式
*/
public class FactoryMethod {
public static void main(String[] args) {
// 父类 对象名 = new 子类();
Factory factoryA = new FactoryA();
Product productA = factoryA.createProduct();
productA.info();
Factory factoryB = new FactoryB();
Product productB = factoryB.createProduct();
productB.info();
}
}
interface Factory{
Product createProduct();
}
class FactoryA implements Factory{
@Override
public Product createProduct() {
return new ProductA();
}
}
class FactoryB implements Factory{
@Override
public Product createProduct() {
return new ProductB();
}
}
interface Product{
void info();
}
class ProductA implements Product{
@Override
public void info() {
System.out.println("产品的信息:A");
}
}
class ProductB implements Product{
@Override
public void info() {
System.out.println("产品的信息:B");
}
}
3)适用性
Factory Method 模式适用于:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
3. Abstract Factory(抽象工厂)
1)意图
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
2)结构
/**
* 抽象工厂模式
*/
public class AbstractFactory {
public static void main(String[] args) {
Factory factory1 = new Factory1();
ProductA productA1 = factory1.createProductA();
productA1.info();
ProductB productB1 = factory1.createProductB();
productB1.info();
Factory factory2 = new Factory2();
ProductA productA2 = factory2.createProductA();
productA2.info();
ProductB productB2 = factory2.createProductB();
productB2.info();
}
}
// 声明一个创建抽象产品对象的操作接口
interface Factory{
ProductA createProductA();
ProductB createProductB();
}
// 实现创建具体产品对象的操作
class Factory1 implements Factory{
@Override
public ProductA createProductA() {
return new ProductA1();
}
@Override
public ProductB createProductB() {
return new ProductB1();
}
}
class Factory2 implements Factory{
@Override
public ProductA createProductA() {
return new ProductA2();
}
@Override
public ProductB createProductB() {
return new ProductB2();
}
}
// 为一类产品对象声明一个接口
interface ProductA{
void info();
}
interface ProductB{
void info();
}
// 定义一将被相应的具体工厂创建的产品对象
class ProductA1 implements ProductA{
@Override
public void info() {
System.out.println("产品的信息:A1");
}
}
class ProductA2 implements ProductA{
@Override
public void info() {
System.out.println("产品的信息:A2");
}
}
class ProductB1 implements ProductB{
@Override
public void info() {
System.out.println("产品的信息:B1");
}
}
class ProductB2 implements ProductB{
@Override
public void info() {
System.out.println("产品的信息:B2");
}
}
3)适用性
Abstract Factory 模式适用于:
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当提供一个产品类库,只想显示它们的接口而不是实现时。
4. Builder(生成器)
1)意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2)结构
import java.util.*;
/**
* 生成器模式
*/
public class Main {
public static void main(String[] args) {
Director director = new Director();
Builder builder1 = new Builder1();
director.Construct(builder1);
Product product1 = builder1.getResult();
product1.show();
Builder builder2 = new Builder2();
director.Construct(builder2);
Product product2 = builder2.getResult();
product2.show();
}
}
class Director{
public void Construct(Builder builder){
builder.BuildPart();
}
}
abstract class Builder{
public abstract void BuildPart();
public abstract Product getResult();
}
class Builder1 extends Builder{
Product product = new Product();
@Override
public void BuildPart() {
product.add("A");
product.add("B");
product.add("C");
product.add("D");
product.add("E");
product.add("F");
}
@Override
public Product getResult() {
return product;
}
}
class Builder2 extends Builder{
Product product = new Product();
@Override
public void BuildPart() {
product.add("A");
product.add("B");
product.add("C");
}
@Override
public Product getResult() {
return product;
}
}
class Product{
List<String> parts = new ArrayList<String>();
public void add(String part){
parts.add(part);
}
public void show(){
System.out.print("产品的组成:");
for (String part : parts) {
System.out.print(part + " ");
}
System.out.println();
}
}
3)适用性
Builder 模式适用于:
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
5. Prototype(原型)
1)意图
用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
2)结构
其中:
- Prototype声明一个复制自身的接口。
- ConcretePrototype 实现一个复制自身的操作。
- Client 让一个原型复制自身从而创建一个新的对象
/**
* 原型模式
*/
public class Main {
public static void main(String[] args) {
Product product1 = new Product(2022,5.28);
System.out.println(product1.getId()+ " " + product1.getPrice());
Product product2 = (Product) product1.Chlone();
System.out.println(product2.getId()+ " " + product2.getPrice());
}
}
interface Prototype{
Object Chlone();
}
class Product implements Prototype{
private int id;
private double price;
public Product(){}
public Product(int id,double price){
this.id = id;
this.price = price;
}
public int getId() {
return id;
}
public double getPrice() {
return price;
}
@Override
public Object Chlone() {
Product object = new Product();
object.id = this.id;
object.price = this.price;
return object;
}
}
3)适用性
Prototype 模式适用于:
- 当一个系统应该独立于它的产品创建、构成和表示时。
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。
6. Singleton(单例)
1)意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2)结构
其中:Singleton指定一个Instance操作,允许客户访问它的唯一实例,Instance是一个类
操作:可能负责创建它自己的唯一实例。
/**
* 单例模式
*/
public class SingletonPattern {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
Singleton instance3 = Singleton.getInstance();
System.out.println("instance1: " + instance1);
System.out.println("instance2: " + instance2);
System.out.println("instance3: "+ instance3);
}
}
class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
3)适用性
Singleton 模式适用于:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
- 当这个唯一实例应该是通过子类化可扩展的,并且客户无须更改代码就能使用一个扩展的实例时。
结构型设计模式(7种)
1. Adapter(适配器)
1)意图
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
2)结构
其中:
- Target定义Client使用的与特定领域相关的接口。
- Client与符合Target接口的对象协同。
- Adaptee定义一个已经存在的接口,这个接口需要适配。
- Adapter对Adaptee的接口与Target接口进行适配。
/**
* 适配器模式
*/
public class AdapterPattern {
public static void main(String[] args) {
Target target = new Adapter();
target.Request();
}
}
class Target{
public void Request(){
System.out.println("普通请求~");
}
}
/**
* 适配器
*/
class Adapter extends Target {
private Adaptee adaptee = new Adaptee();
@Override
public void Request() {
adaptee.SpecificRequest();
}
}
class Adaptee{
public void SpecificRequest(){
System.out.println("特殊请求~");
}
}
3)适用性
Adapter 模式适用于:
- 想使用一个已经存在的类,而它的接口不符合要求。
- 想创建一个可以服用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。(了解)
- (仅适用于对象Adapter)想使用一个已经存在的子类,但是不可能对每一个都进行子
类化以匹配它们的接口。对象适配器可以适配它的父类接口。(了解)
2. Bridge(桥接)
1)意图
将抽象部分与其实现部分分离,使它们都可以独立地变化。
2)结构
/**
* 桥接模式
*/
public class BridgePattern {
public static void main(String[] args) {
Product productA = new ProductA();
Product productB = new ProductA();
Color red = new Red();
productA.setName("产品A");
productA.setColor(red);
productA.Operation();
Blue blue = new Blue();
productB.setName("产品B");
productB.setColor(blue);
productB.Operation();
}
}
abstract class Product{
private String name;
protected Color color;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setColor(Color color){
this.color = color;
}
public abstract void Operation();
}
interface Color{
void OperationImpl(String name);
}
class ProductA extends Product{
@Override
public void Operation() {
color.OperationImpl(this.getName());
}
}
class Red implements Color{
@Override
public void OperationImpl(String name) {
System.out.println(name + ": 红色" );
}
}
class Blue implements Color{
@Override
public void OperationImpl(String name) {
System.out.println(name + ": 蓝色" );
}
}
3)适用性(了解)
Bridge 模式适用于:
不希望在抽象和它的实现部分之间有一个固定的绑定关系。
类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。
对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译。
(C++)想对客户完全隐藏抽象的实现部分。
有许多类要生成的类层次结构。
想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
3. Composite(组合)
1)意图
将对象组合成树型结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
2)结构
其中:
- Component 为组合中的对像声明接口:在适当情况下实现所有类共有接口的默认行为:
声明一个接口用于访问和管理 Component 的子组件;(可选)在递归结构中定义一个
接口,用于访问一个父组件,并在合适的情况下实现它。 - Leaf 在组合中表示叶结点对象,叶结点没有子结点;在组合中定义图元对象的行为。
- Composite定义有子组件的那些组件的行为;存储子组件;在Component接口中实现
与子组件有关的操作。 - Client 通过 Component 接口操纵组合组件的对象。
import java.util.*;
/**
* 组合模式
*/
public class CompositePattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
AbstractFile root = new Folder("root");
AbstractFile folderA = new Folder("folderA");
AbstractFile folderB = new Folder("folderB");
AbstractFile fileC = new File("fileC");
AbstractFile fileD = new File("fileD");
AbstractFile fileE = new File("fileE");
root.Add(folderA);
root.Add(folderB);
root.Add(fileC);
folderA.Add(fileD);
folderA.Add(fileE);
print(root);
}
static void print(AbstractFile file){
file.printName();
List<AbstractFile> childrenList = file.getChildren();
if (childrenList == null){
return;
}
for (AbstractFile children : childrenList) {
print(children);
}
}
}
abstract class AbstractFile{
protected String name;
public void printName(){
System.out.println(name);
}
public abstract boolean Add(AbstractFile file);
public abstract boolean Remove(AbstractFile file);
public abstract List<AbstractFile> getChildren();
}
class Folder extends AbstractFile {
private List<AbstractFile> childrenList = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
@Override
public boolean Add(AbstractFile file) {
return childrenList.add(file);
}
@Override
public boolean Remove(AbstractFile file) {
return childrenList.remove(file);
}
@Override
public List<AbstractFile> getChildren() {
return childrenList;
}
}
class File extends AbstractFile{
public File(String name) {
this.name = name;
}
@Override
public boolean Add(AbstractFile filei) {
return false;
}
@Override
public boolean Remove(AbstractFile file) {
return false;
}
@Override
public List<AbstractFile> getChildren() {
return null;
}
}
3)适用性
Composite 模式下适用于:
- 想表示对象的部分-整体层次结构。
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
4. Decorator(装饰器)
1)意图
动态地给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更加
灵活。
2)结构
其中:
- Component定义一个对象接口,可以给这些对象动态地添加职责。
- ConcreteComponent定义一个对象,可以给这个对象添加一些职责。
- Decorator 维持一个指向 Component 对象的指针,并定义一个与Component 接口一致的接口。
- ConcreteDecorator 向组件添加职责。
/**
* 装饰器模式
*/
public class DecoratorPattern {
public static void main(String[] args) {
Person zhangsan = new Student("张三");
zhangsan = new DecoratorA(zhangsan);
zhangsan = new DecoratorB(zhangsan);
zhangsan.Operation();
System.out.println("==========分割线==============");
// 对像链
Person lisi = new DecoratorB(new DecoratorA(new Student("李四")));
lisi.Operation();
}
}
abstract class Decorator extends Person{
protected Person person;
}
class DecoratorA extends Decorator{
public DecoratorA(Person person){
this.person = person;
}
@Override
public void Operation() { // 职责
person.Operation(); // 原本的职责
System.out.println("写作业~");
}
}
class DecoratorB extends Decorator{
public DecoratorB(Person person){
this.person = person;
}
@Override
public void Operation() { // 职责
person.Operation(); // 原本的职责
System.out.println("考试~");
}
}
abstract class Person{
protected String name;
public abstract void Operation(); // 职责
}
class Student extends Person{
public Student(String name){
this.name = name;
}
@Override
public void Operation() {
System.out.println(name + "的职责:学习~");
}
}
3)适用性
Decorator 模式适用于:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤销的职责。
- 当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于类定义被隐藏,或类定义不能用于生成子类。(了解)
5. Facade(外观)
1)意图
为子系统中的一组接口提供一个一致的界面,Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
2)结构
其中:
- Facade知道哪些子系统类负责处理请求;将客户的请求代理给适当的子系统对象。
- Subsystem classes实现子系统的功能;处理有Facade对象指派的任务;没有Facade的
任何相关信息,即没有指向Facade的指针。
import java.util.Scanner;
/**
*/
public class FacadePattern {
public static void main(String[] args) {
Facade facade = new Facade();
facade.methodA();
facade.methodB();
facade.methodC();
}
}
class Facade{
SubSystemOne subSystemOne;
SubSystemTwo subSystemTwo;
SubSystemThree subSystemThree;
public Facade(){
subSystemOne = new SubSystemOne();
subSystemTwo = new SubSystemTwo();
subSystemThree = new SubSystemThree();
}
public void methodA(){
subSystemOne.methodOne();
}
public void methodB(){
subSystemTwo.methodTwo();
}
public void methodC(){
subSystemThree.methodThree();
}
}
class SubSystemOne{
public void methodOne(){
System.out.println("执行子系统一的功能~");
}
}
class SubSystemTwo{
public void methodTwo(){
System.out.println("执行子系统二的功能~");
}
}
class SubSystemThree{
public void methodThree(){
System.out.println("执行子系统三的功能~");
}
}
3)适用性
Facade 模式适用于:
- 要为一个复杂子系统提供一个简单接口时。
- 客户程序与抽象类的实现部分之间存在着很大的依赖性。
- 当需要构建一个层次结构的子系统时,使用 Facade 模式定义子系统中每层的入口点。
6. Flyweight(享元)
1)意图
运用共享技术有效地支持大量细粒度的对象。
2)结构
其中:
- Flyweight 描述一个接口,通过这个接口 Flyweight 可以接受并作用于外部状态。
- ConcreteFlyweight 实现Flyweight接口,并为内部状态(如果有)增加存储空间。
- ConcreteFlyweight 对象必须是可共享的。它所存储的状态必须是内部的,即它必须独
立于 ConcreteFlyweight 对象的场景。 - 并非所有的 Flyweight 子类都需要被共享。Flyweight 接口使共享成为可能,但它并不
强制共享。在 Flyweight 对象结构的某些层次,UnsharedConcreteFlyweight 对象通常将
ConcreteFlyweight 对象作为子结点。 - FlyweightFactory 创建并管理Flyweight对象;确保合理地共享Flyweight,当用户请求
一个Flyweight时,FlyweightFactory 对象提供一个已创建的实例或者在不存在时创建
个实例。 - Client 维持一个对 Flyweight 的引用;计算或存储一个或多个 Flyweight 的外部状态。
/**
* 享元模式 案例1
*/
public class FlyweightPattern {
public static void main(String[] args) {
PieceFactory factory = new PieceFactory();
Piece whitePiece1 = factory.getPiece(0);
whitePiece1.draw(66,87);
System.out.println(whitePiece1);
Piece blackPiece1 = factory.getPiece(1);
blackPiece1.draw(20,11);
System.out.println(blackPiece1);
Piece whitePiece2 = factory.getPiece(0);
whitePiece1.draw(26, 54);
System.out.println(whitePiece2);
Piece blackPiece2 = factory.getPiece(1);
blackPiece2.draw(12, 34);
System.out.println(blackPiece2);
}
}
class PieceFactory{
private Piece[] pieces = {new WhitePiece(),new BlackPiece()};
public Piece getPiece(int key){
if (key == 0) return pieces[0];
else return pieces[1];
}
}
abstract class Piece{
protected String color;
public abstract void draw(int x,int y);
}
class WhitePiece extends Piece{
public WhitePiece(){
this.color = "white";
}
@Override
public void draw(int x, int y) {
System.out.println("draw a color: "+color + " piece x: " + x + " y: " + y);
}
}
class BlackPiece extends Piece{
public BlackPiece(){
this.color = "black";
}
@Override
public void draw(int x, int y) {
System.out.println("draw a color: " + color + " piece x: " + x + " y: " + y);
}
}
import java.util.*;
/**
* 享元模式 案例2
*/
public class FlyweightPattern {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
Random random = new Random();
String[] colors = {"red","blue","green","whilte","black"};
for (int i = 1; i <= 10; i++) {
int x = random.nextInt(colors.length);
Shape shape = factory.getShape(colors[x]);
System.out.print("第" + i + "个圆:");
shape.draw(random.nextInt(2022),random.nextInt(528));
}
}
}
class ShapeFactory{
private Map<String,Shape> map = new HashMap<>();
public Shape getShape(String key){
if (!map.containsKey(key)) {
map.put(key, new Circle(key));
System.out.println("create color: " + key + " circle");
}
return map.get(key);
}
}
abstract class Shape {
protected String color;
public abstract void draw(int x, int y);
}
class Circle extends Shape {
public Circle(String color){
this.color = color;
}
@Override
public void draw(int x, int y) {
System.out.println("draw a color: " + color + " circle x:"+ x + " y:" + y);
}
}
3)适用性
Flyweight模式适用于:
- 一个应用程序使用了大量的对象。
- 完全由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,所以对于概念上明显
有别的对象,标识测试将返回真值。
7. Proxy(代理)
1)意图
为其他对象提供一种代理以控制对这个对象的访问。
2)结构
/**
* 代理模式
*/
public class ProxyPattern {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.buy();
}
}
interface Subject{
void buy();
}
class Proxy implements Subject{
protected RealSubject realSubject;
public Proxy(RealSubject realSubject){
this.realSubject = realSubject;
}
@Override
public void buy() {
System.out.println("办理购买前的手续~");
realSubject.buy(); // 付钱
System.out.println("办理购买后的手续~");
}
}
class RealSubject implements Subject{
@Override
public void buy() {
System.out.println("付钱~");
}
}
3)适用性 (了解)
Poxy模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候,常见情况有:
- 远程代理(Remote Proxy)为一个对象在不同地址空间提供局部代表。
- 虚代理(Virtual Proxy)根据需要创建开销很大的对象。
- 保护代理(Protection Proxy)控制对原始对象的访问,用于对象应该有不同的访问权
限的时候。 - 智能引用(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。
行为设计模式(11种)
1. Chain of Responsibility(责任链)
1)意图
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
2)结构
其中:
- Handler定义一个处理请求的接口;(可选)实现后继链。
- ConcreteHandler处理它所负责的请求;可访问它的后继者;如果可处理该请求,就处理它,否则将该请求转发给后继者。
- Client向链上的具体处理者(ConcreteHandler)对象提交请求。
/**
* 责任链模式
*/
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
Handler counsellor = new Counsellor();
Handler dean = new Dean();
Handler headmaster= new Headmaster();
counsellor.setNext(dean);
dean.setNext(headmaster);
counsellor.HandRequest(25);
}
}
abstract class Handler{
protected Handler next;
public void setNext(Handler next){
this.next =next;
}
public abstract void HandRequest(int request);
}
class Counsellor extends Handler{
@Override
public void HandRequest(int request) {
if (request <= 7){
System.out.println("辅导员审批通过~");
}else {
if (next != null){
next.HandRequest(request);
}else {
System.out.println("无法审批");
}
}
}
}
class Dean extends Handler{
@Override
public void HandRequest(int request) {
if (request <= 15){
System.out.println("院长审批通过~");
}else {
if (next != null){
next.HandRequest(request);
}else {
System.out.println("无法审批");
}
}
}
}
class Headmaster extends Handler{
@Override
public void HandRequest(int request) {
if (request <= 30){
System.out.println("校长审批通过~");
}else {
if (next != null){
next.HandRequest(request);
}else {
System.out.println("无法审批");
}
}
}
}
3)适用性
Chain of Responsibility 模式适用于以下条件:
- 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
- 想在不明确指定接收者的情况下向多个对象中的一个提交一个请求。
- 可处理一个请求的对象集合应被动态指定。
2. Command(命令)
1)意图
将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
2)结构
其中:
- Command声明执行操作的接口。
- ConcreteCommand 将一个接收者对象绑定于一个动作:调用接收者相应的操作,以实现Execute。
- Client 创建一个具体命令对象并设定它的接收者。
- Invoker 要求该命令执行这个请求。
- Receiver 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
/**
* 命令模式
*/
public class CommandPattern {
public static void main(String[] args) {
Tv tv = new Tv(); // 接收者 对象 电视机
Command onCommand = new OnCommand(tv); // 命令对象 开机命令
Command offCommand = new OnCommand(tv); // 命令对象 关机命令
Invoker invoker = new Invoker(); //请求者
invoker.setCommand(onCommand); // 给请求者设置 开机 命令
invoker.call(); // 请求者去请求命令
System.out.println("==============分割线===============");
invoker.setCommand(offCommand); // 给请求者设置 关机命令
invoker.call(); // 请求者去请求命令
}
}
class Invoker{ // 请求者
private Command command; // 命令
public void setCommand(Command command){ // 设置请求者的命令
this.command = command;
}
public void call(){ // 调用
command.Execute();
}
}
interface Command{ // 命令接口
void Execute(); // 执行命令
}
class OnCommand implements Command{// 开机命令
private Tv tv;
public OnCommand(Tv tv){
this.tv = tv;
}
@Override
public void Execute() {
tv.OnAction();
}
}
class OffCommand implements Command{ // 关机命令
private Tv tv;
public OffCommand(Tv tv){
this.tv = tv;
}
@Override
public void Execute() {
tv.OffAction();
}
}
class Tv{
public void OnAction(){ // 开机行为
System.out.println("电视机开机了...");
}
public void OffAction(){ // 关机行为
System.out.println("电视机关机了...");
}
}
3)适用性
Command 模式适用于:
- 抽象出待执行的动作以参数化某对象。
- 在不同的时刻指定、排列和执行请求。
- 支持取消操作。
- 支持修改日志。
- 用构建在原语操作上的高层操作构造一个系统。
3. Interpreter(解释器)
1)意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
2)结构
import java.util.*;
/**
* 解释器模式
*/
public class InterpreterPattern {
public static void main(String[] args) {
Context context = new Context();
context.check("A区的开发人员");
context.check("B区的调试人员");
context.check("C区的测试人员");
System.out.println("========分割线=============");
context.check("D区的程序员");
context.check("D区的测试员");
context.check("C区的程序员");
}
}
class Context{
private String[] regions = {"A区","B区","C区"};
private String[] persions = {"开发人员","测试人员","调试人员"};
private NonterminalExpression nonterminal;
public Context(){
TerminalExpression region = new TerminalExpression(regions);
TerminalExpression person = new TerminalExpression(persions);
nonterminal = new NonterminalExpression(region,person);
}
public void check(String info){
boolean result = nonterminal.Interpret(info);
if (result){
System.out.println("识别成功~");
}else {
System.out.println("识别失败~");
}
}
}
interface Expression{
boolean Interpret(String info);
}
class NonterminalExpression implements Expression{
private TerminalExpression region;
private TerminalExpression person;
public NonterminalExpression(TerminalExpression region,TerminalExpression person){
this.region =region;
this.person = person;
}
@Override
public boolean Interpret(String info) {
String[] str = info.split("的");
// B区鹅调试人员 --> str = {"B区","调试人员"};
return region.Interpret(str[0]) && person.Interpret(str[1]);
}
}
class TerminalExpression implements Expression{
private Set<String> set = new HashSet<>();
public TerminalExpression(String[] data){
for (String str : data) {
set.add(str);
}
}
@Override
public boolean Interpret(String info) {
return set.contains(info);
}
}
3)适用性
Interpreter模式适用于当有一个语言需要解释执行,并且可将该语言中的句子表示为一个抽象语法树时,以下情况效果最好:
- 该文法简单。对于复杂的发文,文法的类层次变得庞大而无法管理。
- 效率不是一个关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的,
而是首先将它们转换成另一种形式。
4. Iterator(迭代器)
1)意图
提供一种方法顺序访问一个聚合对象中的各个元素,且不需要暴露该对象的内部表示。
2)结构
其中:
- Iterator (迭代器)定义访问和遍历元素的接口。
- ConcreteIterator(具体迭代器)实现迭代器接口;对该聚合遍历时跟踪当前位置。
- Aggregate( 聚合)定义创建相应迭代器对象的接口。
- ConcreteAggregate (具体聚合)实现创建相应迭代器的接口,该操作返回 ConcreteIterator
的一个适当的实例。
import java.util.*;
/**
* 迭代器模式
*/
public class IteratorPattern {
public static void main(String[] args) {
BookAggregate bookAggregate = new BookAggregate();
String[] books = {"数据结构","操作系统","计算机网络","计算机组成原理"};
double[] prices = {10.24,20.48,40.96,81.92};
for (int i = 0; i < 4; i++) {
bookAggregate.Add(new Book(books[i],prices[i]));
}
Iterator iterator = bookAggregate.CreateIterator();
while (iterator.hasNext()) {
Book book = (Book) iterator.next();
System.out.println("书名:" + book.getName() + " 价格:" + book.getPrice());
}
}
}
interface Iterator{
boolean hasNext();
Object next();
}
class BookIterator implements Iterator{
private int index;
private BookAggregate bookAggregate;
public BookIterator(BookAggregate bookAggregate){
this.index = 0;
this.bookAggregate = bookAggregate;
}
@Override
public boolean hasNext() {
if (index < bookAggregate.getSize()){
return true;
}
return false;
}
@Override
public Object next() {
Object obj = bookAggregate.get(index);
index ++ ;
return obj;
}
}
class BookAggregate implements Aggregate{
private List<Book> list = new ArrayList<>();
public void Add(Book book){
list.add(book);
}
public Book get(int index){
return list.get(index);
}
public int getSize(){
return list.size();
}
@Override
public Iterator CreateIterator() {
return new BookIterator(this);
}
}
interface Aggregate{
Iterator CreateIterator();
}
class Book{
private String name;
private double price;
public Book(String name,double price){
this.name = name;
this.price = price;
}
public String getName(){
return name;
}
public double getPrice(){
return price;
}
}
3)适用性
Iterator 模式适用于:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 支持对聚合对象的多种遍历。
- 为遍历不同的聚合结构提供一个统一的接口。
5. Mediator(中介者)
1)意图
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
2)结构
其中:
- Mediator (中介者)定义一个接口用于各同事(Colleague)对象通信。
- ConcreteMediator (具体中介者)通过协调各同事对象实现协作行为;了解并维护它的
各个同事。 - Colleague class (同事类)知道它的中介者对象;每一个同事类对象在需要与其他同事
通信的时候与它的中介者通信。
/**
* 中介者模式
*/
public class MediatorPattern {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
Colleague1 colleague1 =new Colleague1(mediator);
Colleague2 colleague2 = new Colleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("软考加油~");
colleague2.sendMessage("祝你上岸~");
}
}
abstract class Colleague{
protected Mediator mediator;
}
class Colleague1 extends Colleague{
public Colleague1(Mediator mediator){
this.mediator = mediator;
}
public void sendMessage(String message){
mediator.sendMessage(message,this);
}
public void Notify(String message){
System.out.println("同事1收到消息:" + message);
}
}
class Colleague2 extends Colleague{
public Colleague2(Mediator mediator){
this.mediator = mediator;
}
public void sendMessage(String message){
mediator.sendMessage(message,this);
}
public void Notify(String message){
System.out.println("同事2收到消息:" + message);
}
}
abstract class Mediator{
public abstract void sendMessage(String message,Colleague colleague);
}
class ConcreteMediator extends Mediator{
private Colleague1 colleague1;
private Colleague2 colleague2;
public void setColleague1(Colleague1 colleague1){
this.colleague1 = colleague1;
}
public void setColleague2(Colleague2 colleague2){
this.colleague2 = colleague2;
}
@Override
public void sendMessage(String message, Colleague colleague) {
if (colleague == colleague1){
// 让同事2收到消息
colleague2.Notify(message);
}else {
// 让同事1收到消息
colleague1.Notify(message);
}
}
}
3)适用性
Mediator 模式适用于:
- 一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
- 想定制一个分布在多个类中的行为,而又不想生成太多的子类。
6. Memento(备忘录)
1)意图
在不破坏封装性的前提下捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
2)结构
import java.util.*;
/**
* 备忘录模式
*/
public class MementoPattern {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator();
originator.setState("1024");
Memento backup1 = originator.createMemento();
caretaker.addMemento(backup1);
originator.setState("2048");
Memento backup2 = originator.createMemento();
caretaker.addMemento(backup2);
originator.setState("4096");
Memento backup3 = originator.createMemento();
caretaker.addMemento(backup3);
System.out.println(originator.getState());
caretaker.showMemento();
Memento memento1 = caretaker.getMemento(2);
originator.setState(memento1.getState());
System.out.println("根据第2次备份还原之后的状态为:" + originator.getState());
}
}
class Originator{ // 原发器
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento createMemento(){
return new Memento(state);
}
public void setMemento(Memento memento){
state = memento.getState();
}
}
class Memento{ // 备忘录
private String state;
public String getState(){
return state;
}
public Memento(String state){
this.state = state;
}
}
class Caretaker{ // 管理者
private List<Memento> mementoList = new ArrayList<>();
public void addMemento(Memento memento){
mementoList.add(memento);
}
public Memento getMemento(int index){
// 判断参数是否合法
if (index >=1 && index <= mementoList.size()) {
return mementoList.get(index - 1);
}
return null;
}
public void showMemento(){
int cnt = 1;
for (Memento memento : mementoList) {
System.out.println("第" + cnt + "次备份,状态为:" + memento.getState());
cnt ++;
}
}
}
3)适用性
Mement 模式适用于:
- 必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
- 如果一个用接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
7. Observer(观察者)
1)意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
2)结构
其中:
- Subject(目标)知道它的观察者,可以有任意多个观察者观察同一个目标;提供注册
和删除观察者对象的接口。 - Observer(观察者)为那些在目标发生改变时需获得通知的对象定义一个更新接口。
- ConcreteSubject(具体目标)将有关状态存入各ConcreteObserver对象;当它的状态发
生改变时,向它的各个观察者发出通知。 - ConcreteObserver (具体观察者)维护一个指向 ConcreteSubject 对象的引用;存储有关
状态,这些状态应与目标的状态保持一致;实现 Observer 的更新接口,以使自身状态
与目标的状态保持一致。
import java.util.*;
/**
* 观察者模式
*/
public class ObserverPattern {
public static void main(String[] args) {
Subject subjectA = new ConcreteSubject("目标A");
Observer observerB = new ConcreteObserver("张三",subjectA);
Observer observerC = new ConcreteObserver("李四",subjectA);
Observer observerD = new ConcreteObserver("王五",subjectA);
subjectA.setState("更新了");
}
}
interface Subject { // 目标接口
void Attach(Observer observer); // 添加观察者
void Detach(Observer observer); // 删除观察者
void Notify(); // 状态改变后,通知所有观察者
void setState(String state); // 设置状态(改变状态)
String getState(); // 获取状态
}
class ConcreteSubject implements Subject {
private String name;
private String state;
private List<Observer> observerList;
public ConcreteSubject(String name) {
state = "未更新";
this.name = name;
observerList = new ArrayList<>();
}
@Override
public void Attach(Observer observer) {
observerList.add(observer);
}
@Override
public void Detach(Observer observer) {
observerList.remove(observer);
}
@Override
public void Notify() {
for (Observer observer : observerList) {
observer.update();
}
}
@Override
public void setState(String state) {
this.state = state;
System.out.println(name + "的状态发生变化,变化后的状态为:" + state);
Notify();
}
@Override
public String getState() {
return state;
}
}
interface Observer { // 观察者接口
void update(); // 收到通知,更新观察者的状态
}
class ConcreteObserver implements Observer {
private String name;
private String state;
private Subject subject;
public ConcreteObserver(String name, Subject subject) {
this.name = name;
this.subject = subject;
subject.Attach(this);
state = subject.getState();
}
@Override
public void update() {
System.out.println(name + " 收到通知");
state = subject.getState(); // 让当前观察者的状态 和 目标改变后的状态保持一致
System.out.println(name + " 改变后的状态为:"+state);
}
}
3)适用性
Observer 模式适用于:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。(了解)
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时。
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的。
8. State(状态)
1)意图
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
2)结构
/**
* 状态模式
*/
public class StatePattern {
public static void main(String[] args) {
Context context = new Context(); // count = 3
System.out.println("状态:" + context.getState() + " 数量:" + context.getCount());
context.Request(); // 购买一个饮料 count = 2
context.Request(); // 购买一个饮料 count = 1
context.Request(); // 购买一个饮料 count = 0
System.out.println("状态:" + context.getState() + " 数量:" + context.getCount());
context.Request(); // 无货,等待补货
System.out.println("状态:" + context.getState() + " 数量:" + context.getCount());
context.Request(); // 购买一个饮料 count = 4
System.out.println("状态:" + context.getState() + " 数量:" + context.getCount());
}
}
class Context{ // 贩卖机
private int count;
private State state;
public Context(){
count = 3;
state = new StateA();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void Request(){ // 购买饮料
state.Handle(this);
}
}
interface State{
void Handle(Context context);
}
class StateA implements State{
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count >= 1){
System.out.println("购买成功~");
context.setCount(count - 1);
if (context.getCount() == 0){
context.setState(new StateB()); // 设置为无货状态
}
}else {
System.out.println("购买失败~");
}
}
}
class StateB implements State{
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count == 0){
System.out.println("购买失败!等待补货~");
context.setCount(5);
System.out.println("补货成功,请重新购买~");
context.setState(new StateA());
}
}
}
3)适用性
State 模式适用于:
- 一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
- 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。
9. Strategy(策略)
1)意图
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。此模式使得算法可以独立于使用它们的客户而变化。
2)结构
/**
* 策略模式
*/
public class StrategyPattern {
public static void main(String[] args) {
OperationContext context = new OperationContext(new Addstrategy());
context.Operation(20,17);
context = new OperationContext(new Substrategy());
context.Operation(20,17);
context = new OperationContext(new Multstrategy());
context.Operation(20,17);
}
}
class OperationContext{
private Strategy strategy;
public OperationContext(Strategy strategy){
this.strategy =strategy;
}
public void Operation(int a, int b){
strategy.TwoNumberOperation(a,b);
}
}
interface Strategy{
void TwoNumberOperation(int a,int b);
}
class Addstrategy implements Strategy{
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a + b);
}
}
class Substrategy implements Strategy{
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a - b);
}
}
class Multstrategy implements Strategy{
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a * b);
}
}
3)适用性
Strategy 模式适用于:
- 许多相关的类仅仅是行为有异。
- 需要使用一个算法的不同变体。
- 使用算法客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的 Strategy 类中,以代替这些条 件语句。
10. Template Method(模板方法)
1)意图
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
2)结构
其中:
- AbstractClass(抽象类)定义抽象的原语操作,具体的子类将重定义它们以实现一个算法的各步骤;实现模板方法,定一个算法的骨架,该模板方法不仅调用原语操作,也调用定义在 AbstractClass 或其他对象中的操作。
- ConcreteClass(具体类)实现原语操作以完成算法中与特定子类相关的步骤。
/**
* 模板方法模式
*/
public class TemplateMethodPattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
Person student = new Student();
Person teacher = new Teacher();
student.TemplateMethod();
System.out.println("=========分割线=============");
teacher.TemplateMethod();
}
}
abstract class Person{
public void TemplateMethod(){
System.out.println("上课 去教室");
PrimitiveOperation1();
System.out.println("下课 离开教室");
PrimitiveOperation2();
}
public abstract void PrimitiveOperation1(); // 原语操作 1:上课过程 学生 听课... 老师 讲课
public abstract void PrimitiveOperation2(); // 原语操作 2:作业 学生 写作业 提交作业... 老师批改作业 打分数
}
class Student extends Person{
@Override
public void PrimitiveOperation1() {
System.out.println("学生:听课 学习 做笔记 提出问题");
}
@Override
public void PrimitiveOperation2() {
System.out.println("学生:写作业 提交作业");
}
}
class Teacher extends Person{
@Override
public void PrimitiveOperation1() {
System.out.println("老师:上课 讲课 解答问题 布置作业");
}
@Override
public void PrimitiveOperation2() {
System.out.println("老师:批改作业 打分数");
}
}
3)适用性
Template Method 模式适用于:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为应被提取出来并集中到一个公共父类中,以避免代码重复。
- 控制子类扩展。模板方法旨在特定点调用“hook”操作(默认的行为,子类可以在必要时进行重定义扩展),这就只允许在这些点进行扩展。 (了解)
11. Visitor(访问者)
1)意图
表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的新操作。
2)结构
其中:
- Visitor(访问者)为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。该操作的名字和特征标识了发送 Vist 请求给该访问者的那个类,这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
- ConcreteVisitor (具体访问者)实现每个有 Visitor 声明的操作,每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。Concrete Visitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
- Element (元素)定义以一个访问者为参数的 Accept 操作。
- ConcreteElement (具体元素)实现以一个访问者为参数的 Accept 操作。
- ObjectStructure (对象结构)能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个组合或者一个集合,如一个列表或一个无序集合。
import java.util.*;
/**
* 访问者模式
*/
public class VisitorPattern {
public static void main(String[] args) {
PersonStructure structure = new PersonStructure();
Visitor1 visitor1 = new Visitor1();
System.out.println("访问者1的访问记录:");
structure.Accept(visitor1);
System.out.println("学生年龄的总和:" + visitor1.getStudentAgeSum() +" 老师年龄的总和:" + visitor1.getTeacherAgeSUm());
System.out.println("=========分割线==============");
Visitor2 visitor2 = new Visitor2();
System.out.println("访问者2的访问记录:");
structure.Accept(visitor2);
System.out.println("学生的最高成绩:" + visitor2.getMaxScore() + " 老师的最高工龄:" + visitor2.getMaxWorkYear());
}
}
interface Visitor{
void VistStudent(Student student); // 访问
void vistTeacher(Teacher teacher); // 访问老师
}
class Visitor1 implements Visitor{ // 访问者1 分别统计学生和老师的年龄总和
private int studentAgeSum = 0;
private int teacherAgeSUm =0;
public int getStudentAgeSum() {
return studentAgeSum;
}
public int getTeacherAgeSUm() {
return teacherAgeSUm;
}
@Override
public void VistStudent(Student student) {
System.out.println("访问者1访问学生:" + student.getName() + " 年龄:" + student.getAge());
studentAgeSum += student.getAge();
}
@Override
public void vistTeacher(Teacher teacher) {
System.out.println("访问者1访问老师:" + teacher.getName() + " 年龄:" + teacher.getAge());
teacherAgeSUm += teacher.getAge();
}
}
class Visitor2 implements Visitor{ // 访问者2 分别求出 学生的最高成绩 以及 老师的最高工龄
private int maxScore = -1;
private int maxWorkYear = -1;
public int getMaxScore() {
return maxScore;
}
public int getMaxWorkYear() {
return maxWorkYear;
}
@Override
public void VistStudent(Student student) {
System.out.println("访问者2访问学生:" + student.getName() + " 成绩:" + student.getScore());
maxScore = Math.max(maxScore,student.getScore());
}
@Override
public void vistTeacher(Teacher teacher) {
System.out.println("访问者2访问老师:" + teacher.getName() + " 工龄:" + teacher.getWorkYear());
maxWorkYear = Math.max(maxWorkYear,teacher.getWorkYear());
}
}
class PersonStructure{
private List<Person> personList = new ArrayList<>();
public PersonStructure(){
personList.add(new Student("张三",20,70));
personList.add(new Student("李四",21,80));
personList.add(new Student("王五",22,90));
personList.add(new Teacher("李老师",26,3));
personList.add(new Teacher("陈老师",27,4));
personList.add(new Teacher("刘老师",28,5));
}
public void Accept(Visitor visitor){
for (Person person : personList) {
person.Accept(visitor);
}
}
}
abstract class Person{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge() {
return age;
}
public abstract void Accept(Visitor visitor);
}
class Student extends Person{
private int score;
public Student(String name,int age,int score){
super(name,age);
this.score = score;
}
public int getScore(){
return score;
}
@Override
public void Accept(Visitor visitor) {
visitor.VistStudent(this);
}
}
class Teacher extends Person{
private int workYear;
public Teacher(String name,int age,int workYear){
super(name,age);
this.workYear = workYear;
}
public int getWorkYear(){
return workYear;
}
@Override
public void Accept(Visitor visitor) {
visitor.vistTeacher(this);
}
}
3)适用性
Visitor 模式适用于:
- 一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些
操作“污染”这些对象的类。 - 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。
结构化开发方法
模块化
模块化是指将一个待开发的软件分解成若干个小的简单部分--模块,每个模块可独立地开发、测试,最后组装成完整的程序。这是一种复杂问题“分而治之”的原则。模块化的目的是使程序的结构清晰,容易阅读、理解、测试和修改。
模块独立
耦合
耦合是模块之间的相对独立性(互相连接的紧密程度)的度量。耦合取决于各个模块之间接口的复杂程度、调用模块的方式以及通过接口的信息类型等。
- 无直接耦合:指两个模块之间没有直接的关系,属于不同模块。
- 数据耦合:指两个模块之间有调用关系,传递的是简单的数据值。
- 标记耦合:指两个模块之间传递的是数据结构。
- 控制耦合:指一个模块调用另一个模块时,传递的是控制变量。
- 外部耦合:模块间通过软件之外的环境联结。
- 公共耦合:通过一个公共数据环境相互作用。
- 内容耦合:当一个模块直接使用另一个模块的内部数据,或通过非正常入口转入另一个模块内部。
内聚
内聚是对一个模块内部各个元素彼此结合的紧密程度的度量。
- 偶然内聚(巧合内聚):各处理元素之间没有任何联系。
- 逻辑内聚:模块内执行若干个逻辑上相似的功能。
- 时间内聚:把需要同时执行的动作组合在一起。
- 过程内聚:指定的过程执行。
- 通信内聚:模块内的所有处理元素都在同一个数据结构上操作。
- 顺序内聚:指一个模块中的各个处理元素都密切相关于同一功能且必须顺序执行。
- 功能内聚:最强的内聚,指模块内的所有元素共同作用完成一个功能,缺一不可。
总结:耦合性和内聚性是模块独立性的两个定性标准,在将软件系统划分模块时,应尽量做到高内聚、低耦合,提高模块的独立性。
系统结构设计原则
- 分解-协调原则
- 自顶向下的原则
- 信息隐蔽、抽象的原则
- 一致性原则:统一的规范、统一的标准和统一的文件模式。
- 明确性原则:功能明确、接口明确、消除多重功能和无用接口、避免病态连接、降低接口复杂度。
- 模块之间的耦合尽可能小,模块的内聚度尽可能高。(高内聚、低耦合)
- 模块的扇入系数和扇出系数要合理。(扇入扇出适中)
- 模块的规模适当。
- 模块的作用范围应该在其控制范围之内。
系统文档
1)用户与系统分析人员通过文档进行沟通。这里的文档主要包括可行性研究报告、总体规划报告、系统开发合同和系统方案说明书等;系统开发合同和系统方案说明书==项目开发计划
2)系统开发人员与项目管理人员通过文档在项目期内进行沟通。这里的文档主要有系统开发计划(包括工作任务分解表、PERT 图、甘特图和预算分配表等)、系统开发月报以及系统开发总结报告
3)系统测试人员与系统开发人员通过文档进行沟通。系统测试人员可以根据系统方案说明书、系统开发合同、系统设计说明书和测试计划
4)系统开发人员与用户在系统运行期间进行沟通。用户通过系统开发人员撰写的文档运行系统。这里的文档主要是用户手册和操作指南。
5)系统开发人员与系统维护人员通过文档进行沟通。这里的文档主要有系统设计说明书和系统开发总结报告。
6)用户与维修人员在运行维护期间进行沟通。用户在使用信息系统的过程中,将运行过程中的问题进行记载,形成系统运行报告和维护修改建议,系统维护人员根据维护修改建议以及系统开发人员留下的技术手册等文档对系统进行维护和升级。
数据流图
数据字典(DD)
数据字典的内容
- 数据字典是为数据流图中的每个数据流、文件、加工,以及组成数据流或文件的数据项做出说明。
- 数据字典有4类条目:数据流、数据项、数据存储和基本加工。数据项是组成数据流和数据存储的最小元素
加工逻辑的描述
加工逻辑也称为“小说明”。加工逻辑描述方法有结构化语言、判定表和判定树。
结构化语言
外层:顺序结构、选择结构和循环结构
杂记:
-
结构化分析模型包括数据流图、实体联系图、状态迁移图和数据字典
-
根据加工规格说明和控制规格说明进行过程设计;根据数据字典和实体关系图进行数据设计,根据数据流图进行接口设计:根据数据流图进行体系结构设计。
-
体系结构设计:定义软件的主要结构元素及其关系;数据设计:基于E-R图确定文件系统的结构及数据库的表结构;接口设计:用户界面、软件和其他硬件及使用人员的外部接口、以及各种构件之间的内部接口;过程设计:算法及内部数据结构,选定某种过程的表达来描述各种算法
-
结构化方法指导思想是自顶向下、逐层分解,基本原则是功能的分解与抽象。特别适合于数据处理领域的问题,面向对象技术适用于复杂的大规模的项目
-
系统结构图SC包括:模块、模块间的调用、模块之间的通信和辅助控制符号
-
MVC:有利于代码重用;提高系统的可维护性;提高系统的开发效率;由于需要分层调用,所以并不能提高运行效率
-
软件设计模型关注新系统总体结构、代码设计、处理过程、数据结构和界面模型等
-
数据流图:如流程图、NS盒图等,其中决策树和决策表适于用来表示加工中涉及多个逻辑条件的情况。
软件工程
软件过程
1. 能力成熟度模型(CMM)
CMM 将软件过程改进分为以下5个成熟度级别:
1)初始级
软件过程的特点是杂乱无章,有时甚至很混乱,几乎没有明确定义的步骤,项目的成功完全依赖个人的努力和英雄式核心人物的作用。
2)可重复级
建立了基本的项目管理过程和实践来跟踪项目费用、进度和功能特性,有必要的过程准则来重复以前在同类项目中的成功。
3)已定义级
管理和工程两方面的软件过程已经文档化、标准化,并综合成整个软件开发组织的标准软件过程。
4)已管理级
制定了软件过程和产品质量的详细度量标准。软件过程的产品质量都被开发组织的成员所理解和控制。
5)优化级
加强了定量分析,通过来自过程质量反馈和来自新观念、新技术的反馈使过程能不断持续地改进。
2. 能力成熟度模型集成(CMMI)
CMMI 提供了两种表示方法:
1)阶段式模型
阶段式模型的结构类似于 CMM,它关注组织的成熟度。
有五个成熟度等级:
- 初始的:过程不可预测且缺乏控制。
- 已管理的:过程为项目服务。
- 已定义的:过程为组织服务。
- 定量管理的:过程已度量和控制。
- 优化的:集中于过程改进。
2)连续式模型
连续式模型关注每个过程域的能力,一个组织对不同的过程域可以达到不同的过程域能力能力。
CMMI 中包括6个过程域能力等级:
- CL₀(未完成的):过程域未执行或未得到 CL₁ 中定义的所有目标。
- CL₁(已执行的):其共性目标是过程将可标识的输入工作产品转换成可标识的输出工作产品,以实现支持过程域的特定目标。
- CL₂(己管理的):其共性目标集中于己管理的过程的制度化。根据组织级政策规定过程的运作将使用哪个过程,项目遵循己文档化的计划和过程描述,所有正在工作的人都有权使用足够的资源,所有工作任务和工作产品都被监控、控制和评审。
- CL₃(己定义级的):其共性目标集中于己定义的过程的制度化。过程是按照组织的剪裁指南从组织的标准过程集中剪裁得到的,还必须收集过程资产和过程的度量,并用于将来对过程的改进。
- CL₄(定量管理的):其共性目标集中于可定量管理的过程的制度化。使用测量和质量保证来控制和改进过程域,建立和使用关于质量和过程执行的定量目标作为管理准则。
- CL₅(优化的):使用量化(统计学)手段改变和优化过程域,以满足客户要求的改变和持续改进计划中的过程域的功效。
软件过程模型
软件开发过程模型是指为了有效地开发、维护和更新软件系统,提出的一系列步骤、阶段和方法的系统框架,以实现提高软件质量、加快开发速度和降低开发成本的目的。
常见的软件开发过程模型包括瀑布模型、增量模型、演化模型和喷泉模型。
瀑布模型
瀑布模型是一种线性的软件开发过程模型,开发流程严格按照顺序依次进行,每个阶段都必须完成后才能进入下一个阶段。瀑布模型包括需求分析、设计、编码、测试和维护五个阶段。
优点:
-
容易理解,管理成本低;
-
强调开发的阶段性早期计划及需求调查和产品测试。
不足:
- 客户必须能够完整、正确和清晰地表达他们的需要;
- 在开始的两个或 3个阶段中,很难评估真正的进度状态;
- 当接近项目结束时,出现了大量的集成和测试工作;
- 直到项目结束之前,都不能演示系统的能力。
特点:
- 明确的阶段,每个阶段都有明确可执行的目标
- 任务分工明确,各个阶段的任务可以并行开展
- 阶段间有严格的输入、输出关系
不足:
- 迭代能力不强,回退成本高,变更需求引起全局变化成本高
- 不适用于需求不完全确定的项目、对质量控制要求极高的项目
V模型
V模型描述了质量保证活动和沟通、建模相关活动以及早期构建相关的活动之间的关系。
增量模型
增量模型采用了逐步完善的思路,将软件的开发过程划分为一个个的增量,每个增量都能够独立实现某一或多项功能或特性。在逐步实现的过程中,可以不断根据需求变化来进行迭代,从而保证最终的软件达到客户需求和期望。
特点:
- 采用可迭代的方式,适应需求不断变化的局面
- 可集成性强,增量之间保持一致性
- 开发完一个增量可实现部分投入使用
- 具有瀑布模型的所有优点
不足:
- 需求变化频繁,增量之间的界限可能模糊,不便于控制
- 增量标准的确定和成本效益考虑要求高
演化模型
演化模型是一种以进化为中心的软件开发过程模型,侧重于以人的知识和技能为核心,强调在开发过程中不断地学习、改进和重构。演化模型适用于复杂性较高、需求不稳定的软件开发项目。典型的演化模型有原型模型和螺旋模型
特点:
- 适应需求频繁变动和较长开发周期的软件开发项目
- 开发人员可不断地矫正和完善软件,可利用先前的成果进行开发
- 风险得到有效控制,软件质量有所提高
不足:
- 开发人员几乎对整个软件的开发掌握程度较低,软件项目的可控性相对较差
- 软件演化过程因为没有严格的阶段,是软件项目能否成功的关键之一
原型模型
原型方法比较适合于用户需求不清、需求经常变化的情况。当系统规模不是很大也不太复杂时,采用该方法比较好。通过初始原型帮助用户确定需求
螺旋模型
对于复杂的大型软件,加入了风险分析
1.制订计划。确定软件的目标选定实施方案,明确项目开发的限制条件
2.风险分析。分析所选的方案,识别风险,消除风险。
3.实施工程。实施软件开发,验证阶段性产品。
4.用户评估。评价开发工作,提出修正建议,建立下一个周期的开发计划。
喷泉模型
喷泉模型是一种以用户需求为动力,以对象作为驱动的模型,适合于面向对象的开发方法。它克服了瀑布模型不支持软件重用和多项开发活动集成的局限性。使开发过程具有迭代性和无间隙性
统一过程(UP)模型
初启阶段结束时产生一个构想文档;
精化阶段结束时产生一个补充需求分析、一个软件架构描述和一个可执行的架构原型等制品;
构建阶段结束时的成果是个准备交到最终用户手中的产品,包括具有最初运作能力的在适当的平台上集成的软件产品;
移交阶段结束时产生移交给用户产品发布版本
初始阶段:生命周期目标
精化阶段: 生命周期架构
初始运作:功能构建阶段
移交阶段:产品发布。
敏捷方法
敏捷方法是一种反应灵活、拥有高度互动性和以人为本的软件开发方法。它的核心是通过不断地交付成果和及时反馈,来满足客户需求和不断变化的业务环境。以下是敏捷方法中的一些常见实践:
- 极限编程(XP)
- 水晶法(Crystal)
- 并列争求法(Scrum)
- 自适应软件开发(ASD)
- 敏捷统一过程(AUP)
极限编程(XP)
它由价值观、原则、实践和行为 4个部分组成
4 大价值观:沟通、简单性、反馈和勇气。
5 个原则:快速反馈、简单性假设、逐步修改、提倡更改和优质工作。
12 个最佳实践:计划游戏(快速制定计划、随着细节的不断变化而完善)、小型发布(系统的设计要能够尽可能早地交付)、隐喻(找到合适的比喻传达信息、简单设计(只处理当前的需求,使设计保持简单)、测试先行(先写测试代码,然后再编写程序).重构(重新审视需求和设计,重新明确地描述它们以符合新的和现有的需求)、结对编程(可以获得更高的代码质量,编码速度与传统的单人编码相当)、集体代码所有制(任何来开发人员可以堆任何部分进行改变)、持续集成(可以按日甚至按小时为客户提供可运行的版本)、每周工作 40 个小时、现场客户和编码标准。
水晶法(Crystal)
水晶法认为每一个不同的项目都需要一套不同的策略、约定和方法论
并列争求法(Scrum)
并列争求法使用迭代的方法,其中,把每 30 天一次的迭代称为一个“冲刺”
自适应软件开发(ASD)
ASD 有6个基本的原则:有一个使命作为指导;特征被视为客户价值的关键点;过程中的等待是很重要的,因此“重做”与“做”同样关键;变化不被视为改正,而是被视为对软件开发实际情况的调整;确定的交付时间迫使开发人员认真考虑每一个生产的版本的关键需求;风险也包含其中。
敏捷统一过程(AUP)
敏捷统一过程(Agile Unified Process,AUP)采用“在大型上连续”以及在“在小型上迭的原理来构建软件系统。采用经典的 UP 阶段性活动(初始、精化、构建和转换),提供了一系列活动,能够使团队为软件项目构想出一个全面的过程流。
每个AUP迭代执行以下活动:
建模;实现;测试;部署;配置及项目管理;环境管理;
概要设计
- 软件体系结构的设计
- 确定每个模块的功能
- 确定模块之间的调用关系
- 确定模块之间的接口
- 数据结构及数据库设计
- 编写概要设计文档
- 评审
详细设计
- 对每个模块进行详细的算法设计
- 对模块内的数据结构进行设计
- 对数据库进行物理设计
- 其他设计
- 编写详细设计说明书
- 评审
系统测试
意义:系统测试是为了发现错误而执行程序的过程,成功的测试是发现了至今尚未发现的错误的测试。
目的:测试的目的就是希望能以最少的人力和时间发现潜在的各种错误和缺陷。
系统测试原则
- 应尽早并不断地进行测试。
- 测试工作应该避免由原开发软件的人或小组承担。
- 在设计测试方案时,不仅要确定输入数据,而且要根据系统功能确定预期输出结果。
- 在设计测试用例时,不仅要设计有效、合理的输入条件,也要包含不合理、失效的输入条件。
- 在测试程序时,不仅要检验程序是否做了该做的事,还要校验程序是否做了不该做的事。
- 严格按照测试计划来进行,避免测试的随意性。
- 妥善保存测试计划、测试用例。
- 测试例子都是精心设计出来的。
- 系统测试阶段的测试目标来自于需求分析阶段。
单元测试
- 单元测试的测试内容
- 模块接口。测试模块的数据流可以正确地流入、流出。
- 局部数据结构。
- 重要的执行路径。
- 出错处理。
- 边界条件。
- 单元测试过程
- 驱动模块。
- 桩模块。
集成测试
- 自顶向下集成测试(从抽象到具体)
自顶向下集成测试是一种构造软件体系结构的增量方法。不用编写驱动模块,需要编写桩模块的
- 自底向上集成测试(从具体到抽象)
自底向上集成测试就是从原子模块(程序结构的最底层构件)开始进行构造和测试。需要编写驱动模块,不需要编写桩模块的
- 回归测试
软件发生变更,建立了新的数据流路径;回归测试有助于保证变更不引入无意识行为或额外的错误。
- 冒烟测试
冒烟测试是一种常用的集成测试方法,是时间关键项目的决定性机制,它让软件团队频繁地对项目进行评估。
-
三明治:结合自底向上和自顶向下两种测试策略。
-
一次性:对所有构件一次性测试,然后集成
静态测试
静态测试。静态测试是指被测试程序不在机器上运行,而是采用人工检测和计算机辅助静态分析的手段对程序进行检测。
动态测试
黑盒测试
完全不考虑软件的内部结构和特性的情况下
- 等价类划分:有效等价类、无效等价类 eg:0>=x<=100 有效等价类:0到100范围内的数;无效等价类:x小于0的数或者x>100的数
- 边界值分析 eg:取0或者100作为有效边界值,-1或者101作为无效边界值
- 错误推测
- 因果图
白盒测试
- 逻辑覆盖。逻辑覆盖考察用测试数据运行被测程序时对程序逻辑的覆盖程度
- 菱形是判断分支;矩形是语句;->是程序的流向
-
语句覆盖:语句覆盖是指选择足够的测试数据,使被测试程序中的每条语句至少执行一次。语句覆盖对程序执行逻辑的覆盖很低,因此一般认为它是很弱的逻辑覆盖。
-
判定覆盖:使得被测程序中的每个判定表达式至少获得一次“真”值和“假”值,或者说是程序中的每个取“真”分支和取“假”分支至少都通过一次,因此判定覆盖也称为分支覆盖。判定覆盖要比语句覆盖更强一些。
-
条件覆盖:条件覆盖是指构造一组测试用例,使得每一判定语句中每个逻辑条件的各种可能的值至少满足一次。
-
判定/条件覆盖:使得判定中每个条件的所有可能取值(真/假)至少出现一次,并使每个判定本身的判定结果!(真/假) 也至少出现一次。
-
条件组合覆盖:使得每个判定中条件的各种可能值的组合都至少出现一次。满足条件组合覆盖的测试用例是定满足判定覆盖、条件覆盖和判定/条件覆盖的。
- 路径覆盖:路径覆盖是指覆盖被测试程序中所有可能的路径。有几个判定语句就有几条路径
- 循环覆盖
- for循环/while循环
- do-while循环
- 基本路径测试
系统维护概述
1. 系统可维护性概念
系统是否能被很好地维护,可以用系统的可维护性这一指标来衡量。
系统可维护性的评价指标
- 可理解性。
- 可测试性。
- 可修改性。
文档是软件可维护性的决定因素,必须在开发阶段保证软件具有可维护性的特点
编写高质量文档可以提高软件开发的质量。
文档也是软件产品的一部分,没有文档的软件就不能称之为软件。
软件文档的编制在软件开发工作中占有突出的地位和相当大的工作量高质量文档对于软件产品的效益有着重要的意义。
总的来说,软件文档只好不坏,选项中说软件文档不好的就是不正确的。
2.系统维护的内容及类型
系统维护主要包括硬件维护、软件维护和数据维护
软件维护:
-
正确性维护。正确性维护是指改正在系统开发阶段已发生而系统测试阶段尚未发现的错误。
-
适应性维护。适应性维护是指使应用软件适应信息技术变化和管理需求变化而进行的修改。
-
完善性维护。这是为扩充功能和改善性能而进行的修改,主要是指对已有的软件系统增加一些在系统分析和设计阶段中没有规定的功能与性能特征。
-
预防性维护。为了改进应用软件的可靠性和可维护性,为了适应未来的软/硬件环境的变化。
可靠性、可用性利可维护性是软件的质量属性,软件工程中,用 0-1 之间的数来度量。
可靠性是指一个系统对于给定的时间间隔内、在给定条件下无失效运作的概率。可以用 MTTF/(1+MTTF) 来度量,其中 MTTF 为平均无故障时间。
可用性是在给定的时间点上,一个系统能够按照规格说明正确运作的概率。可以用 MTBF/(1+MTBF) 来度量,其中 MTBF 为平均失效间隔时间。
可维护性是在给定的使用条件下,在规定的时间间隔内,使用规定的过程和资源完
成维护活动的概率。可以用 1/(1+MTTR) 来度量,其中 MTTR 为平均修复时间。
McCabe度量法
计算有向图 G 的环路复杂性的公式为:
-
V(G)=m-n+2,其中V(G)是有向图 G中的环路个数,m 是G中的有向弧数,n 是G中的节点数
-
闭合区域+1
沟通路径
软件项目估算
COCOMO 模型
项目估算是项目计划和管理的一个至关重要的方面。
COCOMO 模型是一种精确的、易于使用的成本估算模型。COCOMO 模型按其详细程度分为基本 COCOMO 模型、中级 COCOMO 模型和详细 COCOMO 模型。
1)基本 COCOMO 模型:是一个静态单变量模型
2)中级 COCOMO 模型:是一个静态多变量模型
3)详细 COCOMO 模型:它将软件系统模型分为系统、子系统和模块 3 个层次,除包括中级模型所考虑的因素外,还考虑了在需求分析、软件设计等每一步的成本驱动属性的影响。
COCOMOII模型
COCOMOII也是一种层次结构的估算模型,被分为 3 个阶段性模型:
- 应用组装模型
- 早期设计阶段模型
- 体系结构阶段模型
COCOMOI模型也需要使用规模估算信息,在模型层次结构中有 3 种不同的规模估算选择:对象点、功能点和代码行。应用组装模型使用的是对象点:早期设计阶段模型使用的是功能点,功能点可以转换为代码行。
进度管理
甘特图(Gant)
当日历中同一时段存在多个水平条时,表示任务之间的并发
优点:
能清晰地描述每个任务从何时开始,到何时结束,任务的进展情况以及各个任务之间的并行性。
不足:
它不能清晰地反映出各任务之间的依赖关系,难以确定整个项目的关键所在也不能反映计划中有港力的部分。
项目计划评审技术图(PERT)
PERT 图是一个有向图,图中的箭头表示任务,它可以标上完成该任务所需的时间。
只有当流入该结点的所有任务都结束时,结点所表示的事件才出现,流出结点的任务才可以开始。
开始结点的最早时刻=0;结束结点的最迟结点==结束结点的最早时刻
松弛时间=最迟时间-最早时间
关键路径就是松弛时间等于零的结点
优点:
不仅给出了每个任务的开始时间、结束时间和完成该任务所需的时间,还给出了任务之间的关系,即哪些任务完成后才能开始另外一些任务,以及如期完成整个工程的关键路径。图中的松弛时间则反映了完成某些任务时可以推迟其开始时间或延长其所需完成的时间
不足:
PERT 图不能反映任务之间的并行关系。
项目活动图
软件配置管理
软件配置管理其主要目标包括: 变更标识、变更控制、版本控制、确保变更正确的实现、变更报告
软件配置管理其主要内容包括:版本管理、配置支持、变更支持、过程支持、团队支持、变化报告、审计支持。
软件配置管理其主要内容包括:软件配置标识、变更管理、版本控制、系统建立、配置审核、配置状态报告。
配置数据库可以分为以下三类:
(1) 开发库。专供开发人员使用,其中的信息可能做频繁修改,对其控制相当宽松。
(2)受控库。在生存期某一阶段工作结束时发布的阶段产品,这些是与软件开发工作相关的计算机可读信息和人工可读信息。软件配置管理正是对受控库中的各个软件项进行管理,受控库也称为软件配置库。
(3) 产品库。在开发的软件产品完成系统测试后,作为最终产品存入产品库,等待交付用户或现场安装。
风险
风险管理
两个特性:不确定性和损失。不确定性是指风险可能发生也可能不发生;损失是指如果风险发生,就会产生恶性后果。
类型:
项目风险威胁到项目计划。项目风险是指预算、进度:人员(聘用职员及组织)、资源、利益相关者、需求。项目复杂度、规模及结构不确定性也属于项目风险因素。
技术风险威胁到要开发软件的质量及交付时间。技术风险是指设计、实现、接口、验证和维护等方面的潜在问题。
商业风险威胁到要开发软件的生存能力,且常常会危害到项目或产品。5 个主要的商业风险如下。
(1) 市场风险。
(2) 策略风险。
(3) 销售风险。
(4) 管理风险。
(5) 预算风险。
风险识别
风险识别试图系统化地指出对项目计划(估算、进度、资源分配等) 的威胁。识别出已知风险和可预测风险后,项目管理者首先要做的是在可能时回避这些风险,在必要时控制这些风险。
风险预测(风险估计)
风险预测又称风险估计,它试图从两个方面评估一个风险:风险发生的可能性或概率;如果风险发生了所产生的后果。
- 风险预测活动
通常,项目计划人员与管理人员、技术人员一起进行以下 4 步风险预测活动。
(1) 建立一个尺度或标准,以反映风险发生的可能性。
(2) 描述风险产生的后果。
(3) 估算风险对项目和产品的影响。
(4) 标注风险预测的整体精确度,以免产生误解
2 )评估风险影响
如果风险真的发生,有 3 个因素可能会影响风险所产生的后果,即风险的本质、范围和时间。
风险的本质是指当风险发生时可能带来的问题。
风险的范围包括风险的严重性(即风险有多严重)及风险的整体分布情况
风险的时间是指何时能够感受到风险的影响及风险的影响会持续多长时间
整体的风险显露度(Risk Exposure,RE) 可由下面的关系确定:
RE=P*C
其中,P是风险发生的概率,C 是风险发生时带来的项目成本
风险评估
一种对风险评估很有用的技术就是定义风险参照水准。成本、进度和性能就是 3 种典型的风险参照水准。也就是说,对于成本超支、进度延期、性能降低(或它们的某种组合),有一个表明导致项目终止的水准。
风险控制
风险控制的目的是辅助项目组建立处理风险的策略。一个有效的策略必须考虑以下 3 个问题:
1)风险避免
应对风险的最好办法是主动地避免风险,即在风险发生前分析引起风险的原因,然后采取措施,以避免风险的发生。
- 风险监控
项目管理者应监控某些因素,这些因素可以提供风险是否正在变高或变低的指示。
- RMMM计划
风险管理策略可以包含在软件项目计划中,或者风险管理步骤也可以组织成一个独立的风险缓解、监控和管理计划(RMMM 计划)。
建立了 RMMM计划,而且项目已经启动之后,风险缓解及监测步骤也就开始了。风险缓解是一种问题规避活动,而风险监测是一种项目跟踪活动,所以风险监测的另一个任务就是试图找到“起源”
软件质量模型
软件质量是指反映软件系统或软件产品满足规定或隐含需求的能力的特征和特性全体。软件质量管理是指对软件开发过程进行独立的检查活动,由质量保证、质量规划和质量控制3 个主要活动构成。软件质量保证是指为保证软件系统或软件产品充分满足用户要求的质量而进行的有计划、有组织的活动,其目的是生产高质量的软件。
软件质量特性
ISO/IEC9126 软件质量模型
ISO/IEC 9126 软件质量模型由 3 个层次组成:第一层是质量特性,第二层是质量子特性、第三层是度量指标。
1)功能性 (Functionality)。功能是指满足规定或隐含需求的那些功能。
适应性 (Suitability)。是否适合有关的软件属性。
准确性 (Accurateness)。与能够得到正确或相符的结果
互用性(Interoperability)。与其他指定系统进行交互操作的能力相关的软件属性。
依从性(Compliance)。使软件服从有关的标准、约定、法规及类似规定的软件属性。
安全性Security)。与避免对程序及数据的非授权故意或意外访问的能力有关的软件属性。
2)可靠性 (Reliability)。软件维持在其性能水平有关的能力。
成熟性(Maturity)。与由软件故障引起失效的频度有关的软件属性。
容错性 (Fault tolerance)。与在软件错误或违反指定接口的情况下
易恢复性 (Recoverability)。与在故障发生后,重新建立其性能水平并恢复直接受影响数据的能力
2) Mc Call 软件质量模型
Mc Call 也给出了一个三层模型框架,第一层是质量特性,第二层是评价准则,第三层是度量指标。
软件评审
- 设计的规格说明书符合用户的要求,这称为设计质量
- 程序按照设计规格说明所规定的情况正确执行,这称为程序质量。
- 评审的主要目标是为了发现软件中的错误
模块结构。模块分为处理模块和数据模块两类,模块间的动态结构也与这些模块分类有关。对这样的模块结构进行检查的项目如下。
- 控制流结构。
- 数据流结构。
- 模块结构与功能结构之间的对应关系。
软件工具
用来辅助软件开发、运行、维护、管理和支持等过程中的活动的软件称为软件工具。
软件维护工具
辅助软件维护过程中活动的软件称为软件维护工具,软件维护工具主要有版本控制工具、文档分析工具、开发信息库工具、逆向工程工具(需求阶段)和再工程工具。
容错技术
容错技术是对某些无法避开的差错,使其影响减至最小的技术。
通常冗余技术:结构冗余、信息冗余、时间冗余和冗余附加技术。
在屏蔽硬件错误的容错技术中,冗余附加技术包括:关键程序和数据的冗余存储及调用: 检测、表决、切换、重构、纠错和复算的实现
在屏蔽软件错误的容错技术中,冗余附加技术包括:冗余备份程序的存储及调用;实现错误检测和错误恢复的程序;实现容错软件所需的固化程序
嵌入式操作系统
特点:微型化;可定制;实时性;可靠性;可移植性
嵌入式系统初始化过程可以分为3个主要环
节,按照自底向上、
从硬件到软件的次序依次为:片级初始化、板级初始化和系统级初始化。
- 片级初始化:完成嵌入式微处理器的初始化
- 板级初始化完成嵌入式微处理器以外的其他硬件设备的初始化。
- 系统初始化过程以软件初始化为主,主要进行操作系统的初始化。
杂记:
- 软件工程是一种层次化的技术,从底向上分别为质量、过程、方法和工具。
- RUP应用了角色、活动、制品和工作流4种重要的模型元素,其中角色表述“谁做”,制品表述“做什么”,活动表述“怎么做”,工作流表述“什么时候做”。
- 数据处理领域的不太复杂的软件,适于用结构化方法进行开发。
- 为了确定构建软件系统所需的人数,需要考虑软件系统的规模、系统的技术复杂性、项目计划和开发人员的技术背景
- 软件需求包括功能需求、非功能需求和设计约束三个方面的内容。功能需求是所开发的软件必须具备什么样的功能:非功能需求是指产品必须具备的属性或品质,如可靠性、性能、响应时间和扩展性等等
- 软件系统中的功能分为四类:
- 传入模块:输入数据;传出模块:输出数据
- 变换模块:从上级调用,经过特定的处理转换成其他形式,将结果返回给调用模块
- 协调模块:一般不对数据加工,主要通过调用、协调和管理其他模块完成特定功能
- 软件可靠性与软件的潜在错误的数量.位置有关,与软件产品的使用方式有关
信息安全
全性、可靠性与系统性能评测基础知识
加密技术和认证技术
公加验,私解签
- 加密技术(窃听):
- 对称加密(私有密钥加密)
加密和解密是同一把密钥,只有一把密钥
密钥分发有缺陷 1. 加密解密速度很快 2. 适合加密大量明文数据 - 非对称密钥(公开密钥加密)
加密和解密不是同一把密钥,一共有两把密钥 分别是公钥和私钥
用公钥加密只能用私钥解密 用私钥加密只能用公钥解密
不能通过一把推出另一把 用接收方的公钥加密明文,可以实现防止窃听的效果
密钥分发没有缺陷 1.加密解密速度很慢 - 混合加密
- 对称加密(私有密钥加密)
- 认证技术:
- 摘要(篡改):将发送的明文进行Hash算法后得到的摘要放在密文后一起发送过去,与接收方解密后的明文进行相同的Hash算法得到的摘要进行对比,如果一致,则没有篡改,否则有篡改。
- 数字签名(假冒,否认):发送方用自己的私钥对摘要进行签名(加密)得到数字签名放在密文后一起发送过去
接收方用发送方的公钥对数字签名进行验证(解密)如果验证成功则该消息没有被假冒且不能否认,否则该消息的真实性为假冒发送。 - 数字证书(假冒):用户向CA(权威机构)机构申请数字证书,将个人信息和公钥发给CA机构,CA机构颁给用户数字证书,数字证书用CA的私钥进行签名(加密)用CA的公钥验证(解密)数字证书得到用户的公钥。
认证是处理主动攻击
对称密钥(私钥、私有密钥加密)算法(享密钥加密算法) | 非对称密钥(公钥、公开密钥加密)算法 |
---|---|
DES | RSA |
3DES | ECC |
RC-5 | DSA |
IDEA | |
AES | |
RC4 |
Hash函数
MD5 摘要算法(128位散列值)
SHA-1 安全散列算法
防火墙技术
防火墙(Firewall)是建立在内外网络边界上的过滤封锁机制。
- 包过滤防火墙
过滤型的防火墙通常是直接转发报文,它对用户完全透明,速度较快。
优点:低水平控制;每个IP包的字段都被检查,eg:源地址;目的地址;协议和端口;
缺点:不能防范黑客攻击 不支持应用层协议、不能处理新的安全威胁。
- 应用代理网关防火墙
内网用户对外网的访问变成防火墙对外网的访问,然后再由防火墙转发给内网用户。
优点;可以检查应用层、传输层和网络层的协议特征,对数据包的检测能力比较强;
缺点:难以配置,处理速度非常慢
- 状态检测技术防火墙
状态检测技术防火墙结合了代理防火墙的安全性和包过滤防火墙的高速度等优点。
病毒
计算机病毒的特征包括:传播性、隐蔽性、感染性、潜伏性、触发性、破坏性等
worm表示蠕虫病毒、Trojan表示特洛伊木马、Backdoor表示后门病毒、Macro表示宏病毒
宏病毒感染的对象主要是文本文档、电子表格等
木马软件: 冰河
蠕虫病毒:欢乐时光、熊猫烧香、红色代码、爱虫病毒、震网
网络攻击
拒绝服务攻击 (Dos攻击) : 目的是使计算机或网络无法提供正常的服务拒绝服务攻击是不断向计算机发起请求来实现的
重放攻击: 攻击者发送一个目的主机已经接受过的报文来达到攻击目的攻击者利用网络监听或者其他方式盗取认证凭据,之后再重新发送给认证服务器主要用于身份认证过程,目的是破坏认证的正确性。
口令入侵攻击: 使用某些合法用户的账号和口令登录到目的主机,然后再实施攻击活动
特洛伊木马:被伪装成程序或游戏,当用户下载了带有木马的软件或附件时,这个程序:就会向黑客发起连接请求,建立连接后黑客就实施攻击活动。
端口欺骗攻击: 采用端口扫描找到系统漏洞从而实施攻击
网络监听: 攻击者可以接收某一网段在同一条物理通道上传输的所有信息,使用网络监听可以轻松截取包括账号和口令在内的信息资料
IP欺骗攻击:产生的IP数据包为伪造的源IP地址,以便冒充其他系统或发件人的身份。
SQL注入攻击: 是黑客对数据库进行攻击的常用手段之一没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。攻击者可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据首先获取数据库的权限,就可获取用户账号和口令信息,以及对某些数据修改等。
入侵检测技术: 专家系统、模型检测、简单匹配
网络安全
杂记:
- 安全需求可划分为物理线路安全选择题网络安全、系统安全和应用安全。
- 机房安全属于物理安全,入侵检测属于网络安全,漏洞补丁管理属于系统安全,而数据库安全则是应用安全。
数据结构
线性结构
栈
顺序栈
链栈
队列
顺序队列和循环队列
计算队尾元素的指针为:(Q.front+len-1+M)%M
计算队头元素的指针为:(Q.rear-len+1+M)%M
队列中元素个数为:(rear-front+M)%M
链队列和双端队列
串
串的基本概念:
- 空串:长度为零的串称为空串,空串不包含任何字符
- 空格串:由一个或多个空格组成的串
- 子串: 由串中任意长度的连续字符构成的序列称为子串。含有子串的串称为主串。子串在主串中的位置是指子串首次出现时,该子串的第一个字符在主串中的位置。空串是任意串的子串。
- 串相等:指两个串长度相等且对应序号的字符也相同
- 串比较:两个串比较大小时以字符的 ASCI 码值 (或其他字符编码集合) 作为依据比较操作从两个串的第一个字符开始进行,字符的码值大者所在的串为大;实质上若其中一个串先结束,则以串长较大者为大。
串的模式匹配和朴素匹配
手算next数组
KMP
数组求址
矩阵
对称矩阵&三对角矩阵
稀疏矩阵
稀疏矩阵三元组表的顺序存储结构称为三元组顺序表,常用的三元组表的链式存储结构是十字链表
非线性结构
树
基本概念
性质
二叉树
基本概念
性质
满二叉树和完全二叉树
二叉树的顺序存储
二叉树的链式存储
二叉链表:n个结点,有2n个指针域,有n-1个有效指针,空指针有n+1个
三叉链表:n,3n,指向子节点和指向父节点有效指针都为n-1,空指针为3n-(n-1)-(n-1)=n+2
二叉树的遍历
先中后遍历、层次遍历(从上往下,从左往右)
只有通过先序和中序,或通过中序和后序,才可以唯一确定一个二叉树
平衡二叉树
:二叉树中的任意一个结点的左右子树高度之差的绝对值不超过1
完全二叉树是一个平衡二叉树,平衡二叉树不一定是一个完全二叉树
二叉排序树
二叉排序树(二叉查找树):根结点的关健字
大于左子树所有结点的关键字,
小于右子树所有结点的关键字,左右子树也是一颗二叉排序树
中序遍历得到的序列是有序序列
哈夫曼树(最优二叉树)
具有n个叶子结点的权值的最优二叉树不唯一,WPL值是唯一确定的
权值大的结点里根节点更近,则相反
构造规则:
- 从前往后,找两个权值最小
- 小左大右
- 加入末尾
- 权值相同从前往后
- 用时在调
哈夫曼编码
左分支设0,右分支设1
哈夫曼编码压缩比
线索二叉树
先看什么排序,再利用空指针域去指向
图
完全图
:总度数=2e(无论有向还是无向),e=1/2总度数
无向完全图最少有n-1条边;有向完全图最少有n条边
路径
:第一个顶点和最后一个顶点相同的路径称为回路或环。顶点均不相同,则称其为简单路径。
图的存储结构
邻接矩阵
无向图的邻接矩阵是对称的,有向图的邻接矩阵则不一定对称。
借助于邻接矩阵容易判定任意两个顶点之间是否有边(或弧) 相连,并且容易求得各个顶点的度。
对于无向图,顶点 v的度是邻接矩阵第i行(或列)中值不为 0 的元素个数;对于有向图,有第 i行(或列) 中值不为 0 的元素个数是顶点 的出度 ,第j列的非 0 元素个数是顶点的入度
邻接表
邻接表可得出:出度
逆邻接表可得出:入度
稠密图和稀疏图
邻接矩阵用于存储稠密图,邻接表用于存储稀疏图
图的遍历
深度优先遍历
某顶点查找其邻接点的过程
邻接矩阵:O(n2);邻接表:O(n+e)
回溯,由递归来实现
广度优先遍历
某顶点开始,访问该层的所有邻接点,再把访问完的邻接点作为开始结点,由队列实现,邻接矩阵:O(n2);邻接表:O(n+e)
拓扑排序
有向无环图
(1)在 AOV 网中选择一个入度为0(没有前驱)的顶点且输出它。
(2) 从网中删除该顶点及与该顶点有关的所有弧。
(3) 重复上述两步,直到网中不存在入度为 0 的顶点为止
查找
静态查找表:顺序查找、折半查找(二分)、分块查找
动态查找表:二叉排序树、平衡二叉树、B_树、哈希表
顺序查找
适用于顺序存储和链式存储(可无序)
折半(二分)查找
顺序表,最多比较log2n+1次
哈希表
除留取余法:H(key)=key%m (m取接近n 但不大于n的质数)
解决冲突:开放地址法:Hi=(H(key)+di)%m
增量序列:
- 线性探测法:di=1,2,3……
- 二次探测法:12,-12,22,-22
堆
小根堆
根结点最小,从下往上构造,一层一层来
大根堆
根结点最大
排序
直接插入和希尔排序
简单选择排序和堆排序
堆排序
每一次排序可以确定一个结点的位置
冒泡和快速排序
冒泡排序
每次排序可以确定最大/最小的数
快速排序
分治的思想
归并排序
各排序算法的比较
算法设计与分析
时间复杂度
算法时间复杂度以算法中基本操作重复执行的次数(简称为频度)作为算法的时间度量。一般不必要精确计算出算法的时间复杂度,只要大致计算出相应的数量级即可,如O(1)、O(㏒₂n)、O(n)或O(n²)等。
递归式时间复杂度(适用于每次递归的时间复杂度不变):递归的次数 x 每次递归的时间复杂度
主方法。主方法也称为主定理,给出了求解以下形式的递归式的快速方法。
空间复杂度
非递归:O(1) O(n) O(n²)
渐进符号
回溯法
0-1背包
n皇后问题
分治法(快速排序)
递归有两个基本要素:
- 边界条件,即确定递归到何时终止,也称为递归出口
- 递归模式,即大问题是如何分解为小问题的,也称为递归体
分支算法在每一层递归上都有 3 个步骤:
- 分解。将原问题分解成一系列子问题。
- 求解。递归地求解各子问题。若子问题足够小,则直接求解。
- 合并。将子问题的解合并成原问题的解。
归并排序算法
#include<stdio.h>
#define INT_MAX 2147483647
/**
* 归并排序
**/
void Merge(int A[],int p,int q,int r){
int n1 = q - p + 1,n2 = r -q,i,j,k;
int L[50],R[50];
for(i=0;i<n1;i++)
L[i] = A[p+i];
for(j=0;j<n2;j++)
R[j] = A[q+j+1];
L[n1] = INT_MAX;
R[n2] = INT_MAX;
i=0;
j=0;
for(k=p;k<r+1;k++){
if(L[i] < R[j]){
A[k] = L[i];
i++;
}else{
A[k]=R[j];
j++;
}
}
}
void MergeSort(int A[],int p,int r){
int q;
if(p < r){
q = (p+r) / 2;
MergeSort(A, p, q);
MergeSort(A, q+1, r);
Merge(A, p, q,r);
}
}
int main(){
int A[] = {4,1,3,6,7,5,2,9};
MergeSort(A, 0, 7);
int i;
for (i = 0; i<8; i++) {
printf("%d ",A[i]);
}
printf("\n");
return 0;
}
最大字段和问题
#include<stdio.h>
#include<stdlib.h>
int MaxSubSum(int * Array,int left,int right){
int sum = 0;
int i;
if(left == right){ /*分解到单个整数,不可继续分解*/
if(Array[left] > 0)
sum = Array[left];
else
sum = 0;
}else{
/*从 left 和 right 的中间分解数组*/
int center = (left + right)/2; /*划分的位置*/
int leftsum = MaxSubSum(Array, left, center);
int rightsum = MaxSubSum(Array, center+1, right);
/*计算包括 center 的最大值,判断是情形1、情形2还是情形3*/
int s1 = 0;
int lefts = 0;
for(i = center;i >= left;i--){
lefts = lefts + Array[i];
if(lefts > s1)
s1 = lefts;
}
int s2 = 0;
int rights = 0;
for(i = center + 1;i<= right;i++){
rights = rights + Array[i];
if(rights > s2)
s2 = rights;
}
sum = s1 + s2;
/*情形1*/
if (sum < leftsum) {
sum = leftsum;
}
/*情形2*/
if(sum < rightsum){
sum = rightsum;
}
}
return sum;
}
int main(){
int *Array = (int *)malloc(6*sizeof(int));
Array[0] = -2;
Array[1] = 11;
Array[2] = -4;
Array[3] = 13;
Array[4] = -5;
Array[5] = -2;
int result = MaxSubSum(Array, 0, 5);
printf("%d\n",result);
return 0;
}
动态规划法
(0-1背包问题,最长公共子序列问题;寻找最优解)
0-1 背包问题
#include<stdio.h>
#define N 4 // 物品数量
#define W 5 // 背包容量
int max(int a,int b){
return a > b ? a : b;
}
int main(){
int v[] = {0,2,4,5,6}; // 物品价值数组
int w[] = {0,1,2,3,4}; // 物品重量数组
int f[N + 1][W + 1] = {}; // 子问题解数组
int i,j;
for(i=1;i<=N;i++){
for(j=1;j<=W;j++){
if(j >= w[i]){ // 选第 i 个物品的前提条件
// 等于不选第 i 个物品 和 选第 i 个物品 两者的较大值
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]] + v[i]);
}else{ // 不选第 i 个物品
f[i][j] = f[i - 1][j]; // 等于从前 i-1 个物品中选,背包容量为 j 时的最大价值
}
}
}
printf("%d\n",f[N][W]);
return 0;
}
时间复杂度:O(N*W) N:物品数量 W:背包容量
矩阵连乘问题
- 时间复杂度:O(n³)
- 空间复杂度O(n²)
贪心法
(活动选择,背包问题)
部分背包问题
#include<stdio.h>
#define N 5 // 物品数量
#define W 100 // 背包容量
// 显示物品价值、重量、单位重量价值数组
void show(int v[],int w[],double vw[]){
int i;
printf("物品价值数组:");
for(i = 1;i<=N;i++) printf("%d ",v[i]);
printf("\n");
printf("物品重量数组:");
for(i = 1;i<=N;i++) printf("%d ",w[i]);
printf("\n");
printf("物品单位重量价值数组:");
for(i = 1;i<=N;i++) printf("%0.1lf ",vw[i]);
printf("\n");
}
double Max_Value(int v[],int w[],double vw[]){
double result = 0.0;
int i;
int w_temp = W;
for(i=1;i<=N;i++){
if(w_temp >= w[i]){
result = result + v[i];
w_temp = w_temp - w[i];
}else{
break;
}
}
if(w_temp > 0 && i<=N){
result = result + w_temp * vw[i];
}
return result;
}
int main(){
int v[] = {0,65,20,30,60,40}; // 物品价值数组
int w[] = {0,30,10,20,50,40}; // 物品重量数组
double vw[N + 1]; // 物品单位重量价值数组
int i;
// 初始化 物品单位重量价值数组
for(i = 1;i<=N;i++) vw[i] = (double) v[i] / w[i];
show(v, w, vw);
double result =Max_Value(v, w, vw);
printf("\nreslut %.1lf\n",result);
return 0;
}
最优子结构含有重叠子问题使用动态规划法,或者含有贪心选择结构使用贪心算法
标签:对象,void,软考,笔记,class,int,new,public From: https://www.cnblogs.com/mglblog/p/17728897.html