高级软件工程复习资料大全

1.概述

  • 功能 用例图

  • 结构 业务类图

  • 形式 数据模式

  • 高质量需求的特点

  • 有哪些需求类型

  • 需求分析的两类基本方法

  • 用例满足的四个必要条件

  • 统一过程的核心要义是什么

  • 模块化的基本原理

  • 本地化外部接口是什么含义

  • 接口的5个基本要素

  • 通用接口定义的基本方法

  • 依赖倒置原则的含义及其应用价值

  • MVC架构为什么更灵活以及MVVM架构为什么更智能(10分)

  • 执行视图、部署视图、分解视图

  • 没有银弹的含义

  • 简述v模型

  • 团队强度和项目特点的关系

  • 简述敏捷宣言的核心思想

  • 编程大题:写一个menu里面自定义查找结点的测试代码(20分)

  • 需求分析:分析现有软件, 用你们写的软件的用户量来证明你了解用户的需求

  • 设计阶段:用快速发布来证明设计是有效的, 能适应变化的。

  • 实现阶段:用各种软件工程的衡量手段来证明大家实现的能力。

  • 稳定阶段:证明测试能否覆盖代码的大部分。

  • 发布阶段: 如期发布, 用户量, 用户评价。

  • 维护阶段:网上的观众或下一个年级的同学能很愿意接手你们的软件。

  • 工欲善其事必先利其器——Typing - VSCode - Git - Vim - RegEx

  • 代码中的软件工程——一个工程化C语言项目范例

  • 需求分析与设计——从分析到设计的基本方法

  • 软件系统设计——软件的结构、特性和描述方法

  • 工程过程与项目管理——软件危机的前生后世

  • 从需求表述中找出用例,往往是动名词短语表示的抽象用例;

  • 描述用例开始和结束的状态,用TUCBW和TUCEW表示的高层用例;

  • 对用例按照子系统或不同的方面进行分类,描述用例与用例、用例与参与者之间的上下文关系,并画出用例图;

  • 进一步逐一分析用例与参与者的详细交互过程,完成一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来扩展用例。

    image-20220104224006800
  • 找出关键步骤进行剧情描述(scenario)

  • 将剧情描述(scenario)转换成剧情描述表(scenario table)

    • KISS原则(Keep it simple and stupid),保持剧情足够简洁,将细节问题留给编码阶段;

    • 优先描述正常剧情,假定所有事情都按预期进行,将异常处理留给编码阶段;

    • 如果需要的话描述正常剧情的多个不同可选流程,以增强设计和编码阶段的灵活性;

    • 有时需要为正常剧情构建一个原型来验证设计的流程是否可行

  • 将剧情描述表转换成序列图的基本方法

    • 情形一:主体(Subject)是一个参与者(Actor)
    • 情形二:主体(Subject)是一个对象(Object)
    • 情形三:主体(Subject)需要接收返回值的情形
    • 情形四:主体(Subject)和主体行为的作用对象(object acted upon)是同一个对象的情形
  • 从分析序列图到设计序列图

    image-20220104215825311

    首先我们需要理解分析和设计的区别,我们可以大致总结成如下几点:

    • 目的不同。分析为了搞清楚应用问题;而设计为了找出软件解决方案;

    • 建模的对象不同。分析是对应用业务领域建模;而设计是对待开发的软件系统建模;

    • 一个是描述(describes),一个说明(prescribes)。分析是对应用业务实际情况的描述,业务实际情况是客观存在;设计是待开发的软件解决方案如何实现应用业务的说明,软件实际上还不存在;

    • 决策的依据不同;分析是基于应用问题的项目做决策;设计是基于待开发的软件系统做决策;

  • 一个完整用例的对象交互建模 - 类图

    ​ 将时序图中的放入类图中

    ​ 将时序图中的方法放在属于他们的类中

    ​ 从对象模型和时序图中填入相应的属性

    ​ 从对象模型和时序图中填入相关的关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2o5tR6XG-1648542041316)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220104230016250.png)]

