本学期上了孟宁老师的《高级软件工程》,收获颇丰,不仅学到了Git,VsCode,Vim等一些软件工具的使用,更重要的是学到了很多软件工程方法,了解到了规范化的软件开发流程以及相应的文档撰写方法,在代码层面也学到了诸如设计模式、软件开发框架的知识,现将课程内容做个总结
一.工具
1.Git
git本地版本库的基本用法
-
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远程版本库的基本用法
-
git clone命令官方的解释是“Clone a repository into a new directory”,即克隆一个存储库到一个新的目录下。
-
git fetch命令官方的解释是“Download objects and refs from another repository”,即下载一个远程存储库数据对象等信息到本地存储库。
-
git push命令官方的解释是“Update remote refs along with associated objects”,即将本地存储库的相关数据对象更新到远程存储库。
-
git merge命令官方的解释是“Join two or more development histories together”,即合并两个或多个开发历史记录。
-
git pull命令官方的解释是“Fetch from and integrate with another repository or a local branch”,即从其他存储库或分支抓取并合并到当前存储库的当前分支
2.Vim
安装Vim
It is included as "vi" with most UNIX systems and with Apple OS X. 使用VSCode则可以在Linux、Windows和OS X上都能使用Vim 在VSCode中Ctrl/⌘+Shift+X管理扩展插件中搜索vim即可安装使用
-
命令模式(Command mode),用户刚刚启动vi/vim,便进入了命令模式。此状态下敲击键盘动作会被vim识别为命令,而非输入字符。比如我们此时按下i,并不会输入一个字符,i被当作了一个命令。命令模式只有一些最基本的命令,因此仍要依靠底线命令模式输入更多命令
-
输入模式(Insert mode),在命令模式下按下i就进入了输入模式,按ESC退出输入模式,切换到命令模式。
-
底线命令模式(Last line mode),在命令模式下按下:(英文冒号)就进入了底线命令模式。底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。基本的命令有q(退出程序)、w(保存文件)等。按ESC键可随时退出底线命令模式。
移动光标的基本方法
-
h 或 向左箭头键(←) 光标向左移动一个字符
-
j 或 向下箭头键(↓) 光标向下移动一个字符
-
k 或 向上箭头键(↑) 光标向上移动一个字符
-
l 或 向右箭头键(→) 光标向右移动一个字符
-
如果你将右手放在键盘上的话,你会发现 hjkl 是排列在一起的,因此可以使用这四个按钮来移动光标。 如果想要进行多次移动的话,例如向下移动 30 行,可以使用 "30j" 或 "30↓" 的组合按键, 亦即加上想要进行的次数(数字)后,按下动作即可!
移动光标的更多方法
-
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 行(常用)
删除
-
x, X 在一行字当中,x 为向后删除一个字符 (相当于 [del] 按键), X 为向前删除一个字符(相当于 [backspace] 亦即是退格键) (常用)
-
nx n 为数字,连续向后删除 n 个字符。举例来说,我要连续删除 10 个字符, 『10x』。
-
dd 删除游标所在的那一整行(常用)
-
ndd n 为数字。删除光标所在的向下 n 行,例如 20dd 则是删除 20 行 (常用)
-
d1G 删除光标所在到第一行的所有数据
-
dG 删除光标所在到最后一行的所有数据
-
d$ 删除游标所在处,到该行的最后一个字符
-
d0 那个是数字的0 ,删除游标所在处,到该行的最前面一个字符
复制与粘贴
-
yy 复制游标所在的那一行(常用)
-
p, P p为将已复制的数据在光标下一行贴上,P则为贴在游标上一行! 举例来说,我目前光标在第 20 行,且已经复制了 10 行数据。则按下 p 后, 那 10 行数据会贴在原本的 20 行之后,亦即由 21 行开始贴。但如果是按下 P 呢? 那么原本的第 20 行会被推到变成 30 行。 (常用)
-
nyy n 为数字。复制光标所在的向下 n 行,例如 20yy 则是复制 20 行(常用)
-
y1G 复制游标所在行到第一行的所有数据
-
yG 复制游标所在行到最后一行的所有数据
-
y0 复制光标所在的那个字符到该行行首的所有数据
-
y$ 复制光标所在的那个字符到该行行尾的所有数据
复原和重做
-
u 复原前一个动作。(常用)
-
[Ctrl]+r 重做上一个动作。(常用) 这个 u 与 [Ctrl]+r 是很常用的指令!一个是复原,另一个则是重做一次
二.概念总结
—编写高质量代码的基本方法
1. 通过控制结构简化代码(if else/while/switch)
2. 通过数据结构简化代码
3. 一定要有错误处理
4. 注意性能优先的代价
5. 拒绝修修补补要不断重构代码
—模块化基本原理
模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独
立地进行设计和开发。这个做法背后的基本原理是关注点的分离 (SoC, Separation of Concerns)
分解成易解决的小问题,降低思考负担。
每个模块只有一个功能,易于开发,并且bug会集中在少数几个模块内,容易定位软件缺陷,也更
加容易维护。
软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度
(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。
一般追求松散耦合。
内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度。
理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特
性(Feather)。
—接口
基本概念
接口就是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定
义一组API函数来约定软件模块之间的沟通方式。
在面向过程的编程中,接口一般定义了数据结构及操作这些数据结构的函数;而在面向对象的编程
中,接口是对象对外开放(public)的一组属性和方法的集合。函数或方法具体包括名称、参数和
返回值等。
基本要素
1. 接口的目的
2. 接口的前置条件
3. 接口的协议规范(如http协议,png图片格式,json数据格式定义etc..)
4. 接口的后置条件
5. 接口的质量属性(如响应时间)
RESTful API
GET用来获取资源;
POST用来新建资源(也可以用于更新资源);
PUT用来更新资源;
DELETE用来删除资源。
耦合方式
1. 公共耦合
当软件模块之间 共享数据区 或 变量名 的软件模块之间即是公共耦合,显然两个软件模块之间
的接口定义不是通过显式的调用方式,而是 隐式 的共享了共享了数据区或变量名。
2. 数据耦合
在软件模块之间仅通过显式的调用传递 基本数据类型 即为数据耦合。
3. 标记耦合
在软件模块之间仅通过显式的调用传递复杂的数据结构(结构化数据)即为标记耦合,这时数据
的结构成为调用双方软件模块隐含的规格约定,因此耦合度要比数据耦合高。但相比公共耦合
没有经过显式的调用传递数据的方式耦合度要低。
通用接口定义的基本方法
1. 参数化上下文(使用参数传递信息,不依赖上下文环境,即不使用闭包函数)
2. 移除前置条件(sum函数中使用数组传递参数,不再限定参数个数)
3. 简化后置条件(移除参数之间的关系,使sum返回的是数组全部元素的和)
软件质量的三个角度
1. 产品的角度,也就是软件产品本身内在的质量特点;
2. 用户的角度,也就是软件产品从外部来看是不是对用户有帮助,是不是有良好的用户体验;
3. 商业的角度,也就是商业环境下软件产品的商业价值,比如投资回报或开发软件产品的其他驱
动因素。
—需求
需求的类型
1. 功能需求:根据所需的活动描述所需的行为
2. 质量需求或非功能需求:描述软件必须具备的一些质量特性
3. 设计约束(设计约束): 设计决策,例如选择平台或接口组件
4. 过程约束(过程约束): 对可用于构建系统的技术或资源的限制
需求分析的两种方法
1. 原型化方法
原型化方法可以很好地整理出用户接口方式(UI,User Interface),比如界面布局和交互操作
过程。
2. 建模的方法
建模的方法可以快速给出有关事件发生顺序或活动同步约束的问题,能够在逻辑上形成模型来
整顿繁杂的需求细节。
—用例
基本概念
用例(Use Case)的核心概念中首先它是一个业务过程(business process),经过逻辑整理抽象
出来的一个业务过程,这是用例的实质。什么是业务过程?在待开发软件所处的业务领域内完成特
定业务任务(business task)的一系列活动就是业务过程。
四个必要条件
必要条件一 :它是不是一个业务过程?
必要条件二:它是不是由某个参与者触发开始?
必要条件三:它是不是显式地或隐式地终止于某个参与者?
必要条件四: 它是不是为某个参与者完成了有用的业务工作?
三个抽象层级
1. 抽象用例(Abstract use case)。只要用一个干什么、做什么或完成什么业务任务的动名词短
语,就可以非常精简地指明一个用例。
2. 高层用例(High level use case)。需要给用例的范围划定一个边界,也就是用例在什么时候
什么地方开始,以及在什么时候什么地方结束;
3. 扩展用例(Expanded use case)。需要将参与者和待开发软件系统为了完成用例所规定的业
务任务的交互过程一步一步详细地描述出来,一般我们使用一个两列的表格将参与者和待开发
软件系统之间从用例开始到用例结束的所有交互步骤都列举出来。
—设计模式
设计模式的本质是面向对象设计原则的实际运用总结出的经验模型。目的是包容变化,即通过使用设
计模式和多态等特殊机制,将变化的部分和不变的部分进行适当隔离。(高内聚,低耦合)
正确使用设计模式具有以下优点:
可以提高程序员的思维能力、编程能力和设计能力。
使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开
发周期。
使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
设计模式由四个部分组成:
该设计模式的名称;
该设计模式的目的,即该设计模式要解决什么样的问题;
该设计模式的解决方案;
该设计模式的解决方案有哪些约束和限制条件。
根据作用对象分两类:
类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确
定下来了。比如模板方法模式等属于类模式。
对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以
变化的,更具动态性。由于组合关系或聚合关系比继承关系耦合度低,因此多数设计模式都是
对象模式。
七大原则
1. 单一职责原则 (Single Responsibility Principle)
2. 开放关闭原则 (OpenClosed Principle)
3. 里氏替换原则 (Liskov Substitution Principle)
4. 依赖倒转原则 (Dependence Inversion Principle)
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节
应该依赖抽象
由于在软件设计中,细节具有多变性,而抽象层则相对稳定,因此以抽象为基础搭建
起来的架构要比以细节为基础搭建起来的架构要稳定得多。
依赖倒置原则在模块化设计中降低模块之间的耦合度和加强模块的抽象封装提高模块
的内聚度上具有普遍的指导意义
5. 接口隔离原则 (Interface Segregation Principle)不考
6. 迪米特法则(Law Of Demeter)
7. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle)
—MVC与MVVM
主要区别在于,MVC中,用户对于M的操作是通过C传递的,然后C将改变传给V,并且M将在发生变化时
通知V,然后V通过C获取变化;在MVVM中,用户直接与V交互,通过VM将变化传递给M,然后M改变之后
通过VM将数据传递给V,从而实现解耦。
另一个区别是,当M的数据需要进行解析后V才能使用时,C若承担解析的任务,就会变得很臃肿;在
MVVM中,VM层承担了数据解析的工作,这时C就只需要持有VM,而不需要直接持有M了。从而完成了
数据的解耦。
—软件架构
软件架构复用
1. 克隆,完整地借鉴相似项目的设计方案,甚至代码,只需要完成一些细枝末节处的修改适配工
作。
2. 重构,构建软件架构模型的基本方法,通过指引我们如何进行系统分解,并在参考已有的软件架
构模型的基础上逐步形成系统软件架构的一种基本建模方法。
几种架构的分解方法
面向功能的分解方法,用例建模即是一种面向功能的分解方法;
面向特征的分解方法,根据数量众多的某种系统显著特征在不同抽象层次上划分模块的方法;
面向数据的分解方法,在业务领域建模中形成概念业务数据模型即应用了面向数据的分解方
法;
面向并发的分解方法,在一些系统中具有多种并发任务的特点,那么我们可以将系统分解到不
同的并发任务中(进程或线程),并描述并发任务的时序交互过程;
面向事件的分解方法,当系统中需要处理大量的事件,而且往往事件会触发复杂的状态转换关
系,这时系统就要考虑面向事件的分解方法,并内在状态转换关系进行清晰的描述;
面向对象的分解方法,是一种通用的分析设计范式,是基于系统中抽象的对象元素在不同抽象
层次上分解的系统的方法。
架构的描述方法
分解视图 Decomposition View
依赖视图 Dependencies View
泛化视图 Generalization View
执行视图 Execution View
实现视图 Implementation View
部署视图 Deployment View
工作任务分配视图 Workassignment View
没有银弹的含义
“在10年内无法找到解决软件危机的杀手锏(银弹)。
软件中的根本困难,即软件概念结构(conceptual structure)的复杂性,无法达成软件概念的完整性
和一致性,自然无法从根本上解决软件危机带来的困境。
—软件的生命周期
分析、设计、实现、交付和维护五个阶段
1. 分析阶段的任务是需求分析和定义,分析阶段一般会在深入理解业务的情况下,形成业务概念原
型
2. 设计阶段分为软件架构设计和软件详细设计,前者一般和分析阶段联系紧密,一般合称为“分析
与设计”;后者一般和实现阶段联系紧密,一般合称为“设计与实现”。
3. 实现阶段分为编码和测试,其中测试又涉及到单元测试、集成测试、系统测试等。
4. 交付阶段主要是部署、交付测试和用户培训等。
5. 维护阶段一般是软件生命周期中持续时间最长的一个阶段,而且在维护阶段很可能会形成单独
的项目,从而经历分析、设计、实现、交付几个阶段,最终又合并进维护阶段。
三.经典代码示例
—回调函数
—MVC架构
四.参考资料
《代码中的软件工程》——孟宁
作者:203