高级软件工程学习笔记
工欲善其事必先利其器
VSCode
优点:
简洁(专注于编辑器,代码理解,版本控制,远程开发,debug)
进程隔离的插件模型 把插件放到单独的进程里,无法干扰主进程代码的执行,保障稳定性。
UI 渲染与业务逻辑隔离 VSCode 统管所有用户界面交互,制定用户界面交互的标准,插件能做的就是响应请求,专注于业务逻辑处理。
代码理解的基础 LSP和DAP协议
- 用于代码理解和调试的第三方插件与VS Code主进程之间的桥梁就是两大协议
- 多语言支持的基础就是Language Server Protocol(LSP)
- 节制的设计 关注物理实体(比如文件、目录)和状态(光标位置)。不关心语言特性,编译
- 基于 JSONRPC的协议 易读,各大语言都支持
远程开发
Remote Development(VSCRD) 响应迅速,沿用本地设置,数据传输开销小(UI在本地,数据请求服务器)
(避免环境配置,本机性能太差)
常用命令
- 打开文件夹( Ctrl+O)
- 新建文件(Ctrl+N)
- 关闭文件(Ctrl+W)
- 编辑文件和保存文件(Ctrl+S)
- 文件内搜索(Ctrl+F)
- 关闭所有文件(Ctrl+K W)
- 关闭已保存的文件(Ctrl+K U)
- 单行代码注释和取消注释Ctrl+/ (//)
- 代码块注释和取消注释Alt+Shift+A(/**/)
- Ctrl+Shift+E 文件资源管理器
- Ctrl+Shift+G 源代码管理
- Ctrl+Shift+F 跨文件搜索
- Ctrl+Shift+H 跨文件替换
- Ctrl+Shift+D 启动和调试
- Ctrl +Shift+P调出VS Code命令行
- Ctrl+Shift+M查看错误和警告
- Ctrl+Shift+X 管理扩展插件
- Ctrl+~打开终端
Git
基本设置和原理
VSCode自带git
sudo apt install git # 在Linux上安装Git
git init # 在一个新建的目录下创建版本库
git clone https://github.com/YOUR_NAME/REPO_NAME.git # 通过clone远端的版本库从而在本地创建一个版本库
原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xIxBrcIq-1657440974364)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220706225040495.png)]
git pull = git fetch + git merge
常用命令
- git init # 初始化一个本地版本库
- git status # 查看当前工作区(workspace)的状态
- git add [FILES] # 把文件添加到暂存区(Index)
- git commit -m "wrote a commit log infro” # 把暂存区里的文件提交到仓库
- git log # 查看当前HEAD之前的提交记录,便于回到过去
- git reset —hard HEAD^^/HEAD~100/commit-id/commit-id的头几个字符 # 回退(/分隔几种标识commit方式)
- git reflog # 可以查看当前HEAD之后的提交记录,便于回到未来
- git reset —hard commit-id/commit-id的头几个字符 # 回退
几个概念
过去和未来之间的分界点就是HEAD,即当前工作区所依赖的版本。
line diff 增量补丁,按行对比(line diff)将差异的部分制作成一个增量补丁。
commit是存储到仓库里的一个版本,是整个项目范围内的一个或多个文件的增量补丁合并起来,形成项目的增量补丁,是一次提交记录。每个提交(commit)都生成一个唯一的commit ID。
branch是按时间线依次排列的一组提交记录(commit),理论上可以通过当前branch上最初的提交(commit)依次打补丁直到HEAD得到当前工作区里的源代码。
tag标签就是某次提交(commit)的commit ID的别名。
默认merge为快进式合并,将分支的commit合并到主分支,–no-ff关闭
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOMlxlZG-1657440974365)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220706230448846.png)]
合作流程
pull/clone下来后新建分支 commit后
切换到master后pull,然后git merge --no-ff mybranch(另一种方式:fork+push分支,然后在仓库中提pull request即可)
git rebase整理一下自己的提交记录,注意不要通过rebase对任何已经提交到远程仓库中的commit进行修改。
(公司合作一般禁用,会将提交记录打乱)
- git rebase -i [startpoint] [endpoint]
- -i 为弹出交互界面(在里面可以设置提交信息,忽略某次提交等)
- [startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支的HEAD。一旦指定, 则表示 [endPoint]后面的commit全部不要了!
- 一般只指定[startpoint] ,可以使用HEAD^^、HEAD~100、commit ID或者commit ID的头几个字符来指定一个commit节点
- 重新整理HEAD之前的两个commit节点 git rebase -i HEAD^^
rebase另一种用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEAQlODi-1657440974365)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220706235006143.png)]
此时 develop rebase feature等于merge feature
此时develop rebase feature会将可能在a之后的提交b放到前面,同时a被移除,改为a’,hashid改变
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q94v3dNn-1657440974366)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220707000214838.png)]
如果先feature rebase develop,再merge develop,这样就相当于直接在a上进行开发,不会有多余记录
git rebase的两种用法(最全)_小垚尧的博客-CSDN博客_git rebase
Vim
vim是一个程序开发工具,由文本编辑器vi发展而来。拥有代码补完、编译及错误跳转等功能。以字体颜色辨别语法的正确性。
三种模式
命令模式(Command mode)
输入模式(Insert mode)在命令模式下按下i就进入了输入模式,按ESC退出输入模式
底线命令模式(Last line mode)在命令模式下按下:(英文冒号)就进入了底线命令模式。
移动光标
- h 或 向左箭头键(←)
- j 或 向下箭头键(↓)
- k 或 向上箭头键(↑)
- l 或 向右箭头键(→)
- 向下移动 30 行,可以使用 “30j” 或 “30↓” 的组合按键
- 0 或功能键 [Home] 移动到这一行的最前面字符处
- $ 或功能键[End] 移动到这一行的最后面字符处
- G 移动到这个档案的最后一行
- gg 移动到这个档案的第一行,相当于 1G
- n 光标向下移动 n 行
删除
- x, X x 为向后删除一个字符 (相当于 [del] 按键), X 为向前删除一个字符(相当于 [backspace] 亦即是退格键)
- dd 删除光标所在的一整行
- ndd 删除光标所在的向下 n 行
- d$ 删除游标所在处,到该行的最后一个字符
- d0 删除游标所在处,到该行的最前面一个字符
复制与粘贴
- yy 复制光标所在的一行
- p, P p将已复制的数据在光标下一行粘贴,P则粘贴在游标上一行
- nyy 复制光标所在的向下 n 行
- y0 复制光标所在的那个字符到该行行首的所有数据
- y$ 复制光标所在的那个字符到该行行尾的所有数据
撤销与重做
- u 撤销
- [Ctrl]+r 重做上一个动作
搜索与替换
- /word 向光标之下寻找 word
- ?word 向光标之上寻找 word
- n 英文按键。代表重复前一个搜寻的动作
- N 英文按键。与 n 刚好相反,『反向』进行前一个搜寻动作
- :n1,n2s/word1/word2/g 在第 n1 与 n2 行之间寻找 word1 这个字符串,并将该字符串取代为 word2
- s是substitute的简写,表示执行替换字符串操作
- g(global)表示全局替换;c(comfirm)表示操作时需要确认;i(ignorecase)表示不区分大小写 (后缀)
编辑模式
- i, I 进入输入模式(Insert mode):
i 为『从光标所在处输入』, I 为『在行首非空格处开始输入』- a, A 进入输入模式(Insert mode):
a 为『从光标所在的下一个字符处开始输入』, A 为『从行尾处开始输入』- o, O 进入输入模式(Insert mode):
o 为『在光标所在的下一行处输入』; O 为在目前光标所在处的上一行输入- r, R 进入取代模式(Replace mode):
r 只会取代光标所在的那一个字符一次;R会一直取代光标所在的文字,直到按下 ESC 为止- [Esc] 退出编辑模式,回到一般模式中
编辑模式在vi的左下角处显示『–INSERT–』或『–REPLACE–』
命令行模式
- :w 存储
- :w! 强制写入
- :w [filename] 写入新的文件
- :q 退出vi
- :q! 强制退出(放弃所有修改)
- :wq 储存后离开,若为 :wq! 则为强制储存后离开
- :set nu 显示行号
- :set nonu 取消行号
注释
- 批量注释:Ctrl + v 进入块选择模式,移动光标选中要注释的行,再按大写的 I 进入行首插入模式输入注释符号如 // 或 #,输入完毕之后,按两下 ESC,Vim 会自动将选中的所有行首都加上注释,保存退出完成注释。
- 取消注释:Ctrl + v 进入块选择模式,选中你要删除的行首的注释符号,注意 // 要选中两个,选好之后按 d 即可删除注释,ESC 保存退出。
- 批量注释:使用下面命令在指定的行首添加注释。使用命令格式: :起始行号,结束行号s/^/注释符/g
- 或s#^#注释符#g (这种主要用于//类注释)
- 取消注释:使用名命令格式: :起始行号,结束行号s/^注释符//g
正则表达式
作用
- 数据验证 测试输入字符串是否合法
- 查找与替换
使用
- VSCode Ctrl+Shift+F(全局搜索)加Alt+R
- vim直接支持正则
语法
| 或 示例:/yes|no
\c忽略大小写 \C大小写敏感 示例:/word\c (vscode不支持此语法,默认忽略,Alt+C区分大小写)
通配符
- .将匹配任意一个字符。
- x+将匹配一个或多个字符,例如hahhhhh,hah+
- x*将匹配零个或多个字符
- x?将匹配零个或一个字符 如colou?r、favou?rite
- x{num1, num2} 将匹配 num1到 num2 个字符,可缺省
- [aiu] 匹配 a或 i或 u(指定字符集匹配) 连字符-表示区间 如 0-5,a-z
- [^12345] 否定字符集, 不匹配字符集内元素
字符集快捷方式
- \w 匹配大小写字母加数字和下划线,等同于[A-Za-z0-9_]
- \W 否定字符集 [^A-Za-z0-9_]
- \d 匹配数字 [0-9]
- \D 否定字符集 匹配非数字
- \s 匹配空格(包括回车等) [\r\t\f\n\v]
- \S 匹配非空格
贪婪匹配(默认)和懒惰匹配(一般只有匹配多个字符才有这个概念)
- 符合正则表达式模式的字符串的最长可能部分,t[a-z]*i应用于"titanic"返回titani
- 最小可能部分 t[a-z]*?i 懒惰匹配 返回 ti(此时?指懒惰匹配)
查找开头和结尾 /^head /tail$
在方括号之外的正则表达式中 ^用于表示字符串的开头。$表示字符串的末尾。
捕获组复用模式capture groups
某些模式在字符串中多次出现, 可复用
用括号(xxx)定义捕获组, \num来匹配前面通过括号定义的第num个捕获组。
示例:字符串中连续出现三次的数字,每个数字由空格分隔,即(\d+)\s\1\s\1。
搜索替换中使用 $访问替换字符串中的捕获组
示例:HTML标题标签中h改为H为 :1,$s/<h(\d)>/<H$1>/g
代码中的软件工程
代码风格
- 基本要求: 简明、易读、无二义性
- 遵守常规语言规范,合理使用空格、空行、缩进、注释等。
- 逻辑清晰。没有代码冗余、重复,清晰的命名规则。选用合适的设计模式、软件架构风格。
- 优雅
注释风格
- 最精简的是无注释,理想的状态是即便没有注释,也能通过函数、变量等的命名直接理解代码。
- 糟糕的状态是代码本身很难理解,而作者又“惜字如金”。
- 还有一种就是将函数功能、各参数的含义和输入/输出用途等一一列举,这往往是模块的对外接口。
具体风格
- 缩进:4个空格
- 行宽:< 100个字符
- 代码行内要适当多留空格
- 在一个函数体内,逻辑上密切相关的语句之间不加空行,逻辑上不相关的代码块之间要适当留有空行以示区隔
- 复杂的表达式中要用括号来清楚的表示逻辑优先级
- 所有 ‘{’ 和 ‘}’ 应独占一行且成对对齐
- 不要把多条语句和多个变量的定义放在同一行
- 变量命名和驼峰式
编写高质量代码的基本方法
通过控制结构简化代码(if else/while/switch)
通过数据结构简化代码
一定要有错误处理 (Debug版本都要验证所有参数;Release版本验证外部参数)
注意性能优先的代价 (软件工程师的人力成本—目前硬件成本越来越低—理解成本,扩展成本)
拒绝修修补补要不断重构代码
模块化
模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独立地进行设计和开发。这个做法背后的基本原理是关注点的分离 (SoC, Separation of Concerns),分解成易解决的小问题,降低思考负担。
每个模块只有一个功能,易于开发,且bug会集中在少数几个模块内,容易定位软件缺陷,更加容易维护。 模块化程度成是软件设计的一个重要指标,使用耦合度 (Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。
耦合度
耦合度是指软件模块之间的依赖程度,一般可以分为紧密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)和无耦合(Uncoupled)。一般在软件设计中追求松散耦合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNclGyrm-1657440974366)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220708163224759.png)]
内聚度(Cohesion)
内聚度是指软件模块内部各种元素之间的依赖程度
理想的内聚是功能内聚,即一个软件模块只做一件事,只完成一个软件特性(Feather)
软件设计中的一些基本方法
KISS(keep it simple&stupid)原则
- 一行代码只做一件事
- 一个块代码只做一件事
- 一个函数只做一件事
- 一个软件模块只做一件事
使用本地化外部接口,本质上是一种设计模式,代理模式,即客户端通过代理间接地访问该对象,能够有效降低模块与外部的耦合度。
先写伪代码的代码结构更好一些(无细节,逻辑清晰)
可重用软件设计
消费者重用和生产者重用
- 消费者重用:重用已有的一些软件模块代码(取决于功能,工作量,文档和测试完善程度)
- 生产者重用:如何生产可重用代码(通用接口,命名清晰,外部交互单独存放易于修改,其它同上)
接口
基本概念
接口就是互相联系的双方共同遵守的一种协议规范,即定 义一组API函数来约定软件模块之间的沟通方式。
- 在面向过程的编程中,接口一般定义了数据结构及操作这些数据结构的函数;
- 在面向对象的编程 中,接口是对象对外开放(public)的一组属性和方法的集合。函数或方法具体包括名称、参数和 返回值等
- 隐藏软件模块内部的实现细节
接口规格的基本要素
- 接口的目的
- 接口的前置条件
- 接口的协议规范(如http协议,png图片格式,json数据格式定义etc…)
- 接口的后置条件 (达成的效果)
- 接口的质量属性(如响应时间)
示例
tLinkTableNode* GetLinkTableHead(tLinkTable *pLinkTable);
//GetLinkTableHead 目的
//pLinkTable != NULL; 前置条件
//tLinkTableNode和tLinkTable数据结构 协议规范
//return tLinkTableNode* 后置条件
微服务
- 由一系列独立的微服务共同组成软件系统的一种架构模式
- 每个微服务单独部署,跑在自己的进程中
- 每个微服务为独立的业务功能开发,一般每个微服务应分解到最小可变产品(MVP),达到功能内聚的理想状态。微服务一般通过RESTful API接口方式进行封装;
- 系统中的各微服务是分布式管理的,强调隔离性,互相之间无耦合或者极为松散的耦合,系统通过前端应用或API网关来聚合各微服务完成整体系统的业务功能。
- 传统单体集中式架构是适应单体服务器环境的软件架构模式;微服务架构则是为了适应大规模集群及基于虚拟化技术和分布式计算的云计算环境的架构模式。
RESTful API
REpresentational State Transfer 即表现层状态转化
- GET用来获取资源;
- POST用来新建资源(也可以用于更新资源);
- PUT用来更新资源;
- DELETE用来删除资源。
使用回调函数提高接口通用性
作为参数的函数就是callback函数,作用:
- 调用函数在处理相似事件的时候可以灵活的使用不同的方法。
- 异步通知
接口划分耦合度
无耦合、数据耦合、标记耦合、控制耦合、公共耦合和内容耦合
公共耦合
软件模块之间共享数据区或变量名(而非调用)
数据耦合
软件模块之间仅通过显式的调用传递基本数据类型
标记耦合
软件模块之间通过显式的调用传递复杂的数据结构(结构化数据)(数据结构成为调用双方软件模块隐含的规格约定)(耦合度要高于数据耦合。低于公共耦合)
通用接口定义的基本方法
- 参数化上下文(使用参数传递信息,不依赖上下文环境,不使用闭包函数)
- 移除前置条件(使用args数组传递参数,不限定参数个数,传入数组长度len)
- 简化后置条件(移除len,动态获取args长度) (len影响结果)
线程安全
指令乱序问题
- 多线程的函数可重入性和线程安全问题
- 编译器编译优化造成的指令乱序
- CPU乱序执行指令
基本概念
- 线程
- 线程堆栈(共享进程资源)
- 可重入函数(多于一个任务并发使用)要么使用局部变量,要么在使用全局变量时加锁。
- 不为连续的调用持有静态数据
- 不返回指向静态数据的指针;
- 所有数据都由函数的调用者提供;
- 使用局部变量,或者通过制作全局数据的局部变量拷贝来保护全局数据;
- 使用静态数据或全局变量时做周密的并行时序分析,通过临界区互斥避免临界区冲突;
- 绝不调用任何不可重入函数。
可重入函数与线程安全
- 可重入的函数不一定是线程安全的,可重入的函数在多个线程中并发使用时是线程安全的,不同的可重入函数(共享全局变量及静态变量)在多个线程中并发使用时会有线程安全问题;
- 不可重入的函数一定不是线程安全的。
cpu乱序执行指令
单核CPU只要将结果顺序地提交到ISA寄存器,就可以保证顺序一致性(Sequential consistency)
多核CPU核和 Cache以及内存之间,存在着Store Buffer为保证多核之间的修改可见性,需要加上内存屏障
ARM64是弱内存序模型,因为精简指令集把访存指令和运算指令分开了,为了性能允许几乎所有的指令乱序,但前提是不影响程序的正确性。因此ARM64架构的指令乱序问题需要引入不同类型的barrier来保证程序的正确性。
barrier()就是常说的编译器屏障(compiler barriers),它的主要用途就是告诉编译器不要优化重排指令顺序。(告诉编译器内存中的值已经改变,之前对内存的缓存(缓存到寄存器)都需要抛弃)
CPU仅能看到机器指令或汇编指令序列中的数据依赖、控制依赖和地址依赖等依赖关系,并不能理解高级语言中定义的逻辑关系,因此CPU指令乱序执行和编译优化指令乱序都可能会破坏高级语言中定义的逻辑关系
编译器指令乱序
编译器进行指令重排优化只能保证是单线程安全。对函数的可重入问题是没有感知的。
makefile
用于自动生成目标文件
.c.o 表示表示所有的 .o文件都是依赖于相应的.c文件的。
Makefile有三个非常有用的变量。分别是$@、$^和 $<, $@ 表示目标文件;$^ 表示所有的依赖文件;$< 表示第一个依赖文件
.c.o和 $<结合,表示要生成一个.o目标文件就自动使用对应的.c文件来编译生成
软件质量的三个角度
- 产品的角度,软件产品本身内在的质量特点;
- 用户的角度,软件产品是不是对用户有帮助,是不是有良好的用户体验;
- 商业的角度,软件产品的商业价值,比如投资回报或开发软件产品的其他驱动因素。
软件设计的方法和原则
- 重构
- 模块化 Modularity
- 接口 Interfaces
- 隐藏实现 Information hiding
- 渐进发展 Incremental development
- 抽象Abstraction
- 通用Generality
从需求分析到软件设计
需求分析就是需求分析师对用户期望的软件行为进行表述,并进一步用对象或实体的状态、属性和行为来定义需求(建模)
绘图工具draw.io(有VSCode插件 文件后缀 .drawio.svg或.drawio或.dio)
需求的类型
- 功能需求:根据所需的活动描述所需的行为
- 质量需求或非功能需求:描述软件必须具备的一些质量特性
- 设计约束: 设计决策,例如选择平台或接口组件
- 过程约束: 对可用于构建系统的技术或资源的限制
需求相关人员
客户:为要开发的软件付费
客户:软件开发后购买
用户:使用系统
领域专家:熟悉软件
市场研究人员:调查以确定未来趋势和潜在客户
律师或审计师:熟悉政府、安全或法律要求
软件工程师或其他技术专家
高质量需求
- 有客观标准,用于判断是否满足需求
- 指定具体数量描述
- 使用实体名称而非代词
- 需求文档中每个名词都有定义
- 解决冲突
- 按需求重要程度分类:必要的,可取的,可选的
- 需求特点
- Correct 正确
- Consistent 一致
- Unambigious无二义
- Complete 完整
- Feasible 可行
- Relevant相关
- Testable 可量化
- Traceable 可追溯
对需求进行分析和建模
需求分析的两类基本方法
- 原型化方法(Prototyping)整理出用户接口方式(UI,User Interface),比如界面布局和交互操作过程。
- 建模的方法(Modeling)给出有关事件发生顺序或活动同步约束的问题,能够在逻辑上形成模型来整顿繁杂的需求细节。
用例
- 用例的四个必要条件
- 是一个业务过程
- 由参与者触发(参与者可以是用户,也可以是一个其它模块)
- 为特定的参与者完成一个特定的业务任务
- 终止于某个特定参与者(得到业务任务完成的结果)
- 抽象用例(Abstract use case)完成一个业务任务如打电话
- 高层用例(High level use case)给用例的范围划定一个边界,始于准备拨号,终于铃声响起
- 扩展用例(Expanded use case)。参与者和软件在用例过程中的全部交互过程。如 tcp三次握手,client和server按何顺序分别做了什么,分两列列出。
- 用例建模的步骤
- 抽象用例
- 高层用例
- 对用例按照子系统或不同的方面进行分类,描述用例与用例、用例与参与者之间的上下文关系,画出用例图
- 扩展用例。
- 用例图
- 关联关系用直线表示 1表示1 *表示多
- 单向关联关系用直线加箭头表示。比如在用例图中参与者和用例就是一种单向关联关系,参与者总是“知道”用例,而用例“不知道”参与者,所以箭头可以从参与者指向用例。
- 包含关系是用带箭头的虚线加<>来表示。
- 扩展关系是用带箭头的虚线加<>来表示的。
面向对象分析涉及的基本概念
对象(Object) 某个类的实例,能独立存在
属性(Attribute)
继承关系(Inheritance Relationship)
IS-A关系,泛化与分化
聚合关系(Aggregation Relationship)
一个对象是另一个对象的一部分。“部分与整体”(part-of)的关系。
关联关系(Association Relationship)
一般关系,比如教师与课程
业务领域建模(Domain Modeling)
- 收集应用业务领域的信息。聚焦在功能需求层面
- 列出重要的应用业务领域概念,给出这些概念的属性,以及这些概念之间的关系
- 给这些应用业务领域概念分类。列出类,属性和值、以及继承关系、聚合关系和关联关系
- 画UML 类图
关系数据模型的MongoDB设计与实现
通用的、基于文档的、分布式的数据库,用类似JSON格式的文档来存储数据。
一对多关系建模的三种基础方案
- One-to-Few
- 内嵌 子数据结构(人的直系亲属)
- One-to-Many
- 子引用 存储对应的 id (数组)(商品的数百个零件)
- One-to-Squillions
- 父引用 存储主机id,主机存储大量数据(日志文件)
- 如果 one 端只有少量信息,可以直接冗余到多端,合并两个对象
双向关联(反范式化)
- 一对多的多也要保存one端的id
- 易于反向统计,但修改时要改多个对象,可能无法保证操作的原子性
- 反范式化在节省读的代价的同时会带来更新的代价(取决于读和更新的频率)
总结
- 优先考虑内嵌
- 数组不应该无限制增长,过大则考虑子引用和父引用
- 不要害怕应用层级别的join:如果索引建的正确并且通过投影条件限制返回的结果,那么应用层级别的join并不会比关系数据库中join开销大多少。
- 反范式设计时请先确认读写比
从需求分析到软件设计
瀑布模型
瀑布模型是最基本的过程模型,它把整个软件过程按顺序划分成了需求、设计、编码、测试和部署五个阶段。瀑布模型的根本特点是按顺序划分阶段。
统一过程(Unified Process)
统一过程(UP,Unified Process)的核心要义是用例驱动(Use case driven)、以架构为中心(Architecture centric)、增量且迭代(Incremental and Iterative)的过程
敏捷统一过程(Agile Unified Process)进一步将软件过程中每一次迭代过程划分为计划阶段和增量阶段。
敏捷统一过程的计划阶段
- 明确业务上的实际需求
- 充分调研获取需求并明确定义需求规格;
- 进行项目的可行性研究;
- 如果项目可行,用例建模并给出用例图;
- 明确需求和用例之间的可跟踪矩阵;
- 项目的概念模型草案;
- 日程安排、需要的资源以及大致的预算范围。
敏捷统一过程的四个关键步骤
- 确定需求;
- 通过用例的方式来满足需求;
- 分配用例到各增量阶段;
- 具体完成各增量阶段所计划的任务。
敏捷统一过程的增量阶段
- 用例建模(Use case modeling)
- 业务领域建模(Domain modeling)
- 对象交互建模(Object Interaction modeling)
- 形成设计类图(design class diagram)
- 软件的编码实现和软件应用部署
对象交互建模(Object Interaction Modeling)
- 找出关键步骤进行剧情描述(scenario)
- 将剧情描述(scenario)转换成剧情描述表(scenario table)
- 将剧情描述表转换成序列图的基本方法
- 从分析序列图到设计序列图
- 一个完整用例的对象交互建模
设计类图
- DCD 可能包含设计类,如控制器、命令、GUI 类。
- 依次填充类,方法,属性,关系
形成软件设计方案的基本方法
- 分析(analysis)和综合(synthesis)。
- 分析是分解大问题变成易于理解的小问题。比如用例建模是将错综复杂的需求分解成一个一个的用例。
- 综合是将一个个小问题的解决方案组合起来构建软件的整体解决方案。
软件科学基础概论
软件是什么
软件的基本构成元素
对象
是类的实例,是属性和方法的集合
函数和变量/常量
常量和函数等放在代码段 初始化的全局变量,静态变量放在数据段 未初始化放在bss段 动态内存分配放在堆 局部变量 常量放在栈 命令行参数和环境变量存储在与栈底紧挨着的位置
指令和操作数
指令码+操作数(立即数或寄存器操作数或内存操作数)
软件的基本结构
顺序结构
分支结构
利用标志寄存器上标志位的指令(判断是否跳转)和跳转指令组合实现
循环结构
函数调用框架 (函数调用时堆栈的处理过程)
继承和对象组合 (继承会破坏封装,使用组合更好)
软件中的一些特殊机制
回调函数
函数(地址)作为参数
多态
允许将不同的子类类型的对象动态赋值给父类类型的变量,通过父类的变量调用方法在执行时实际执行的可能是不同的子类对象方法,因而表现出不同的执行效果。
闭包
将函数作为返回值时,该函数执行所需的上下文环境也作为返回的函数对象的一部分。允许从内部函数访问外部函数作用域。
异步调用
匿名函数 如lamda函数
软件的三种系统类型
S系统:有规范定义,可从规范派生
矩阵操纵矩阵运算
P系统:需求基于问题的近似解,但现实世界保持稳定
象棋程序
E系统:嵌入现实世界并随着世界的变化而变化(大多数软件都属于这个类型)
预测经济运行方式的软件(但经济尚未被完全理解)
软件具有复杂性和易变性,从而难以达成概念的完整性与一致性。(需求的达成永远赶不上需求的变
化)
设计模式
设计模式的本质是面向对象设计原则的实际运用总结出的经验模型。彰显对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解
设计模式优点:
- 提高思维能力
- 使程序设计更加标准化
- 代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
设计模式组成:
- 名称
- 目的,解决什么问题
- 解决方案
- 约束和限制条件。
设计模式分类
- 类模式:用于处理类与子类之间的关系,继承实现,是静态的,编译时确定。如模板方法模式
- 对象模式:用于处理对象之间的关系,组合或聚合实现,运行时确定,耦合度低(应用于多数设计模式)
使用完成的任务类型来划分
- 创建型模式:怎样创建对象,“将对象的创建与使用分离”。如单例模式、原型模式、建造者模式等。
- 结构型模式:如何将类或对象按某种布局组成更大的结构,如代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式。结构型模式分为类结构型模式和对象结构型模式。
- 行为型模式:用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成任务,它涉及算法与对象间职责的分配。如模板方法模式、策略模式、命令模式、职责链模式、观察者模式等。同分为类行为模式和对象行为模式
常用的设计模式
单例(Singleton)模式
//保证一个类仅有一个实例,并提供一个访问它的全局访问点,如数据库实例
//(1)限制调用者直接实例化该对象;(2)为该对象的单例提供一个全局唯一的访问方法。package msgpool...// 消息池type messagePool struct {pool *sync.Pool}// 消息池单例var msgPool = &messagePool{// 如果消息池里没有消息,则新建一个Count值为0的Message实例pool: &sync.Pool{New: func() interface{} { return &Message{Count: 0} }},}// 访问消息池单例的唯一方法func Instance() *messagePool {return msgPool}// 从消息池里获取消息func (m *messagePool) GetMsg() *Message {return m.pool.Get().(*Message)}...
原型(Prototype)模式
//解决对象复制的问题 (若不用原型模式,创建新实例,遍历原始对象成员,依次复制)
//核心就是clone()方法
package prototype...// 原型复制抽象接口type Prototype interface {clone() Prototype}type Message struct {Header *HeaderBody *Body}func (m *Message) clone() Prototype {msg := *mreturn &msg}
建造者(Builder)模式
//将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象
//否则会有多层嵌套,可读性差
package msg...// Message对象的Builder对象type builder struct {once *sync.Oncemsg *Message}// 返回Builder对象func Builder() *builder {return &builder{once: &sync.Once{},msg: &Message{Header: &Header{}, Body: &Body{}},}}// 对Message成员对构建方法func (b *builder) WithSrcAddr(srcAddr string) *builder {b.msg.Header.SrcAddr = srcAddrreturn b}func (b *builder) WithHeaderItem(key, value string) *builder {//保证map只初始化一次b.once.Do(func() {b.msg.Header.Items = make(map[string]string)})b.msg.Header.Items[key] = valuereturn b}func (b *builder) WithBodyItem(record string) *builder {b.msg.Body.Items = append(b.msg.Body.Items, record)return b}// 创建Message对象,在最后一步调用func (b *builder) Build() *Message {return b.msg}
代理(Proxy)模式
//代理以控制对该对象的访问 如外部接口本地化将外部的输入和输出封装成本地接口,有效降低模块与外部的耦合度。
package db
...
// 数据库服务端实现
type Server struct {data map[string]string
}func (s *Server) Save(record Record, reply *bool) error {//存储
}package db
...
// 数据库服务端远程代理
type Client struct {// RPC客户端cli *rpc.Client
}func (c *Client) Save(record Record, reply *bool) error {// 通过RPC调用服务端的接口
}// 工厂方法,返回远程代理实例
func CreateClient() *Client {rpcCli, err := rpc.Dial("tcp", "127.0.0.1:1234")return &Client{cli: rpcCli}
}
适配器(Adapter)模式:
//转换接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。使用而非继承的方式
//新的适配器对象,包含现有的类,在实现新接口时,实际调用现有类的接口(做额外的事情)
package adapter// TypeC接口
type TypeC interface {UseTypeC() string
}// USB接口
type USB interface {UseUSB() string
}// keyboard实现了USB接口,要求适配typec
func (a *keyboard) UseUSB() string {return "I use USB interface"
}//NewAdapter 是适配器的工厂函数
func NewAdapter(keyboard USB) TypeC {return &adapter{USB: keyboard,}
}//Adapter是适配器对象,其中包含一个USB成员
type adapter struct {USB
}//UseTypeC实现了Type-C接口
func (a *adapter) UseTypeC() string {return a.UseUSB() + ", but now I use Type-C interface"
}
装饰(Decorator)模式
//在不改变现有对象,增加其额外的功能。装饰模式实质上是用对象组合的方式扩展功能package network// 关键点1: 定义被装饰的抽象接口type Socket interface {Listen(endpoint Endpoint) error}// 关键点2: 提供一个默认的基础实现type socketImpl struct {listener SocketListener}func DefaultSocket() *socketImpl {return &socketImpl{}}func (s *socketImpl) Listen(endpoint Endpoint) error {return Instance().Listen(endpoint, s)}package sidecar// 关键点3: 定义装饰器,实现被装饰的接口type FlowCtrlSidecar struct {// 关键点4: 装饰器持有被装饰的抽象接口作为成员属性socket network.Socket}// 关键点5: 对于需要扩展功能的方法,新增扩展功能func (f *FlowCtrlSidecar) Receive(packet *network.Packet) {//调用原有方法+扩展}// 关键点6: 不需要扩展功能的方法,直接调用被装饰接口的原生方法即可func (f *FlowCtrlSidecar) Close(endpoint network.Endpoint) {f.socket.Close(endpoint)}// 关键点7: 定义装饰器的工厂方法,入参为被装饰接口func NewFlowCtrlSidecar(socket network.Socket) *FlowCtrlSidecar {return &FlowCtrlSidecar{socket: socket,}}// 关键点8: 使用时,通过装饰器的工厂方法,把所有装饰器和被装饰者串联起来// 简单工厂方法根据可能的输入switch case return一个新实例// 工厂方法给工厂抽象一个接口(单个抽象产品)// 抽象工厂方法多个接口,生产相关联的多个抽象产品func (a AllInOneFactory) Create() network.Socket {return NewAccessLogSidecar(NewFlowCtrlSidecar(network.DefaultSocket()), a.producer)}
外观(Facade)模式
//汽车组件
type CarModel struct {}func NewCarModel() *CarModel {return &CarModel{}
}func (c *CarModel) SetModel() {fmt.Println("CarModel - SetModel")
}
//类似定义CarEngine和CarBody//汽车
type CarFacade struct {model CarModelengine CarEnginebody CarBody
}func NewCarFacade() *CarFacade {return &CarFacade{model: CarModel{},engine: CarEngine{},body: CarBody{},}
}
//简单的对外统一接口,不关心具体的处理
func (c *CarFacade) CreateCompleteCar() {c.model.SetModel()c.body.SetCarBody()c.engine.SetEngine()
}
享元(Flyweight)模式
//大量重复对象固定不变的部分设计成共享对象,固定不变的部分设计成共享对象
//单例模式更关心的是对象在系统中仅仅创建一次,而享元模式更关心的是如何在多个对象中共享相同的状态。
//线程池、固定分配存储空间的消息队列//一个赛季总共会有2460场比赛,对应地,就会有4920个Team实例。但是,NBA的30支球队是固定的,实际上只需30个Team实例就能完整地记录一个赛季的所有比赛信息
package nba
...
//享元工厂
type teamFactory struct {// 球队池,缓存球队实例teams map[TeamId]*Team
}// 根据TeamId获取Team实例,从池中获取,如果池里没有,则创建
func (t *teamFactory) TeamOf(id TeamId) *Team {team, ok := t.teams[id]if !ok {team = createTeam(id)t.teams[id] = team}return team
}// 享元工厂的单例
var factory = &teamFactory{teams: make(map[TeamId]*Team),
}
// 获取工厂的唯一方法
func Factory() *teamFactory {return factory
}// 根据TeamId创建Team实例,只在TeamOf方法中调用,外部不可见
func createTeam(id TeamId) *Team {//具体实现
}
策略(Strategy)模式
//定义一系列算法,将每个算法封装起来,使它们可以相互替换
package strategy// 定义一个策略类
type IStrategy interface {do(int, int) int
}// 策略实现:加
type add struct{}func (*add) do(a, b int) int {return a + b
}// 类似策略实现:减// 具体策略的执行者
type Operator struct {strategy IStrategy
}// 设置策略
func (operator *Operator) setStrategy(strategy IStrategy) {operator.strategy = strategy
}// 调用策略中的方法
func (operator *Operator) calculate(a, b int) int {return operator.strategy.do(a, b)
}
命令(Command)模式
//将一个命令封装为一个对象,使发出请求的责任和执行请求的责任分割开//具体执行命令--接收者
type TV struct{}func (tv TV) Open() {fmt.Println("开机")
}//其它
...//抽象命令类和具体命令
type Command interface {Execute()
}type OpenCommand struct {receiver TV
}func (oc OpenCommand) Execute() {oc.receiver.Open()
}//其它具体命令
...//命令的工厂方法
func NewCommand(t string, tv TV) Command {switch t {case "open":return OpenCommand{receiver: tv,}//其它case}
}//发出命令者--请求该命令
type Invoke struct {Command
}func (i Invoke) ExecuteCommand() {i.Command.Execute()
}
模板方法(TemplateMethod)
//定义抽象算法,部分具体实现放到子类中。模版方法是继承和重载机制的应用,属于类模式。//把大象放入冰箱需要几步?第一步:打开冰箱;第二步:放入大象;第三部:关上冰箱
package mainimport "fmt"//抽象算法
type PutElephantMode interface {OpenFridge()CloseFridge()
}
//模板类
type PutElephant struct {}
//模板类中的具体实现
func (p *PutElephant) put() {fmt.Println("put elephant")
}//模板算法
func (p *PutElephant) PutElephant(putMode PutElephantMode) {putMode.OpenFridge()p.put()putMode.CloseFridge()
}//子类具体实现
type HaierFridge struct{}func (*HaierFridge) OpenFridge() {fmt.Println("open Haier fridge")
}func (*HaierFridge) CloseFridge() {fmt.Println("close Haier fridge")
}
职责链(Chain of Responsibility)
//为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。通过这种方式将多个请求处理者串联为一个链表,去除请求发送者与它们之间的耦合。//gin 中间件//状态 执行失败置1
var status int8 = 0type HandlerFunc func()type HandlersChain []HandlerFunctype RouterGroup struct {Handlers HandlersChainindex int8 //当前执行位置
}//添加中间件,将其组成链式
func (group *RouterGroup) Use(middleware ...HandlerFunc) {group.Handlers = append(group.Handlers, middleware...)
}//链顺序执行
func (group *RouterGroup) Next() {for group.index < int8(len(group.Handlers)) {group.Handlers[group.index]()group.index++}
}func middleware1() {fmt.Println("全局中间件1执行完毕")
}func middleware2() {fmt.Println("全局中间件2执行失败")status = 1
}func main() {r := &RouterGroup{}//添加中间件r.Use(middleware1, middleware2)//运行中间件r.Next()//状态检查if status == 1 {fmt.Println("中间件检查失败,请重试")return}//执行后续流程
}
中介者(Mediator)
//定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度
//在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者package Mediatorimport "fmt"
//抽象同事类
type Colleague interface {Send(msg string)Notify(msg string)SetMediator(mediator Mediator)
}
//具体同事类,只知道自己的行为,不了解其它同事,只认识中介者
type ConcreteColleague1 struct {mediator Mediator
}
//具体类的实现--调用Mediator的实现
...//抽象中介者
type Mediator interface {Send(msg string, colleague Colleague)
}
//具体中介者
type ConcreteMediator struct {c1 Colleaguec2 Colleague
}func (c *ConcreteMediator) Send(msg string, colleague Colleague) {if colleague == c.c1 {c.c2.Notify(msg)} else {c.c1.Notify(msg)}
}
观察者模式observer
//多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,把这种改变通知给其他多个对象
//发布-订阅模式
type Subject struct {observers []Observercontext string
}
//工厂方法
func NewSubject() *Subject {return &Subject{observers: make([]Observer, 0),}
}
//添加观察者--订阅
func (s *Subject) Attach(o Observer) {s.observers = append(s.observers, o)
}
//通知订阅者-仅用于UpdateContext
func (s *Subject) notify() {for _, o := range s.observers {o.Update(s)}
}
//更新后自动通知
func (s *Subject) UpdateContext(context string) {s.context = contexts.notify()
}
//观察者抽象类
type Observer interface {Update(*Subject)
}
//观察者具体类
type Customer struct {name string
}
//工厂方法
func NewCustomer(name string) *Customer {return &Customer{name: name,}
}
//具体实现
func (r *Customer) Update(s *Subject) {fmt.Printf("%s received %s\n", r.name, s.context)
}
设计模式背后的设计原则
开闭原则(Open Closed Principle,OCP)
软件应当对扩展开放,对修改关闭Liskov替换原则(Liskov Substitution Principle,LSP)
子类可以扩展父类的功能,但最好不要改变父类原有的功能(重载)。依赖倒置原则(Dependence Inversion Principle,DIP)
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。即要面向接口(抽象)编程,不要面向实现(细节)编程。(降低耦合,细节变化,抽象不变则不必修改)单一职责原则(Single Responsibility Principle,SRP)
字面意思,可以提高类的内聚度,符合模块化设计的高内聚低耦合的设计原则。迪米特法则(Law of Demeter,LoD)
只与直接朋友交谈,如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
合成复用原则(Composite Reuse Principle,CRP)
又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。在软件复用时,要尽量先使用组合或者聚合关系来实现(维持封装,耦合度低,灵活性高),其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循Liskov替换原则。
常见的软件架构举例
三层架构
层次化架构是利用面向接口编程的原则将层次化的结构型设计模式作为软件的主体,三层架构是层次化架构中比较典型的代表,界面层—>业务逻辑层—>数据访问层
MVC架构
MVC即为Model-View-Controller(模型-视图-控制器)
Model(模型)存取数据的对象及其数据模型。
View(视图)可视化的界面接口。
Controller(控制器)控制数据流向模型对象,并在数据变化时更新视图
与三层架构对比:
- 设计模式—软件架构
- Model — 业务逻辑+数据访问
优点
多个视图共享一个模型,大大提高代码的可重用性
控制器可以用来连接不同的模型和视图去完成用户的需求
三个模块相互独立,改变其中一个不会影响其它两个
MVVM
Model-View-ViewModel
- MVC中,用户对于M的操作是通过C传递的,然后C将改变传给V,并且M将在发生变化时 通知V,然后V通过C获取在MVVM中,用户直接与V交互,通过VM将变化传递给M,然后M改变之后 通过VM将数据传递给V,从而实现解耦。 (最大区别:实现了View和Model的自动同步)
- 当M的数据需要进行解析后V才能使用时,C若承担解析的任务,就会变得很臃肿;在 MVVM中,VM层承担了数据解析的工作
软件架构风格与描述方法
软件架构复用
- 克隆,完整地借鉴相似项目的设计方案,修改适配。
- 重构,参考已有的软件架构模型的基础上逐步形成系统软件架构
软件架构风格与策略
管道-过滤器
面向数据流的软件体系结构,最典型的应用是编译系统。一个普通的编译系统包括词法分析器、语法分析器、语义分析与中间代码生成器、目标代码生成器等一系列对源代码进行处理的过程。对源代码处理的过滤器通过管道连接起来。
客户-服务
Client/Server(C/S)和Browser/Server(B/S)
通过请求和应答的方式访问或者调用服务代码。包括函数调用和返回,TCP 的send和recv,HTTP的GET请求和响应。P2P
P2P(peer-to-peer)架构是客户-服务模式的一种特殊情形,P2P架构中每一个构件既是客户端又是服务端。
发布-订阅
见观察者模式
CRUD
中心化管理系统关键数据的软件架构风格,四种持久化操作。
层次化
每一层为它的上一层提供服务,同时又作为下一层的客户,如osi
软件架构的描述方法
- 分解视图 Decomposition View 分成哪些部分
- 依赖视图 Dependencies View 软件模块之间的依赖关系
- 泛化视图 Generalization View 软件模块之间的一般化或具体化的关系(组合继承)
- 执行视图 Execution View 系统运行时的时序结构特点,比如流程图、时序图等
- 实现视图 Implementation View 软件架构与源文件之间的映射关系(给源文件合理命名)
- 部署视图 Deployment View 执行实体和计算机资源建立映射关系
- 工作任务分配视图 Work-assignment View 系统分解成可独立完成的工作任务,以便分配给各项目团队和成员
软件质量标准
符合标准规范,满足用户期望
- 易于修改维护(Modifiability) 高内聚低耦合
- 良好的性能表现(Performance) 系统的速度和容量
- 安全性(Security)防御攻击和从攻击恢复的能力
- 可靠性(Reliability)正确执行功能
- 健壮性(Robustness)基于可靠性,能适应异常环境
- 易用性(Usability)
- 商业目标(Business goals)
软件危机和软件过程
没有银弹
“在10年内无法找到解决软件危机的杀手锏(银弹)。 软件中的根本困难,即软件概念结构(conceptual structure)的复杂性,无法达成软件概念的完整性 和一致性,自然无法从根本上解决软件危机带来的困境。
基于组件的软件工程方法,将软件组件内的复杂结构包装起来(模块化设计),使得软件组件简单易用,组合成大型软件。产生基于组件的软件供应链。
软件的生命周期
- 分析阶段 需求分析和定义,形成业务概念原型
- 设计阶段 软件架构设计和软件详细设计,又称“分析与设计”和“设计与实现”。
- 实现阶段 编码和测试,测试又涉及到单元测试、集成测试、系统测试等。
- 交付阶段 部署、交付测试和用户培训等。
- 维护阶段 软件生命周期中最长的阶段
软件过程
描述性的过程试图客观陈述在软件开发过程中实际发生什么。
说明性的过程试图主观陈述在软件开发过程中应该会发生什么。
常见过程模型
瀑布模型
瀑布模型是一个过程活动的顺序结构,没有任何迭代(假定需求不会发生任何变化)
需求分析—>概要设计—>详细设计—>编码实现—>单元测试+集成测试—>系统测试—>验收测试—>维护
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D2F3hW4r-1657440974367)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220710150501397.png)]
原型化的瀑布模型
原型就是根据需要完成的软件的一部分如用户接口原型和软件架构原型(将风险前移,判断是否符合需求)
V模型
V模型也是在瀑布模型基础上发展出来的,单元测试、集成测试和系统测试是为了在不同层面验证设 计,而交付测试则是确认需求是否得到满足。 通过将瀑布模型前后两端的过程活动结合起来,提高过程活动的内聚度,改善软件开发效率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tewBLBBA-1657440974367)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220710150844593.png)]
生死相依原则: 特定过程活动和评估该特定过程的过程活动成对出现
分阶段的增量和迭代开发,每次交付系统的一小部分,从而缩短开发迭代的周期。
- 增量开发就是从一个功能子系统开始交付,每次交付会增加一些功能,这样逐步扩展功能最终完成整个系统功能的开发。
- 迭代开发是首先完成一个完整的系统或者完整系统的框架,然后每次交付会升级其中的某个功能子系统,这样反复迭代逐步细化最终完成系统开发。
螺旋模型
计划–>确定目标,替代方案,约束–>开发测试–>评估替代方案和风险
个体软件过程PSP
应用增量式开发方法在如下过程,以开发大型系统
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wlwu02QJ-1657440974367)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220710152034779.png)]
团队软件过程TSP
团队的基本要素
- 团队规模
- 团队的凝聚力
- 团队协作的基本条件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ai5mYVX-1657440974376)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220710152346087.png)]
团队项目的基本策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4IkShlnF-1657440974377)(C:\Users\lan\AppData\Roaming\Typora\typora-user-images\image-20220710152423655.png)]
评价团队的方法
CMM/CMMI用于评价软件生产能力并帮助其改善软件质量,侧重于软件开发过程的管理及工程能力的提高与评估。
- 一级,初始级,目标清晰,可实现。无法保证同类项目成功率。
- 二级,管理级,能遵守既定的计划与流程,有资源准备,权责到人,对整个流程进行监测与控制,并联合上级单位进行审查。保证同类项目的成功率。
- 三级,已定义级,将上述标准流程予以制度化。保证各类项目成功率。
- 四级,量化管理级,项目管理实现数字化,降低质量波动。
- 五级,持续优化级,充分利用信息资料,对可能出现的问题予以预防。主动改善流程。
CMM/CMMI主要应用在两大方面:
- 能力评估 如软件过程评估和软件能力评价
- 过程改进 上述级别反映了过程改进活动的轻重缓急和先后顺序
敏捷方法的敏捷宣言
- 个体和互动 高于 流程和工具
- 工作的软件 高于 详尽的文档
- 客户合作 高于 合同谈判
- 响应变化 高于 遵循计划
- 尽管右项有其价值,我们更重视左项的价值。
Scrum敏捷开发方法
- 项目经理(Scrum Master),负责项目的开发过程。
- 产品经理(Product Owner),比如定义产品功能和特性。
- 团队(Team)
在Scrum中每一轮迭代称为一个冲刺(Sprint)得到软件的一个增量版本,每个冲刺包括如下活动形式
- 冲刺规划会议(Sprint Plan Meeting)决定当前的冲刺(Sprint)需要解决的事情
- 每日站立会议(Scrum Daily Meeting)日报
- 冲刺评审会议(Sprint Review Meeting)跨团队演示,注重功能而非细节
- 冲刺回顾会议(Sprint Retrospective Meeting)改进
DevOps
开发(软件工程)、技术运营和质量保障(QA)三者的交集
可看成是敏捷方法从技术开发领域扩展到业务运维领域实现业务上全周期的敏捷性
最小可行产品(MVP,Minimum Viable Product),又称为最小功能集(Minimal Feature Set),把产品最核心的功能用最小的成本实现出来(或者描绘出来),然后快速征求用户意见获得反馈进行改进优化。
重构作为编程的一种基本方法得到业界的普遍认同和采纳;微服务结构则有利于在更高的设计抽象层级上对软件进行重构;敏捷方法则进一步有利于在软件开发过程层面进行迭代和重构;DevOps则终极性地在业务、运维和效益层面进行快速迭代重构。
高级软件工程学习笔记相关推荐
- 软件工程学习笔记《四》需求分析
文章目录 软件工程学习笔记<目录> 需求工程师 当代的需求工程师需要具备的能力 当代的需求工程师需要努力的方向 当代的需求工程师需要注意的错误 需求的定义 需求目标 需求分析的实质 需求分 ...
- 软件工程学习笔记《目录》
软件工程学习笔记<目录> 软件工程学习笔记<一>什么是软件工程 软件工程学习笔记<二>代码规范 软件工程学习笔记<三>代码优化和性能测试 软件工程学习笔 ...
- 软件工程学习笔记《三》代码优化和性能测试
文章目录 软件工程学习笔记目录 如何在开源社区提问? 代码审查 代码优化 运行结果 参数解释 代码优化原则 对常见的数据结构排序算法进行测试 关于冒泡排序优化的探讨 结果 软件工程学习笔记目录 [ht ...
- 软件工程学习笔记《二》代码规范
文章目录 软件工程学习笔记目录 google代码规范 节选python来自google翻译 错误注释的示例 命名规范 import语句的规范 import this 源码 软件工程学习笔记目录 [ht ...
- 软件工程学习笔记《一》什么是软件工程
文章目录 软件工程学习笔记目录 软件工程过程 软件工程方法 软件质量 软件质量如何评价 软件的质量模型 ISO9126模型 易用性: 效率 可维护性 可移植性 为什么内存缓冲区是2048或4096 软 ...
- JavaScript高级程序设计学习笔记(三)
分享一下第五章(引用类型)的笔记,内容比较多,我拆成了两部分,今天这部分是关于Object.Array.Date和RegExp类型的. 以下的笔记是书上一些我以前学习的时候,没有太重视的js基础知识, ...
- 【C#8.0 and .NET Core 3.0 高级编程学习笔记】
@C#8.0 and .NET Core 3.0 高级编程学习笔记 前言 为了能精细地完成对C#语言的学习,我决定选择一本书,精读它,理解它,记录它.我想选择什么书并不是最重要的,最重要的是持之以恒的 ...
- 中科大高级软件工程学习心得体会
中科大高级软件工程学习心得体会 在本门课程中,首先我对一些基本开发工具,如VSCode.Git.Vim.正则表达式等,重新审视了自己的认知,顺便学了一手shell编程,并在课程的基础上又自定义了很多配 ...
- 高级软件工程-读书笔记之思想篇
虽说本科的时候学过软件工程这门课程,却因为缺少项目编程实践,所以对其理解得不是很透彻.但是在研一的继续学习却有了更为深刻的理解和体会,究其原因除了一而再的受教,更多的是理论与实践的结合.那么,高级软件 ...
最新文章
- P - The Shortest Path in Nya Graph HDU - 4725
- 学习攻略|清华大学对外免费开放2000门课程
- MySql 基于C_API的数据库封装
- 自定义控件 - 流式布局:TagFlowLayout
- HDU - 4289 Control(最小割-最大流)
- flash和linux文件系统,面向大容量Flash的高效Linux文件系统改进和实现
- c++thread里暂停线程_多线程技术
- 2021亳州高考成绩查询,2021年亳州高考状元名单公布,亳州文理科状元是谁多少分...
- 新建cordova应用,插件开发教程系列(总目录)
- OpenSuSE 网络配置
- 网络是怎样连接的pdf
- 华为交换机端口基本配置指南
- 【IP路由基础(直连路由、静态路由的三种配置)】--20211203、20211206
- forward与sendRedirect区别
- 六级未过,排名10%开外,如何保研浙大计算机?
- 微型计算机键盘连接在,树莓派推出Pi 400型微型计算机 主机藏在键盘里连接显示器即可使用...
- C语言实现密码登录界面,你可能已被盯上!
- intel第6代服务器芯片,Intel第六代处理器 Skylake CPU、GPU、主板完全解析
- 【英语竞赛】专项练习之翻译
- cocos2d-x碰撞检测学习笔记