2.需求分析

2.1 获取需求的主要方法

2.1.1 什么是需求?

​ 需求分析是在获取需求的基础上进一步对软件涉及的对象或实体的状态、特征和行为进行准确描述或建模的工作。

2.1.2 为什么需求非常重要?
2.1.3 获取需求的主要困难
2.1.4 有哪些类型的需求?
  • 功能需求
  • 质量需求和非功能需求
  • 设计限制
  • 过程限制
2.1.5 有哪些和需求相关的人员?
2.1.6 获取需求的主要方法
  • 采访消费者
  • 观察现有的系统
  • 与用户一同了解需求
  • 采访组中用户和利益相关者
  • 特定的设计策略
  • 头脑风暴
2.1.7 高质量的需求是什么样子?
  • 可以被测试的
  • 能够解决冲突
  • 对需求进行具体化特征化
2.1.8 需求的特征

​ 正确、一致、无二义性、完整、可行、无与主要目标不相干的需求、可测试的、可回溯的。

2.2 对需求进行分析和建模

2.2.1 原型化方法(Prototyping)和建模的方法(Modeling)
  • 原型化方法可以很好地整理出用户接口方式(UI,User Interface),比如界面布局和交互操作过程。
  • 建模的方法可以快速给出有关事件发生顺序或活动同步约束的问题,能够在逻辑上形成模型来整顿繁杂的需求细节。
2.2.2 用例建模(Use Case Modeling)

用例是一个抽象的业务过程。由业务领域内某个参与者出发,为特定参与者完成一个特定的业务任务。

  • 抽象用例(Abstract use case)。只要用一个干什么、做什么或完成什么业务任务的动名词短语,就可以非常精简地指明一个用例;

  • 高层用例(High level use case)。需要给用例的范围划定一个边界,也就是用例在什么时候什么地方开始,以及在什么时候什么地方结束;

  • 扩展用例(Expanded use case)。需要将参与者和待开发软件系统为了完成用例所规定的业务任务的交互过程一步一步详细地描述出来,一般我们使用一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来。

image-20220104151510754
2.2.3 用例建模的基本步骤
  • 第一步,从需求表述中找出用例,往往是动名词短语表示的抽象用例;

  • 第二步,描述用例开始和结束的状态,用TUCBW和TUCEW表示的高层用例;

  • 第三步,对用例按照子系统或不同的方面进行分类,描述用例与用例、用例与参与者之间的上下文关系,并画出用例图;

  • 第四步,进一步逐一分析用例与参与者的详细交互过程,完成一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来扩展用例。

    image-20220104152120536
image-20220104152921499
2.2.4 面向对象分析涉及的基本概念
  • 对象和属性

  • 继承关系 概括化和具体化

    image-20220104153352978
  • 聚合关系 部分和整体

    image-20220104153434129
  • 关联关系 除了继承和聚合以外的其他关系

    image-20220104153631282
2.2.5 业务领域建模(Domain Modeling)(对象模型)

​ 业务领域建模是开发团队用于获取业务领域知识的过程。

  • 收集信息

  • 思考

  • 分类(分明那些属于类、属性)

    • 类可以独立存在
    • 属性不能独立存在
    • 动词更多表示一种联系
  • 构建 (用UML图画出来)

    image-20220104155320411 img
