功能 用例图
结构 业务类图
形式 数据模式
高质量需求的特点
有哪些需求类型
需求分析的两类基本方法
用例满足的四个必要条件
统一过程的核心要义是什么
模块化的基本原理
本地化外部接口是什么含义
接口的5个基本要素
通用接口定义的基本方法
依赖倒置原则的含义及其应用价值
MVC架构为什么更灵活以及MVVM架构为什么更智能(10分)
执行视图、部署视图、分解视图
没有银弹的含义
简述v模型
团队强度和项目特点的关系
简述敏捷宣言的核心思想
编程大题:写一个menu里面自定义查找结点的测试代码(20分)
需求分析:分析现有软件, 用你们写的软件的用户量来证明你了解用户的需求
设计阶段:用快速发布来证明设计是有效的, 能适应变化的。
实现阶段:用各种软件工程的衡量手段来证明大家实现的能力。
稳定阶段:证明测试能否覆盖代码的大部分。
发布阶段: 如期发布, 用户量, 用户评价。
维护阶段:网上的观众或下一个年级的同学能很愿意接手你们的软件。
工欲善其事必先利其器——Typing - VSCode - Git - Vim - RegEx
代码中的软件工程——一个工程化C语言项目范例
需求分析与设计——从分析到设计的基本方法
软件系统设计——软件的结构、特性和描述方法
工程过程与项目管理——软件危机的前生后世
从需求表述中找出用例,往往是动名词短语表示的抽象用例;
描述用例开始和结束的状态,用TUCBW和TUCEW表示的高层用例;
对用例按照子系统或不同的方面进行分类,描述用例与用例、用例与参与者之间的上下文关系,并画出用例图;
进一步逐一分析用例与参与者的详细交互过程,完成一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来扩展用例。
找出关键步骤进行剧情描述(scenario)
将剧情描述(scenario)转换成剧情描述表(scenario table)
KISS原则(Keep it simple and stupid),保持剧情足够简洁,将细节问题留给编码阶段;
优先描述正常剧情,假定所有事情都按预期进行,将异常处理留给编码阶段;
如果需要的话描述正常剧情的多个不同可选流程,以增强设计和编码阶段的灵活性;
有时需要为正常剧情构建一个原型来验证设计的流程是否可行
将剧情描述表转换成序列图的基本方法
从分析序列图到设计序列图
首先我们需要理解分析和设计的区别,我们可以大致总结成如下几点:
目的不同。分析为了搞清楚应用问题;而设计为了找出软件解决方案;
建模的对象不同。分析是对应用业务领域建模;而设计是对待开发的软件系统建模;
一个是描述(describes),一个说明(prescribes)。分析是对应用业务实际情况的描述,业务实际情况是客观存在;设计是待开发的软件解决方案如何实现应用业务的说明,软件实际上还不存在;
决策的依据不同;分析是基于应用问题的项目做决策;设计是基于待开发的软件系统做决策;
一个完整用例的对象交互建模 - 类图
将时序图中的类放入类图中
将时序图中的方法放在属于他们的类中
从对象模型和时序图中填入相应的属性
从对象模型和时序图中填入相关的关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2o5tR6XG-1648542041316)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220104230016250.png)]
需求分析是在获取需求的基础上进一步对软件涉及的对象或实体的状态、特征和行为进行准确描述或建模的工作。
正确、一致、无二义性、完整、可行、无与主要目标不相干的需求、可测试的、可回溯的。
用例是一个抽象的业务过程。由业务领域内某个参与者出发,为特定参与者完成一个特定的业务任务。
抽象用例(Abstract use case)。只要用一个干什么、做什么或完成什么业务任务的动名词短语,就可以非常精简地指明一个用例;
高层用例(High level use case)。需要给用例的范围划定一个边界,也就是用例在什么时候什么地方开始,以及在什么时候什么地方结束;
扩展用例(Expanded use case)。需要将参与者和待开发软件系统为了完成用例所规定的业务任务的交互过程一步一步详细地描述出来,一般我们使用一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来。
第一步,从需求表述中找出用例,往往是动名词短语表示的抽象用例;
第二步,描述用例开始和结束的状态,用TUCBW和TUCEW表示的高层用例;
第三步,对用例按照子系统或不同的方面进行分类,描述用例与用例、用例与参与者之间的上下文关系,并画出用例图;
第四步,进一步逐一分析用例与参与者的详细交互过程,完成一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来扩展用例。
对象和属性
继承关系 概括化和具体化
聚合关系 部分和整体
关联关系 除了继承和聚合以外的其他关系
业务领域建模是开发团队用于获取业务领域知识的过程。
收集信息
思考
分类(分明那些属于类、属性)
构建 (用UML图画出来)
class Student { ... }
class Course {...}
class Enroll {
private:
char grade;
Student* student;
Course* course;
public:
Enroll (Student* s, Course* c);
char getGrade();
void setGrade(char grade);
}
Enroll::Enroll(Student* s, Course* c) {
student=s; course=c;
}
内嵌 使用一对很少且不需要单独访问内嵌内容
子引用 一对很多
父引用 一的那端引用嵌入多的一端对象中
优先考虑内嵌,除非有什么迫不得已的原因。
需要单独访问一个对象,那这个对象就不适合被内嵌到其他对象中。
数组不应该无限制增长。如果many端有数百个文档对象就不要去内嵌他们可以采用引用ObjectID的方案;如果有数千个文档对象,那么就不要内嵌ObjectID的数组。该采取哪些方案取决于数组的大小。
不要害怕应用层级别的join:如果索引建的正确并且通过投影条件限制返回的结果,那么应用层级别的join并不会比关系数据库中join开销大多少。
在进行反范式设计时请先确认读写比。一个几乎不更改只是读取的字段才适合冗余到其他对象中。
在mongodb中如何对你的数据建模,取决于你的应用程序如何去访问它们。数据的结构要去适应你的程序的读写场景。
瀑布模型:需求 -> 设计 -> 编码 -> 测试 -> 部署
核心要义:用例驱动 ,以架构为中心 , 增量且迭代
关键步骤
显然,第一到第三步主要是计划阶段的工作,第四步是接下来要进一步详述的增量阶段的工作。
增量阶段的步骤
用例建模(Use case modeling);
业务领域建模(Domain modeling);
对象交互建模(Object Interaction modeling);
形成设计类图(design class diagram);
软件的编码实现和软件应用部署;
对象交互建模的基本步骤
找出关键步骤进行剧情描述(scenario)
将剧情描述(scenario)转换成剧情描述表(scenario table)
KISS原则(Keep it simple and stupid),保持剧情足够简洁,将细节问题留给编码阶段;
优先描述正常剧情,假定所有事情都按预期进行,将异常处理留给编码阶段;
如果需要的话描述正常剧情的多个不同可选流程,以增强设计和编码阶段的灵活性;
有时需要为正常剧情构建一个原型来验证设计的流程是否可行
将剧情描述表转换成序列图的基本方法
从分析序列图到设计序列图
首先我们需要理解分析和设计的区别,我们可以大致总结成如下几点:
目的不同。分析为了搞清楚应用问题;而设计为了找出软件解决方案;
建模的对象不同。分析是对应用业务领域建模;而设计是对待开发的软件系统建模;
一个是描述(describes),一个说明(prescribes)。分析是对应用业务实际情况的描述,业务实际情况是客观存在;设计是待开发的软件解决方案如何实现应用业务的说明,软件实际上还不存在;
决策的依据不同;分析是基于应用问题的项目做决策;设计是基于待开发的软件系统做决策;
一个完整用例的对象交互建模 形成设计类图(DCD)的基本步骤
将时序图中的类放入类图中
将时序图中的方法放在属于他们的类中
从对象模型和时序图中填入相应的属性
从对象模型和时序图中填入相关的关系
面向对象设计的实际运用,对类封装性、继承性、多态性、关联和组合的充分理解。
二十三种设计模式
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
模板方法模式、命令模式、迭代器模式、观察者模式、中介模式、备忘录模式、状态模式、策略模式、职责链模式、访问者模式
设计模式背后的原则
开闭原则:对扩展开放,不建议修改
里氏替换原则:子类尽量不用重写父类的方法
依赖倒置原则:要面向接口编程,不要面向实现编程
单一职责原则:每个类只完成一件事情
迪米特法则:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。
合成复用原则:优先考虑组合或者聚合等关联,其次再考虑继承等关系
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,典型的应用如数据库实例。
将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例,原型模式的应用场景非常多,几乎所有通过复制的方式创建新实例的场景都有原型模式。
将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。主要应用于复杂对象中的各部分的建造顺序相对固定或者创建复杂对象的算法独立于各组成部分。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7tsvikt-1648542041319)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220105140924511.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltmtdTKm-1648542041322)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220105142134418.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azDhWpBv-1648542041323)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220105144828126.png)]
为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理模式是不要和陌生人说话原则的体现,典型的应用如外部接口本地化将外部的输入和输出封装成本地接口,有效降低模块与外部的耦合度
从程序的结构实现松耦合,进而扩大整体的类结构
为网线和USB提供转换的接口
系统需要使用一些现有的类,而类的接口不符合系统的需要,甚至没有这些类的方法。
// 要被适配的类: 网线
public class Adaptee {
public void request() {
System.out.println("连接网线上网");
}
}
// 客户端类: 想上网,插不上网线
public class computer {
public void net(NetToUsb adapter) {
adapter.handleRequest();
}
}
// 网线转usb口
public interface NetToUsb {
public void handleRequest();
}
// 继承
public class Adapter extends Adaptee implements NetToUsb {
public void handleRequest() {
super.request();
}
}
// 组合
public class Adapter2 implements NetToUsb {
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
this.adpatee = adaptee;
}
public void handleRequest() {
adaptee.request();
}
}
Computer computer = new Computer();// 电脑
Adaptee adaptee = new Adaptee(); // 网线
Adapter2 adapter = new Adapter2(adaptee); // 适配器
在不改变现有对象结构的情况下,动态地给对象增加一些职责,即增加其额外的功能。装饰模式实质上是用对象组合的方式扩展功能,因为比继承的方式扩展功能耦合度低。装饰模式在 Java 语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。
为复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
运用共享技术来有效地支持大量细粒度对象的复用。比如线程池、固定分配存储空间的消息队列等往往都是该模式的应用场景。
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。策略模式是多态和对象组合的综合应用。
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。模版方法是继承和重载机制的应用,属于类模式。
子类可以定义方法的顺序和类型。
为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。通过这种方式将多个请求处理者串联为一个链表,去除请求发送者与它们之间的耦合。
定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者,采用“中介者模式”大大降低了对象之间的耦合性,提高系统的灵活性。
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zzscw4Vy-1648542041325)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220106010831362.png)]
MVC中M、V和C所代表的含义如下:
Model(模型)代表一个存取数据的对象及其数据模型。
View(视图)代表模型包含的数据的表达方式,一般表达为可视化的界面接口。
Controller(控制器)作用于模型和视图上,控制数据流向模型对象,并在数据变化时更新视图。控制器可以使视图与模型分离开解耦合。
M: model 用于存取前端页面的数据集
V: view 看到的html页面
VM: viewmodel 桥梁
view
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eoFDI5MT-1648542041327)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220106135713062.png)]
model
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSZWxDXk-1648542041328)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220106135740968.png)]
viewmodel
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t5eS0ULT-1648542041330)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220106135822670.png)]
Observer.prototype.transform = function(data){
for(var key in data){
var value = data[key];
// 将等于转换成set和get方法
Object.defineProperty(data, key, {
enumerable:true,
configurable:true,
get:function(){
return value;
},
set:function(newVal){
if(newVal == value){
return;
}
//遍历newVal
this.transform(newVal);
data[key] = newVal;//赋值还会调用set方法死循环了
}
});
//递归处理
this.transform(value);
}
};
编译视图模板
当编译视图模版创建Watcher对象时,也就是观察者模式中的观察者,其中通过触发getter方法将Watcher对象自身添加到观察者列表中。这一过程几句关键代码按照执行顺序罗列如下
//创建Watcher对象时,获取它要监听占位符/表达式的一个成员对应的getter方法
this.getter = parseExpression(exp).get;
this.update();
//执行该getter方法,如果没有通过Object.defineProperty定义则没有该getter方法
var newVal = this.get();
Dep.target = this;
var value = this.getter?this.getter(this.vm):'';
//该getter方法中添加观察者到观察者列表
dep.addSub(Dep.target);
观察者模式
将Compiler的解析结果,与Observer所观察的对象连接起来建立关系,在Observer观察到对象数据变化时,接收通知,同时更新DOM,称之为Watcher;
1.修改message
2.触发setter方法
3.发送更新通知给观察者
4.观察者更新视图
//发送更新通知给观察者
Dep.prototype.notify = function(newVal){
for(var uid in this.subs){
this.subs[uid].update(newVal);
}
};
//观察者更新视图
update :function(){
var newVal = this.get();
if(this.value != newVal){
this.cb && this.cb(newVal, this.value);
this.value = newVal;
}
//在method中修改message
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
//触发setter方法
set:function(newVal){
if(newVal == value){
return;
}
//data[key] = newVal;
//死循环!赋值还会调用set方法
value = newVal;
//为什么可以这样修改?闭包依赖的外部变量
//遍历newVal
this.transform(newVal);
//发送更新通知给观察者
dep.notify(newVal);
}
MVVM与MVC的最大区别就是:它实现了View和Model的自动同步,也就是当Model的数据改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变数据后该数据对应View层显示会自动改变。MVVM并不是用VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。
易于修改维护(Modifiability)
良好的性能表现(Performance)
安全性(Security)
可靠性(Reliability)
健壮性(Robustness)
易用性(Usability)
商业目标(Business goals)
全局常量(const)、字符串常量、函数以及编译时可决定的某些东西一般存储在代码段(text);
初始化的全局变量、初始化的静态变量(包括全局的和局部的)存储在已初始化数据段;
未初始化的全局变量、未初始化的静态变量(包括全局的和局部的)存储在未初始化数据段(BSS);
动态内存分配(malloc、new等)存储在堆(heap)中;
局部变量(初始化以及未初始化的,但不包含静态变量)、局部常量(const)存储在栈(stack)中;
命令行参数和环境变量存储在与栈底紧挨着的位置。
回调函数
int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char*) args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
//执行回调函数
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable,
int Conditon(tLinkTableNode * pNode, void * args),
void * args)
{
...
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != NULL)
{
if(Conditon(pNode, args) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
int main()
{
...
//传递回调函数
SearchLinkTableNode(head, SearchCondition, (void*)cmd);
…
}
多态
闭包
闭包是变量作用域的一种特殊情形,一般用在将函数作为返回值时,该函数执行所需的上下文环境也作为返回的函数对象的一部分,这样该函数对象就是一个闭包。
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
异步调用
匿名函数
前所未有的复杂度
抽象思维 vs. 逻辑思维
唯一不变的就是变化本身
难以达成的概念完整性和一致性
软件系统规模越来愈大,复杂度高,可靠性突出,个人设计不满足要求,迫切需要改变生产方式,提高软件开发效率。
在10年内无法找到解决软件危机的杀手锏
软件概念结构(conceptual structure)的复杂性,无法达成软件概念的完整性和一致性,自然无法从根本上解决软件危机带来的困境。
分析 -> 设计 -> 实现 -> 交付 -> 维护
需求分析 -> 系统设计 -> 程序设计 -> 代码编写 -> 单元测试 和集成测试-> 系统测试 -> 交付测试- > 操作与维修
为了尽早暴露风险和控制风险,在瀑布模型的基础上增加一个原型化(prototyping)阶段。
原型就是根据需要完成的软件的一部分,完成哪一部分是根据开发原型的目标确定,比较常见的有用户接口原型和软件架构原型。
把瀑布模型前后两端的过程活动结合起来,提高过程活动的内聚度,改善开发的效率。
增量开发就是从一个功能子系统开始交付,每次交付会增加一些功能,这样逐步扩展功能最终完成整个系统功能的开发。
迭代开发是首先完成一个完整的系统或者完整系统的框架,然后每次交付会升级其中的某个功能子系统,这样反复迭代逐步细化最终完成系统开发
引入了其他模型不具备的风险管理,使软件在无法排除重大风险时有机会停止,以减小损失。同时,在每个迭代阶段构建原型是螺旋模型用以减小风险的基本策略。
四大阶段
PSP 个体软件工程开发
计划阶段
开发阶段
总结分析
TSP 团队软件工程开发
基本要素
软件成熟度模型
个体和互动 高于 流程和工具
工作的软件 高于 详尽的文档
客户合作 高于 合同谈判
响应变化 高于 遵循计划
软件工程、技术运营和质量保障的交集
DevOps看成是敏捷方法从技术开发领域扩展到业务运维领域
全过程——不放过任何一个环节。
全员化——要树立全员成本控制的理念,而不是仅靠创业者自己。
标准化——要力求做到量化,能够定量的要定量,不能定量的要定性,做到成本管理有依据可衡量。
责任化——明确承担相应责任的对象,如果没有明确的责任人的话,成本目标和手段等均将形同虚设。
打开文件夹( 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+`1,2,3 切换集成终端
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 rebase命令格式大致如下:
git rebase -i [startpoint] [endpoint]
其中-i的意思是--interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支的HEAD。
一般只指定[startpoint] ,即指定从某一个commit节点开始,可以使用HEAD^^、HEAD~100、commit ID或者commit ID的头几个字符来指定一个commit节点,比如下面的代码指定重新整理HEAD之前的三个commit节点。
$ git rebase -i HEAD^^^
git rebase —abort
git rebase --continue
合并方法
默认的合并方式为"快进式合并"(fast-farward merge),会将分支里commit合并到主分支里,合并成一条时间线,与我们期望的呈现为一段独立的分支线段不符,因此合并时需要使用--no-ff参数关闭"快进式合并"(fast-farward merge)。
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 撤销
r 重做
/word 向光标之下寻找一个名称为 word 的字符串。例如要在档案内搜寻 vbird 这个字符串,就输入 /vbird 即可! (常用)
?word 向光标之上寻找一个字符串名称为 word 的字符串。
n 这个 n 是英文按键。代表重复前一个搜寻的动作。举例来说, 如果刚刚我们执行 /vbird 去向下搜寻 vbird 这个字符串,则按下 n 后,会向下继续搜寻下一个名称为 vbird 的字符串。如果是执行 ?vbird 的话,那么按下 n 则会向上继续搜寻名称为 vbird 的字符串!
N 这个 N 是英文按键。与 n 刚好相反,为『反向』进行前一个搜寻动作。 例如 /vbird 后,按下 N 则表示『向上』搜寻 vbird 。
使用 /word 配合 n 及 N 是非常有帮助的!可以让你重复的找到一些你搜寻的关键词!
:n1,n2s/word1/word2/g n1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串,并将该字符串取代为 word2 !举例来说,在 100 到 200 行之间搜寻 vbird 并取代为 VBIRD 则:『:100,200s/vbird/VBIRD/g』。(常用)
s是substitute的简写,表示执行替换字符串操作
g(global)表示全局替换;c(comfirm)表示操作时需要确认;i(ignorecase)表示不区分大小写
:1,$s/word1/word2/g 或 :%s/word1/word2/g 从第一行到最后一行寻找 word1 字符串,并将该字符串取代为 word2 !(常用)
:1,$s/word1/word2/gc 或 :%s/word1/word2/gc 从第一行到最后一行寻找 word1 字符串,并将该字符串取代为 word2 !且在取代前显示提示字符给用户确认 (confirm) 是否需要取代!(常用)
将当前文件中xxx字符串全部替换为yyy字符串 :1,$s/xxx/yyy/g
将当前文件中10-20行的代码注释掉 :10,20s/^/#/g
将2-3行代码复制粘贴10次
https://gitee.com/fragile_xia/ustc_sse_lesson/blob/master/%E9%AB%98%E7%BA%A7%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F_note.md
简明
易读
无二义性
耦合度是指软件模块之间的依赖程度,一般可以分为紧密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)和无耦合(Uncoupled)。
内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度。理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性。
微服务架构的基本概念可以简单概括为通过模块化的思想垂直划分业务功能
公共耦合
当软件模块之间共享数据区或变量名的软件模块之间即是公共耦合,显然两个软件模块之间的接口定义不是通过显式的调用方式,而是隐式的共享了共享了数据区或变量名。
数据耦合
在软件模块之间仅通过显式的调用传递基本数据类型即为数据耦合。
标记耦合
在软件模块之间仅通过显式的调用传递复杂的数据结构(结构化数据)即为标记耦合,这时数据的结构成为调用双方软件模块隐含的规格约定,因此耦合度要比数据耦合高。但相比公共耦合没有经过显式的调用传递数据的方式耦合度要低。