高级软件工程学习笔记

一、编程神器 Visual Studio Code

常用快捷键:

打开文件夹(Ctrl/⌘ +O)和关闭文件夹工作区(Ctrl/⌘ +K F) 
新建文件(Ctrl/⌘ +N)、关闭文件(Ctrl/⌘ +W)、编辑文件和保存文件(Ctrl/⌘ +S) 
文件内搜索(Ctrl/⌘ +F) 
关闭所有文件(Ctrl/⌘ +K W) 
关闭已保存的文件(Ctrl/⌘ +K U) 
Ctrl+/用于单行代码注释和取消注释,Ctrl+Shift+A用于代码块注释和取消注释
Ctrl/⌘+Shift+E 文件资源管理器 
Ctrl+Shift+G 源代码管理 
Ctrl/⌘+Shift+F 跨文件搜索 
Ctrl/⌘+Shift+D 启动和调试 
Ctrl/⌘+Shift+P查找并运行所有命令 
Ctrl/⌘+Shift+M查看错误和警告 
Ctrl/⌘+Shift+X 管理扩展插件 
Ctrl+`切换集成终端 
Ctrl+1、2、3... 选中某个文件编辑器

VS Code的优点:

  • 简洁而聚焦的产品定位,贯穿始终
  • 进程隔离的插件模型:把插件们放到单独的进程里, 插件进程怎么折腾也无法干扰主进程代码的执行,主程序的稳定性得到了保障
  • UI渲染与业务逻辑隔离,一致的用户体验:根本不给插件开发者“发明”新界面的机会
  • 代码理解和调试——LSP和DAP两大协议:Language Server Protocol(LSP)有节制的设计、合理的抽象、周全的细节,LSP的大部分请求都是在表达“在指定位置执行规定动作”,是基于文本、JSON、JSONRPC的协议
  • 集大成的Remote Development:可以在远程环境(比如虚机、容器)里开一个 VS Code 工作区,然后用本地的 VS Code 连上去工作,实现了响应迅速、沿用本地设置、数据传输开销小、第三方插件可用、远程文件系统可用

二、五大场景玩转 Git

Git的版本控制使用独立文件和补丁方式,拥有中心版本控制系统和分布式版本控制系统

Git的基本操作逻辑
Git的基本操作逻辑

对于本地Repo,可能有多个branch,至少有一个叫master

本地Repo中的branch与一个或多个远程Repo中的branch存在跟踪关系

场景一:本地版本库

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的头几个字符 # 回退
git reflog # 可以查看当前HEAD之后的提交记录,便于回到未来
git reset -hard commit- id/commit-id的头几个字符 # 回退

场景一主要是在本地对源代码进行基本的版本控制,主要通过git add和git commit -m提交版本,有了提交记录之后可以灵活地将当前工作区里的源代码回退到过去的某个版本

过去和未来之间的分界点就是HEAD,即当前工作区所依赖的版本 

场景二:远程版本库

git clone # 克隆一个存储库到一 个新的目录下
git fetch # 下载一个 远程存储库数据对象等信息到本地存储库 
git push # 将本地存储 库的相关数据对象更新到远程存储库
git merge # 合并两个 或多个开发历史记录
git pull # 从其他存储库或分支抓取并合并到当前存储库的当前分支

首先我们通过git pull拉取远程仓库里的提交项到本地仓库并合并到当前分支,就是将origin/master中的提交项fetch到本地仓库并merge到本地master分支,再进行远程同步

合并(merge)的概念

因为每一个版本都是上一个版本的增量补丁,只要将要合并的分支里的B、D、F几个增量补丁,合并到当前工作区G版本里,解决冲突后提交为H版本

场景三:分叉合并

默认的合并方式为“快进式合并”(fast- farward merge),会将分支里commit合并到主分支里,合并成一条时间线, 与我们期望的呈现为一段独立的分支线段不符,因此合并时需要使用--no-ff参数关闭“快进式合并”(fast-farward merge)

git checkout ¬b mybranch 创建新的分支
git branch 切换分支

推荐流程:

1.克隆或同步最新的代码到本地存储库

2.为自己的工作创建一个分支,该分支应该只负责单一功能模块或代码模块的版本控制

3.在该分支上完成某单一功能模块或代码模块的开发工作

4.最后,先切换回master分支,将远程origin/master同步最新到本地存储库,再合并mybranch到master分支,推送到远程origin/master

场景四:Git Rebase

在mybranch分支上完成自己的工作之后,为了让log记录将来更容易回顾参考,用git rebase重新整理一下提交记录。注意不要通过rebase对任何已经提交到远程仓库中的commit进行修改

git rebase -i [startpoint] [endpoint] 重新整理一下提交记录
一般只指定[startpoint] ,即指定从某一个commit节点开始,可以使用HEAD^^、HEAD~100、commit ID或者commit ID的头几个字符来指定一个commit节点,比如下面的代码指定重新整理HEAD之前的三个commit节点。
git rebase -i HEAD^^^
git rebase —abort
git rebase --continue

场景五:Fork + Pull request

当你想更正别人仓库里的Bug或者向别人仓库里贡献代码时,要走Fork + Pull request的协作开发工作流程:

1.先 fork(分叉) 别人的仓库,相当于拷贝一份

2.做一些 bug fix或其他的代码贡献

3.发起 Pull request 给原仓库

4.原仓库的所有者 review Pull request,如果没有问题的话,就会 merge  Pull request 到原仓库中

Vi/vim

vi/vim的三种模式
h 或 向左箭头键(←)	光标向左移动一个字符
j 或 向下箭头键(↓)	光标向下移动一个字符
k 或 向上箭头键(↑)	光标向上移动一个字符
l 或 向右箭头键(→)	光标向右移动一个字符
n<space> 那个 n 表示『数字』,例如 20 。按下数字后再按空格键,光标会向右移动这一行的 n 个字符。例如 20<space> 则光标会向后面移动 20 个字符距离。
0 或功能键[Home]	这是数字『 0 』:移动到这一行的最前面字符处 (常用)
$ 或功能键[End]	移动到这一行的最后面字符处(常用)
H 光标移动到这个屏幕的最上方那一行的第一个字符
M 光标移动到这个屏幕的中央那一行的第一个字符
L 光标移动到这个屏幕的最下方那一行的第一个字符
G 移动到这个档案的最后一行(常用)
nG n为数字。移动到这个档案的第 n 行。例如 20G 则会移动到这个档案的第 20 行
gg 移动到这个档案的第一行,相当于 1G 啊! (常用)
n<Enter> n 为数字。光标向下移动 n 行(常用)

三、正则表达式

/ 向下查找
?向上查找
n按顺序下一个
N逆序下一个
“.”表示任意一个字符;
“?”表示前一个字符是否存在,也就是存在 0 次或 1 次;
“+”表示前一个字符出现一次或多次;
“*”表示前一个字符出现 0 次、1 次或多次
| 或
可以使用 quantity specifiers 数量说明符指定模式的下限和上限数。数量说明符使用大括号{and}
b[aiu]g 可以匹配“bug、bag、big”
[0-5]匹配 0 和 5 之间的所有数字
[^aeiou]排除元音的所有字符 在括号外表示开头 $表示在结尾

快捷方式\w 匹配字母数字[A-Za-z0-9_]
快捷方式\W 与[^A-Za-z0-9_]相同
快捷方式\d 搜索数字字符集[0-9]
快捷方式\D 查找非数字字符,等于字符集[^0-9]
快捷方式\c 表示大小写不敏感,\C 表示大小写敏感
快捷方式\s 搜索空格,还包括回车、制表符、换页和新行字符
快捷方式\S 搜索非空格

“\”可以将后面出现的字符标记为特殊字符

用括号(and)可以定义capture groups捕获组,用于查找重复的子串
会重复的模式的正则表达式放在括号内
使用捕获组来匹配字符串中连续出现三次的数字,每个数字由空格分隔,如(\d+)\s\1\s\1

在 100 到 200 行之间搜寻 regex 并取代为 RegEx 则为:100,200s/regex/RegEx/g
/g表示全局替换 /c表示操作需要确认 /i表示不区分大小写

<h1>、<h2>、<h3>、<h4>等都改成大写 1,$s/<h(\d)>/<H$1>/g

正则表达式是默认的是 greedy 贪婪匹配,greedy贪婪匹配找到符合正则表达式模式的字符串的最长可能部分,并将其作为匹配返回;相反还有lazy懒惰匹配,是找到符合正则表达式模式的字符串的最小可能部分

可以使用?字符将其更改为lazy懒惰匹配。“titanic”匹配调整后的t[a-z]*?i正则表达式会返回["ti"]

四、简约而不简单——代码规范和代码风格

写代码要小步快跑不断迭代,罗马不是一天建成的,不要期望一撮而就;做实际项目并不鼓励一开始就从头开始写代码,而是找已有的类似项目做对比分析,对开源代码做逆向工程和再工程,对项目有深刻理解的基础上,再考虑是从头构建还是维护一个已有的项目来达成目标

我们把代码的风格分成三重境界:规范整洁;逻辑清晰;优雅。基本要求是简明、易读、无二义性

编写高质量代码的基本方法:通过控制结构简化代码(if else/while/switch);通过数据结构简化代码;一定要有错误处理;注意性能优先的代价 :工作效率、质量保证、代码的可读性、可扩展性;拒绝修修补补要不断重构代码

参数处理的基本原则:

Debug版本中所有的参数都要验证是否正确;Release版本中从外部 (用户或别的模块)传递进来的参数要验证正确性。肯定如何时用断言;可能发生时用错误处理

极限编程:客户和程序员

结对编程:程序员和领航员

五、模块化软件设计

基本原理

模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独 立地进行设计和开发。这个做法背后的基本原理是关注点的分离 (SoC, Separation of Concerns) 分解成易解决的小问题,降低思考负担。每个模块只有一个功能,易于开发,并且bug会集中在少数几个模块内,容易定位软件缺陷,也更加容易维护。

耦合度是指软件模块之间的依赖程度,一般可以分为紧密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)和无耦合(Uncoupled)。一般在软件设计中我们追求松散耦合

内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度,理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或一个软件特性

软件设计中的一些基本方法

KISS(keep it simple&stupid)原则 :一行代码只做一件事;一个块代码只做一件事;一个函数只做一件事;一个软件模块只做一件事

使用本地化外部接口,本质上是一种设计模式,代理模式,即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。能够有效降低模块与外部的耦合度

先写伪代码的代码结构更好一些

六、可重用软件设计

代码重用的四个关键因素:

1.该软件模块是否能满足项目所要求的功能

2.采用该软件模块代码是否比从头构建一个需要更少的工作量,包括构建软件模块和集成软件模块等相关的工作

3.该软件模块是否有完善的文档说明

4.该软件模块是否有完整的测试及修订记录

如上四个关键因素需要按照顺序依次评估

接口就是互相联系的双方共同遵守的一种协议规范

接口

接口的基本要素:

1.接口的目的

2.接口的前置条件

3.接口的协议规范(如http协议,png图片格式,json数据格式定义etc..)

4.接口的后置条件

5.接口的质量属性(如响应时间)

软件模块接口一般用Call-in方式的函数接口和Callback方式的函数接口

微服务接口一般使用RESTful API来定义接口

传统单体集中式(Monolithic)架构与微服务(Microservice)架构

RESTful API:

GET用来获取资源
POST用来新建资源(也可以用于更新资源)
PUT用来更新资源
DELETE用来删除资源

接口间耦合方式

耦合度依次递增可以分为无耦合、 数据耦合、标记耦合、控制耦合、公共耦合和内容耦合。这些耦合度 划分的依据就是接口的定义方式

1. 公共耦合

当软件模块之间 共享数据区 或 变量名 的软件模块之间即是公共耦合,显然两个软件模块之间 的接口定义不是通过显式的调用方式,而是 隐式 的共享了共享了数据区或变量名。

2. 数据耦合

在软件模块之间仅通过显式的调用传递 基本数据类型 即为数据耦合。

3. 标记耦合

在软件模块之间仅通过显式的调用传递复杂的数据结构(结构化数据)即为标记耦合,这时数据的结构成为调用双方软件模块隐含的规格约定,因此耦合度要比数据耦合高。但相比公共耦合 没有经过显式的调用传递数据的方式耦合度要低

通用接口定义的基本方法

1. 参数化上下文(使用参数传递信息,不依赖上下文环境,即不使用闭包函数)

2. 移除前置条件(sum函数中使用数组传递参数,不再限定参数个数)

3. 简化后置条件(移除参数之间的关系,使sum返回的是数组全部元素的和)

七、可重入函数与线程安全

可重入函数的基本要求

1.不为连续的调用持有静态数据

2.不返回指向静态数据的指针

3.所有数据都由函数的调用者提供

4.使用局部变量,或者通过制作全局数据的局部变量拷贝来保护全局数据

5.使用静态数据或全局变量时做周密的并行时序分析,通过临界区互斥避免临界区冲突

6.绝不调用任何不可重入函数

每次运行结果和单线程运行的结果是一样的,就是线程安全的;可重入的函数不一定是线程安全的,不可重入的函数一定不是线程安全的。

可重入的函数在多个线程中并发使用时是线程安全的,但不同的可重入函数(共享全局变量及静态变量)在多个线程中并发使用时会有线程安全问题

参考:软件工程: 《代码中的软件工程》一书的配套ppt和源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值