2.2.6 关联类及关系数据模型
image-20220104155510759
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;
}
2.2.7 关系数据模型的MongoDB设计与实现
  • 内嵌 使用一对很少且不需要单独访问内嵌内容

  • 子引用 一对很多

  • 父引用 一的那端引用嵌入多的一端对象中

  • 优先考虑内嵌,除非有什么迫不得已的原因。

  • 需要单独访问一个对象,那这个对象就不适合被内嵌到其他对象中。

  • 数组不应该无限制增长。如果many端有数百个文档对象就不要去内嵌他们可以采用引用ObjectID的方案;如果有数千个文档对象,那么就不要内嵌ObjectID的数组。该采取哪些方案取决于数组的大小。

  • 不要害怕应用层级别的join:如果索引建的正确并且通过投影条件限制返回的结果,那么应用层级别的join并不会比关系数据库中join开销大多少。

  • 在进行反范式设计时请先确认读写比。一个几乎不更改只是读取的字段才适合冗余到其他对象中。

  • 在mongodb中如何对你的数据建模,取决于你的应用程序如何去访问它们。数据的结构要去适应你的程序的读写场景。

3.从需求分析到软件设计

3.1 敏捷统一过程

  • 瀑布模型:需求 -> 设计 -> 编码 -> 测试 -> 部署

  • 核心要义:用例驱动 ,以架构为中心 , 增量且迭代

  • 关键步骤

    • 第一,确定需求;
    • 第二,通过用例的方式来满足这些需求;
    • 第三,分配这些用例到各增量阶段;
    • 第四,具体完成各增量阶段所计划的任务。

    显然,第一到第三步主要是计划阶段的工作,第四步是接下来要进一步详述的增量阶段的工作。

  • 增量阶段的步骤

    • 用例建模(Use case modeling);

    • 业务领域建模(Domain modeling);

    • 对象交互建模(Object Interaction modeling);

    • 形成设计类图(design class diagram);

    • 软件的编码实现和软件应用部署;

3.2 对象交互建模

image-20220104194616720

对象交互建模的基本步骤

  • 找出关键步骤进行剧情描述(scenario)

  • 将剧情描述(scenario)转换成剧情描述表(scenario table)

    • KISS原则(Keep it simple and stupid),保持剧情足够简洁,将细节问题留给编码阶段;

    • 优先描述正常剧情,假定所有事情都按预期进行,将异常处理留给编码阶段;

    • 如果需要的话描述正常剧情的多个不同可选流程,以增强设计和编码阶段的灵活性;

    • 有时需要为正常剧情构建一个原型来验证设计的流程是否可行

  • 将剧情描述表转换成序列图的基本方法

    • 情形一:主体(Subject)是一个参与者(Actor)
    • 情形二:主体(Subject)是一个对象(Object)
    • 情形三:主体(Subject)需要接收返回值的情形
    • 情形四:主体(Subject)和主体行为的作用对象(object acted upon)是同一个对象的情形
  • 从分析序列图到设计序列图

    image-20220104215825311

    首先我们需要理解分析和设计的区别,我们可以大致总结成如下几点:

    • 目的不同。分析为了搞清楚应用问题;而设计为了找出软件解决方案;

    • 建模的对象不同。分析是对应用业务领域建模;而设计是对待开发的软件系统建模;

    • 一个是描述(describes),一个说明(prescribes)。分析是对应用业务实际情况的描述,业务实际情况是客观存在;设计是待开发的软件解决方案如何实现应用业务的说明,软件实际上还不存在;

    • 决策的依据不同;分析是基于应用问题的项目做决策;设计是基于待开发的软件系统做决策;

      image-20220104224715683
  • 一个完整用例的对象交互建模 形成设计类图(DCD)的基本步骤

    • 将时序图中的放入类图中

    • 将时序图中的方法放在属于他们的类中

    • 从对象模型和时序图中填入相应的属性

    • 从对象模型和时序图中填入相关的关系

      image-20220104225958247

3.3 形成软件设计方案的基本方法

  • 分析 将大问题分解成一个个小问题
  • 综合 将小问题的解答综合成一个大问题

4.软件系统设计

4.1 设计模式

4.1.1 本质

​ 面向对象设计的实际运用,对类封装性、继承性、多态性、关联和组合的充分理解。

  • 模式名称
  • 解决的问题
  • 解决方案
  • 约束和限制条件

