中科大软件学院《高级软件工程》课程学习总结

木刅水 2022-07-10 17:21:37

软件工程:该课程研究和应用如何以系统性的、规范化的、可定量的过程化方法去开发和维护软件,以及如何把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来的学科。它涉及到程序设计语言数据库软件开发工具系统平台、标准、设计模式等方面。

IEEE对软件工程的定义:"将系统化的、规范的、可度量的方法用于软件的开发、运行和维护的过程,即将工程化应用于软件开发中"

作为一名跨专业到软件工程专业的学生,接触软件是相对有一些吃力的,刚开始的时候也是听从学长的建议刷一些算法题、做一些项目,但是始终没有对软件工程又一个系统性的学习,对软件工程的理解还停留在写代码的印象上。而本学期刚好选上了孟老师的《高级软件工程》这一门课,刚好填补了自己的不足,也有了一些学习心得。孟老师课上以概念+实践的方式带我们走进了软件工程的领域,该门课程涵盖范围很广,包括软件的开发流程、编码必备工具(如vim、git等)、软件架构、设计模式等等都有涉及,并且孟老师也布置了一些实践项目加深了我对软件工程的理解。

 

一、软件工程概要

软件的开发流程

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

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

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

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

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

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

代码的良好风格

规范整洁、逻辑清晰、优雅

二、软件工程模块化处理

模块化基本原理

- 模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独立地进行设计和开发。

- 这个做法背后的基本原理是关注点分离

关注点的分离在软件工程领域是最重要的原则,我们习惯上称为模块化,翻译成我们中文的表述其实就是“分而治之”的方法

- 软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。

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

一般在软件设计中追求松散耦合。

内聚度:内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度

理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性(Feather)。

三、可重用软件设计

消费者重用

• 消费者重用是指软件开发者在项目中重用已有的一些软件模块代码,以加快项目工作进度。软件开发者在重用已有的软件模块代码时一般会重点考虑如下四个关键因素:

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

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

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

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

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

接口规格

•  接口规格是软件系统的开发者正确使用一个软件模块需要知道的所有信息,那么这个软件模块的接口规格定义就必须清晰明确地说明正确使用本软件模块的信息。一般来说,接口规格包含五个基本要素:

•  接口的目的

•  接口使用前所需要满足的条件,一般称为前置条件或假定条件

•  使用接口的双方遵守的协议规范

•  接口使用之后的效果,一般称为后置条件

•  接口所隐含的质量属性

微服务

• 由一系列独立的微服务共同组成软件系统的一种架构模式

• 每个微服务单独部署,跑在自己的进程中,也就是说每个微服务可以有一个自己独立的运行环境和软件堆栈

• 每个微服务为独立的业务功能开发,一般每个微服务应分解到最小可变产品(MVP),达到功能内聚的理想状态。微服务一般通过RESTful API接口方式进行封装

• 系统中的各微服务是分布式管理的,各微服务之间非常强调隔离性,互相之间无耦合或者极为松散的耦合,系统通过前端应用或API网关来聚合各微服务完成整体系统的业务功能。

可重入函数与线程安全

可重入函数

- 可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。相反,不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入函数要么使用局部变量,要么在使用全局变量时保护自己的数据。

基本要求:

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

-  不返回指向静态数据的指针

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

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

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

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

什么是线程安全

- 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

可重入性与线程安全之间的关系

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

不可重入的函数一定不是线程安全的

四、函数调用框架

- esp,堆栈指针寄存器(stack pointer)。

ebp,基址指针寄存器(base pointer)。

eip,指令指针寄存器,为了安全考虑程序无权修改该寄存器,只能通过专用指令修改该寄存器,比如call和ret

pushl,压栈指令,由于X86体系结构下栈顶从高地址向低地址增长,所以压栈时栈顶地址减少4个字节。

popl,出栈指令,栈顶地址增加4个字节。

call,函数调用指令,该指令负责将当前eip指向的下一条指令地址压栈,然后设置eip指向被调用程序代码开始处,即pushl eip和eip = 函数名(指针)。

ret,函数返回指令,该指令负责将栈顶数据放入eip寄存器,即popl eip,此时的栈顶数据应该正好是call指令压栈的下一条指令地址

注意以e开头的寄存器位32位寄存器,指令结尾的l是指long,也就是操作4个字节数据的指令。

五、设计模式

基本概念与优点

- 设计模式的本质是面向对象设计原则的实际运用总结出的经验模型。对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解的基础上才能准确理解设计模式。

- 优点:

     - 可以提高程序员的思维能力、编程能力和设计能力

     - 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期

     - 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强

四个关键机制

- 多态:多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。简单的说就是允许将子类类型的指针赋值给父类类型的指针。

- 回调函数:回调函数是一个面向过程的概念,是代码执行过程的一种特殊流程。

- 闭包:闭包是变量作用域的一种特殊情形,一般用在将函数作为返回值时,该函数执行所需的上下文环境也作为返回的函数对象的一部分,这样该函数对象就是一个闭包。

-  lamda函数:lamda函数是函数式编程中的高阶函数,在我们常见的命令式编程语言中常常以匿名函数的形式出现,比如无参数的代码块{ code },有参数的匿名函数往往会使用箭头函数 { x => code }。

常用的设计模式

 

单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,典型的应用如数据库实例

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

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

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

适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。继承和对象组合都可以实现适配器模式,但由于组合关系或聚合关系比继承关系耦合度低,所以对象组合方式的适配器模式比较常用。​​​​​​​

 

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

外观(Facade)模式:为复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问

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

 

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

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

参考资料:代码中的软件工程​​​​​​​

 

...全文
57 回复 打赏 收藏 举报
写回复
回复
切换为时间正序
请发表友善的回复…
发表回复
发帖
代码中的软件工程

395

社区成员

软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
帖子事件
创建了帖子
2022-07-10 17:21