本学期有幸选了《高级软件工程》这门课作为我的必修课,这门课依托孟宁老师的《代码中的软件工程》一书,以五大篇,十一小章深入浅出地讲解了有关软件工程这门学科方方面面的知识,个人感觉很多知识都紧贴实际开发,使我受益良多;孟宁老师上课诙谐幽默,生动形象并通俗易懂,高屋建瓴,能从哲学的角度来思考软件工程中遇到的一些问题,提高了我的思维宽度。本课程内容主要如下所示:
一、工欲善其事,必先利其器
这一章主要介绍了程序员的编程神器:VS Code,详细讲解了它的设计理念和设计特点,以及为什么能这么牛的原因以及开发环境的配置
其次讲解了分布式版本控制系统git,了解了git的基本用法,从五个场景来展示Git在实际工作中的强大用处。
最后讲解了提高搜索效率的秘诀:正则表达式的基本用法,如字符的匹配、替换、捕获组的使用和替换中使用捕获组复用模式等
二、工程化编程实战
这一章主要通过工程上的应用来讲解软件工程的应用,比如工程上的代码规范和代码风格,介绍了软件设计思想之模块化软件设计,然后再介绍了可重用软件设计、可重入函数与线程安全和一些细节的补充,知识点如下:
- 编写高质量代码的基本方法
- 通过控制结构简化代码
- 通过数据结构简化代码
- 一定要有错误处理
- 对调试版本中所有的参数验证正确性
- 对发布版本中从外部传递进来的参数要验证正确性
- 调试过程使用断言,只有在代码逻辑可能发生错误时才用错误处理
- 性能优先策略背后隐藏的代价
- 开发时间的代价
- 测试代码的代价
- 理解代码的代价
- 修改代码的代价
- 拒绝修修补补,要不断重构代码
- 编码过程中的团队合作
- 客户:定义需求、描述测试用例、给需求分配优先级
- 开发者:实现需求,鼓励结对编程,一个写代码,一个审阅代码提供反馈
模块化的基本原理
在软件系统设计时保持系统内各部分相对独立。以便于每一个部分可以被独立的进行设计和开发。
重要的原则在于:关注点的分离。类似于分治法,可类比为人脑各个区域功能相互独立
一般我们使用耦合度和内聚度来衡量软件模块化的程度,耦合度指软件模块之间的依赖程度,分为紧密耦合、松散耦合和无耦合,内聚度指软件模块内部各种元素之间相互依赖的紧密程度,理想的内聚是功能内聚,一个软件模块只做一件事,完成一个主要功能点,设计上我们追求高内聚低耦合
可复用软件设计的关键是接口,接口的基本概念是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定义一组API函数来约定软件模块之间的沟通方式。换句话说,接口具体定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。
其基本要素为:1.接口的目的,2.接口使用前置条件,3.接口遵循的协议规范,4.接口使用后的效果,5.接口隐藏的质量属性
传统单体集中式架构与微服务架构:
微服务架构是由一系列独立的微服务共同组成软件系统的一种架构模式,每个服务都是单独部署的,并跑在自己的进程中。
传统单体集中式架构:是指由位于系统中心的服务器统一管理全部共享资源并处理来自所有用户(客户机)的请求
接口与耦合度之间的关系分为以下几种
-
公共耦合:共享数据区或变量名
-
数据耦合:通过显示的调用传递的基本数据类型
-
标记耦合:在软件模块之间仅通过显示的调用传递复杂的数据结构
通用接口定义的方法
-
参数化上下文:将全局变量作为参数传入接口
-
移除前置条件:将参数尽可能的泛化化
-
简化后置条件
可重入函数的基本要求
-
不为连续的调用持有静态数据
-
不返回指向静态数据的指针
-
所有数据都有函数的调用者提供
-
使用局部变量来保护全局变量
-
通过临界区互斥避免冲突
-
绝不调用任何不可重入函数
三、从需求分析到软件设计
本篇围绕的重点在于需求,首先要理解需求是什么,需求的类型,然后如何获取需求,用什么方法来获得需求,以及需求分析对我们的软件设计起到什么样的作用。
本章学到的知识如下所示:
- 获取需求的主要方法
- 什么是需求
- 需求是对用户期望的软件行为的表述
- 为什么需求非常重要
- 导致软件项目失败的关键因素:需求不完整、缺乏用户参与、不现实的预期、缺乏执行力、需求和规格不断变更、缺乏合理的项目规划、软件不再被需要。多数都与需求相关。
- 有哪些类型的需求
- 功能性需求
- 非功能性需求(性能等)
- 设计约束条件:提前给定的设计决策,比如技术平台的选择限制、接口方式或协议标准的选择限制
- 过程约束条件:技术条件或资源的限制,比如仅能在特定时段提供有限的算力、开发过程所能配备的人员数量等。
- 有哪些和需求相关的人员
- 客户:为开发特定软件付钱的人
- 顾客:购买已有成熟软件产品的人
- 用户:软件实际使用者
- 行业专家、市场研究人员、律师或审计、软件工程师、技术专家
- 获取需求的主要方法
- 访谈法
- 阅读审查现有文件
- 观察当前系统或者当前的工作方法
- 想用户学习工作中处理一些工作细节的方法
- 使用特定领域的一些策略
- 高质量需求是什么样子的
- 便于验证
- 副词形容词数量化
- 代词名词实体名称化
- 名词有唯一准确的定义
- 解决了内在冲突(划分优先级)
- 本质性的需求
- 理想状态的需求
- 可有可无的需求
- 高质量需求的典型特征
- 正确
- 内在一致性
- 准确、无歧义
- 完整
- 可行
- 不存在与目标不相关的需求
- 可跟踪
- 便于验证
- 什么是需求
对需求进行分析和建模:
整理需求的两种方法:原型化方法和建模方法;其中原型化方法可以很好整理出用户交互接口的方式,比如界面布局和交互操作过程 ;而建模方法可以快速梳理出需求的内在结构,以及有关事件发生顺序或活动同步的约束问题,在逻辑上形成模型整顿繁杂的需求细节
通过用例建模:
什么是用例:某个参与者触发某个用例为相应的参与者完成一个业务任务
用例的基本要素:1.它是不是一个业务过程?2.它是不是由某个参与者触发开始?3.它是不是显式地或隐式地终止于某个参与者?4.它是不是为某个参与者完成了有用的业务工作?
用例抽象的三个层级为:1.抽象用例,2.高层用例,3.扩展用例
统一过程的核心要义是:是用例驱动(Use case driven)、以架构为中心(Architecture centric)、增量且迭代(Incremental and Iterative)的过程
四、软件科学基础概论
在这一章节中,主要是对于一些软件科学的基础理论和一些高级用法进行了讲解,在最开始学习了软件中的一些基本构成元素,包括对象,函数变量,同时从更深层次的指令,操作数,二进制符号进一步理解,探索了软件的本质。再进一步探索一些软件中的特殊机制和一些高级用法,包括回调函数,多态,闭包,异步调用,继承和对象组合等等。
随后,学习了软件的设计模式,我是第一次接触设计模式这个概念,所以很多相关的知识点都不算特别能够理解,掌握的也不是很全面,在后期还需要进一步的学习。
在本章学到的相关知识:
对象组合为什么要优于继承?
-
1、继承破坏了对象的封装性,对象组合没有
-
2、对象组合的耦合性更低
-
3、使用对象组合少了继承的一些约束,更加灵活
设计模式由哪些部分组成
-
该设计模式的名称
-
该设计模式的目的,即设计模式要解决什么样的问题
-
该设计模式的解决方案
-
该设计模式的解决方案有哪些约束和限制条件
设计模式的分类
-
按照作用对象来分
-
类模式
-
对象模式
-
-
按照可以完成的任务类型来分
-
创建型模式
-
结构型模式
-
行为型模式
-
常用的设计模式
-
单例模式
-
原型模式
-
建造者模式
-
代理模式
-
适配器模式
-
装饰模式
-
外观模式
-
享元模式
-
策略模式
-
命令模式
设计模式背后的设计原则
-
开闭原则,对拓展开放,对修改关闭
-
单一职责原则,模块化,一个模块对应一个功能
-
Liskov替换原则,继承必须保证父类拥有的性质在子类中任然成立
-
依赖倒置原则,高级和低级不相互依赖,都依赖抽象,因为抽象层相对稳定
-
迪米特法则,只和朋友说话,类似本地化外部接口
-
合成复用原则,对象组合
MVC
-
Model,数据
-
View,视图,前端
-
Controller,业务逻辑,中介者
MVVM的优点
-
低耦合
-
可重用性
-
独立开发
-
可测试
MVC和MVVM的区别
MVC中model发生变化时,M可以直接通知V,但是MVVM中M发生变化时,需要通过VM才能告知V。
数据解析中,MVC中的V需要用到C来进行解析,所以要同时拥有C和M,但是MVVM中,只需要拥有VM就可以。
软件架构复用方法
-
克隆
-
重构
软件架构模型的作用
-
从整体上理解整个系统
-
给使用者提供了一个高层视图
-
为项目的构建过程提供了一个蓝图
-
有助于理清系统演化的内部逻辑
架构分解的常用方法
-
面向功能分解
-
面向特征分解
-
面向数据分解
-
面向并发分解
-
面向事件分解
-
面向对象分解
软件架构的描述方法
-
分解视图:用软件模块勾画出系统结构,用例图
-
依赖视图:展示软件模块之间的依赖关系,关联图
-
泛化视图:展现软件模块之间一般化和具体化关系,如继承,类图
-
执行视图:展示系统运行时的时序结构,流程图,时序图
-
实现视图:描述软件架构与源文件之间的映射关系,包图
-
部署视图:将执行实体和计算机资源简历映射关系,构件图,网络拓扑结构
-
工作任务分配视图:将系统分解成可以独立完成的工作任务
五、软件危机和软件过程
在这一章节中,主要是讲解了关于软件危机的诞生以及应对软件危机所产生的一些有效的方式,比如没有银弹的含义:“在10年内无法找到解决软件危机的杀手锏(银弹),其原因在于工程专家们所找到的各种方法都是舍本逐末,它们解决不了软件中的根本困难,即软件概念结构的复杂性,无法达成软件概念的完整性和一致性,自然无法从根本上解决软件危机带来的困境
软件的生命周期:一般来讲,我们将软件的生命周期划分为:分析、设计、实现、交付和维护这么五个阶段
- 分析阶段的任务是需求分析和定义,比如在敏捷统一过程中用例建模和业务领域建模就属于分析阶段。分析阶段一般会在深入理解业务的情况下,形成业务概念原型,业务概念原型是业务功能和业务数据模型的有机统一体,比如用例的集合和业务数据模型,每一个用例在逻辑上都可以通过操作业务数据模型完成关键的业务过程。
- 设计阶段分为软件架构设计和软件详细设计,前者一般和分析阶段联系紧密,一般合称为“分析与设计”;后者一般和实现阶段联系紧密,一般合称为“设计与实现”。
- 实现阶段分为编码和测试,其中测试又涉及到单元测试、集成测试、系统测试等。
- 交付阶段主要是部署、交付测试和用户培训等。
- 维护阶段一般是软件生命周期中持续时间最长的一个阶段,而且在维护阶段很可能会形成单独的项目,从而经历分析、设计、实现、交付几个阶段,最终又合并进维护阶段
瀑布模型会将整个软件开发过程中的众多风险积累到最后才能暴露出来,为了尽早暴露风险和控制风险,在瀑布模型的基础上增加一个原型化(prototyping)阶段,可以有效将风险前移,改善整个项目的技术和管理上的可控性。
V模型也是在瀑布模型基础上发展出来的,我们发现单元测试、集成测试和系统测试是为了在不同层面验证设计,而交付测试则是确认需求是否得到满足。 也就是瀑布模型中前后两端的过程活动具有内在的紧密联系,如果将模块化设计的思想拿到软件开发过程活动的组织中来, 可以发现通过将瀑布模型前后两端的过程活动结合起来,可以提高过程活动的内聚度,从而改善软件开发效率。 这就是V模型
- PSP和TSP
- 团队基本要素
- 团队的规模
- 团队的凝聚力
- 团队协作的基本条件
- 团队基本要素
- 敏捷方法
- 敏捷开发的宗旨
- 互联网使得知识的获取变得更加容易,很多软件可以由一个小团队来实现。同时,技术更新的速度在加快,用户需求的变化也在加快,开发流程必须跟上这些快速变化的节奏。于是敏捷方法就产生了。
- 敏捷方法的敏捷宣言
- 个体和互动 高于 流程和工具
- 工作的软件 高于 详尽的文档
- 客户合作 高于 合同谈判
- 响应变化 高于 遵循计划
- 尽管右项有其价值,我们更重视左项的价值。
- Scrum敏捷开发方法
- 敏捷开发的宗旨
- DevOps
- 性能优先策略带来的隐藏代价
- cost to write the code faster。当软件工程师的人力成本远大于所消耗的计算资源成本时,提高代码编写的工作效率将更有价值;
- cost to test the code。质量保证的人力成本和质量保证的成效也比所消耗的计算资源成本更有价值;
- cost to understand the code。性能优先的策略往往会让代码很难理解,结果需要消耗更多的工时;
- cost to modify the code。面向机器的代码修改起来更困难,可扩展性差,同样会消耗更多工时。
- 性能优先策略带来的隐藏代价
总体来说学习这门课程使我收获良多,认识到软件的开发是一件复杂且漫长的事情,绝不是如我想象的那么简单,需要遵循很多既定的标准才能开发一个健壮的软件系统,学到了一些软件开发的设计模式这些都是开发人员的经验总结,正所谓理论来源于实践又高于实践,只有通过不断地实践-总结-再实践,整个软件体系才能不断进步。