二十三种设计模式

  • 创建型模式
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
  • 结构型模式
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  • 行为型模式
模板方法模式、命令模式、迭代器模式、观察者模式、中介模式、备忘录模式、状态模式、策略模式、职责链模式、访问者模式

设计模式背后的原则

  • 开闭原则:对扩展开放,不建议修改

  • 里氏替换原则:子类尽量不用重写父类的方法

  • 依赖倒置原则:要面向接口编程,不要面向实现编程

  • 单一职责原则:每个类只完成一件事情

  • 迪米特法则:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。

  • 合成复用原则:优先考虑组合或者聚合等关联,其次再考虑继承等关系

4.1.2 单例模式

​ 某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,典型的应用如数据库实例。

4.1.3 原型模式

​ 将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例,原型模式的应用场景非常多,几乎所有通过复制的方式创建新实例的场景都有原型模式。

4.1.4 建造者模式

​ 将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。主要应用于复杂对象中的各部分的建造顺序相对固定或者创建复杂对象的算法独立于各组成部分。

4.1.5 工厂模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7tsvikt-1648542041319)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220105140924511.png)]

4.1.6 抽象工厂模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltmtdTKm-1648542041322)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220105142134418.png)]

image-20220105141628229

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azDhWpBv-1648542041323)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220105144828126.png)]

4.1.7 代理模式

​ 为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理模式是不要和陌生人说话原则的体现,典型的应用如外部接口本地化将外部的输入和输出封装成本地接口,有效降低模块与外部的耦合度

4.1.8 适配器模式

​ 从程序的结构实现松耦合,进而扩大整体的类结构

​ 为网线和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); // 适配器
image-20220105230935934
  • 客户端需要实现一个request,但只能找到适配者来实现,但缺少一个中介
  • 适配器实现了request并且继承了适配者的方法
  • 客户端通过创建一个target接口的对象,就可以调用request方法了。
image-20220105231423515
  • 客户端需要实现一个request,但只能找到适配者来实现,但缺少一个中介
  • 适配器找到了适配者,并实现了request,但以适配者作为参数。
  • 客户端首先需要创建一个适配者对象,然后把适配者传递给适配器,最后用适配器实现需求。
4.1.9 装饰(Decorator)模式

​ 在不改变现有对象结构的情况下,动态地给对象增加一些职责,即增加其额外的功能。装饰模式实质上是用对象组合的方式扩展功能,因为比继承的方式扩展功能耦合度低。装饰模式在 Java 语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。

4.1.10 外观模式

​ 为复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。

4.1.11 享元模式

​ 运用共享技术来有效地支持大量细粒度对象的复用。比如线程池、固定分配存储空间的消息队列等往往都是该模式的应用场景。

4.1.12 策略(Strategy)模式

​ 定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。策略模式是多态和对象组合的综合应用。

4.1.13 命令(Command)模式

​ 将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

4.1.14 模板方法模式

​ 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。模版方法是继承和重载机制的应用,属于类模式。

image-20220105232917500

​ 子类可以定义方法的顺序和类型。

4.1.15 职责链模式

​ 为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。通过这种方式将多个请求处理者串联为一个链表,去除请求发送者与它们之间的耦合。

image-20220105234355980
4.1.16 中介者(Mediator)模式

​ 定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者,采用“中介者模式”大大降低了对象之间的耦合性,提高系统的灵活性。

4.1.17 观察者模式

​ 指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zzscw4Vy-1648542041325)(C:\Users\jack\AppData\Roaming\Typora\typora-user-images\image-20220106010831362.png)]

4.2 软件架构

4.2.1 三层架构

image-20220106130816742

4.2.2 MVC架构
  • MVC中M、V和C所代表的含义如下:

  • Model(模型)代表一个存取数据的对象及其数据模型。

  • View(视图)代表模型包含的数据的表达方式,一般表达为可视化的界面接口。

  • Controller(控制器)作用于模型和视图上,控制数据流向模型对象,并在数据变化时更新视图。控制器可以使视图与模型分离开解耦合。

4.2.3 MVVM架构

​ 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)]

  • 如何更新view和model 将=号替换成set和get方法并递归
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中实现。

4.2.4 软件架构复用方法
  • 克隆和重构
4.2.5 主流设计方法
  • 功能分解
  • 面向特征分解
  • 面向数据分解
  • 面向过程分解
  • 面向事件分解
  • 面向对象分解
4.2.6 软件架构风格和策略
  • 管道-过滤器
  • 客户-服务
  • P2P
  • 发布-订阅
  • CRUD
  • 层次化
4.2.7 软件架构描述
  • 分解视图 分解视图用软件模块勾划出系统结构,往往会通过不同抽象层级的软件模块形成层次化的结构。
  • 依赖视图 依赖视图展现了软件模块之间的依赖关系
  • 泛化视图 泛化视图展现了软件模块之间的一般化或具体化的关系
  • 执行试图 系统运行时的时序结构特点
  • 实现视图 软件架构和源文件
  • 部署视图 部署视图是将执行实体和计算机资源建立映射关系
  • 工作任务分配视图 工作分配视图将系统分解成可独立完成的工作任务,以便分配给各项目团队和成员。
4.2.8 软件质量的三种视角
  • 产品视角
  • 过程视角
  • 价值视角
4.2.9 软件质量的属性
  • 易于修改维护(Modifiability)

  • 良好的性能表现(Performance)

  • 安全性(Security)

  • 可靠性(Reliability)

  • 健壮性(Robustness)

  • 易用性(Usability)

  • 商业目标(Business goals)

4.3 软件构成

4.3.1 基本构成元素
  • 对象(Object)
  • 函数和变量/常量
  • 指令和操作数
  • 0和1是什么?
4.3.2 存储分布
  • 全局常量(const)、字符串常量、函数以及编译时可决定的某些东西一般存储在代码段(text);

  • 初始化的全局变量、初始化的静态变量(包括全局的和局部的)存储在已初始化数据段;

  • 未初始化的全局变量、未初始化的静态变量(包括全局的和局部的)存储在未初始化数据段(BSS);

  • 动态内存分配(malloc、new等)存储在堆(heap)中;

  • 局部变量(初始化以及未初始化的,但不包含静态变量)、局部常量(const)存储在栈(stack)中;

  • 命令行参数和环境变量存储在与栈底紧挨着的位置。

4.3.3 软件中的一些特殊机制
  • 回调函数

    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();
    
  • 异步调用

  • 匿名函数

4.3.4 软件内在特性
  • 前所未有的复杂度

  • 抽象思维 vs. 逻辑思维

  • 唯一不变的就是变化本身

  • 难以达成的概念完整性和一致性

5.软件危机

5.1 软件危机

5.1.1 概念

​ 软件系统规模越来愈大,复杂度高,可靠性突出,个人设计不满足要求,迫切需要改变生产方式,提高软件开发效率。

5.1.2 没有银弹

​ 在10年内无法找到解决软件危机的杀手锏

5.1.3 根本问题

​ 软件概念结构(conceptual structure)的复杂性,无法达成软件概念的完整性和一致性,自然无法从根本上解决软件危机带来的困境。

5.2 软件过程模型

5.2.1 软件生命周期概述

​ 分析 -> 设计 -> 实现 -> 交付 -> 维护

5.2.2 瀑布模型(没有任何迭代)

​ 需求分析 -> 系统设计 -> 程序设计 -> 代码编写 -> 单元测试 和集成测试-> 系统测试 -> 交付测试- > 操作与维修

5.2.3 带原型的瀑布模型

​ 为了尽早暴露风险和控制风险,在瀑布模型的基础上增加一个原型化(prototyping)阶段。

​ 原型就是根据需要完成的软件的一部分,完成哪一部分是根据开发原型的目标确定,比较常见的有用户接口原型和软件架构原型。

5.2.4 v模型

​ 把瀑布模型前后两端的过程活动结合起来,提高过程活动的内聚度,改善开发的效率。

5.2.5 分阶段增量和迭代
  • 增量开发就是从一个功能子系统开始交付,每次交付会增加一些功能,这样逐步扩展功能最终完成整个系统功能的开发。

  • 迭代开发是首先完成一个完整的系统或者完整系统的框架,然后每次交付会升级其中的某个功能子系统,这样反复迭代逐步细化最终完成系统开发

5.2.6 螺旋模型

​ 引入了其他模型不具备的风险管理,使软件在无法排除重大风险时有机会停止,以减小损失。同时,在每个迭代阶段构建原型是螺旋模型用以减小风险的基本策略。

四大阶段

  • 计划
  • 决定目标、选择和限制
  • 评估选择和风险
  • 发展和测试

5.3 PSP和TSP

PSP 个体软件工程开发

  • 计划阶段

  • 开发阶段

    • 设计
    • 编码
    • 测试
  • 总结分析

TSP 团队软件工程开发

基本要素

  • 团队规模
  • 团队凝聚力
  • 团队协作的基本条件

5.4 CMM/CMMI

​ 软件成熟度模型

5.5 敏捷方法

5.5.1 核心思想
  • 个体和互动 高于 流程和工具

  • 工作的软件 高于 详尽的文档

  • 客户合作 高于 合同谈判

  • 响应变化 高于 遵循计划

5.5.2 基本流程
  • 找出完成产品需要做的事情
  • 决定当前冲刺需要解决的问题
  • 冲刺
  • 得到软件的一个增量版本
5.5.3 主要缺陷
  • 压力大
  • 不方便跨时区、跨语言
  • 程序维护成本偏高
  • 无法被中断

5.6 DevOps

5.6.1 核心

​ 软件工程、技术运营和质量保障的交集

​ DevOps看成是敏捷方法从技术开发领域扩展到业务运维领域

5.6.2 精益创业
  • 全过程——不放过任何一个环节。

  • 全员化——要树立全员成本控制的理念,而不是仅靠创业者自己。

  • 标准化——要力求做到量化,能够定量的要定量,不能定量的要定性,做到成本管理有依据可衡量。

  • 责任化——明确承担相应责任的对象,如果没有明确的责任人的话,成本目标和手段等均将形同虚设。

6.码农的自我修养

6.1 vscode

打开文件夹( 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   切换集成终端
image-20220107000407447

6.2 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 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

合并方法

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

6.3 vim

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 !举例来说,在 100200 行之间搜寻 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次	

6.4 正则表达式

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

7.工程化编程实战

7.1 代码风格原则

  • 简明

  • 易读

  • 无二义性

    image-20220107003428515

7.2 编写高质量代码的基本方法

  • 通过控制结构简化代码
  • 通过数据结构简化代码
  • 一定要有错误处理

7.3 衡量模块化的程度

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

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

7.4 接口规格的要素

  • 接口的目的
  • 接口使用前所需要满足的条件,一般称为前置条件或假定条件;
  • 使用接口的双方遵守的协议规范;
  • 接口使用之后的效果,一般称为后置条件;
  • 接口所隐含的质量属性。

7.5 微服务

​ 微服务架构的基本概念可以简单概括为通过模块化的思想垂直划分业务功能

7.6 三种耦合

  • 公共耦合

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

  • 数据耦合

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

  • 标记耦合

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

7.7 看待软件质量的几个角度

  • 产品的角度,也就是软件产品本身内在的质量特点;
  • 用户的角度,也就是软件产品从外部来看是不是对用户有帮助,是不是有良好的用户体验;
  • 商业的角度,也就是商业环境下软件产品的商业价值,比如投资回报或开发软件产品的其他驱动因素。

你可能感兴趣的:(高级软件工程,git,正则表达式)