说是课程收获,其实更像是知识点总结(bushi)
授课老师是孟宁,参考书是《代码中的软件工程》,按照ppt课程可以看作有5个单元
一、工欲善其事,必先利其器
介绍了vscode、git、vim、正则表达式。作为第一个单元,没有用一堆概念把人吓跑,而是介绍了几个实用性很强的编程工具或技术,鉴定为:能学到东西的
二、工程化编程实战——代码中的软件工程
-
代码风格的原则:简明、易读、无二义性
-
接口注释:名称、功能描述、输入、输出、返回值
-
命名规范
- 变量使用LowerCamel风格,即第一个单词首字母小写之后的大写
- 类、函数等使用Pascal风格,所有单词首字母大写
-
好的代码风格
- 使用控制结构
- 设计合适的数据结构
- 程序一定要有错误处理
- Debug版本验证所有参数,Release版本验证从外部传进来的参数
- 肯定如何时使用断言;可能如何时使用错误处理
- 拒绝修修补补,不断重构代码
-
性能优先的代价
- 代码可读性差、不容易维护,可扩性差、不容易修改,编程人员工作效率低
- 计算机的硬件成本在不断降低,而工程师的人力成本在增加,所以编写质量保证的代码更有意义
-
极限编程中的两类参与者:客户与程序员
-
结对编程中的两类参与者:驾驶员与观察员
-
模块化
- 基本原理:关注点分离(Soc)
- 好处:模块的目标功能单一,容易理解和开发;容易定位bug;系统容易变更和维护
- 使用耦合度和内聚度来衡量模块化的程度
-
耦合度, 指模块之间的依赖程度,分为紧耦合、松散耦合和无耦合,一般追求松散耦合
-
内聚度,指一个模块内部元素的互相依赖的紧密程度,理想的内聚为功能内聚,即一个模块只做一件事
-
软件设计的一些基本方法
- KISS(keep it simple & stupid)每个元素(一行代码、一个函数等)都只做一件事
- 本地化外部接口提高代码适应能力,这是一种设计模式,即代理模式,可以降低模块与外部地耦合度,同时也符合迪米特法则
- 先写伪代码。因为伪代码略去了很多编程细节,最大限度保留了设计上的框架结构,先写伪代码会使代码结构更好。另外代码要有一个框架以避免代码的无序生长
-
可重用软件设计
- 消费者重用。指开发者重用已有的软件模块以加快进度。需要考虑已有模块的功能;是否比从头构建的工作量要少;是否有文档;是否有完整的测试及修订记录
- 生产者重用。指生产可重用的模块,这要求在软件设计时考虑一些因素,比如设计通用的模块与接口,接口要有清晰完善的定义描述,命名规则要清晰一致,要有文档描述数据结构与算法,记录下缺陷与修订情况等
-
接口
-
基本概念:互相联系的双方共同遵守的一种协议规范。在软件设计中就是定义一组函数来约定模块之间的沟通方式。结构定义了一个模块提供了怎样的服务以及其他部分如何访问其所提供的服务。在面向过程中,接口一般是数据结构以及对数据结构的操作。在面向对象中,接口一般是对象对外开放的属性与方法的集合。总的来说都是函数定义
-
五要素:目的、使用前所需的条件(前置条件)、双方遵守的协议规范、使用之后的效果(后置条件)、接口的质量属性
-
-
传统单体集中式架构与微服务架构
-
微服务
- 微服务就是独立的业务功能,是最小可变产品(MVP),是功能内聚的理想状态
- 每个微服务单独部署,有独立的运行环境和软件堆栈
- 各微服务是分布式管理的,有很强的隔离性,互相之间无耦合或松散耦合
- 系统聚合各微服务来完成整体的业务功能
- 微服务通过restful接口进行封装
-
restful接口,rest意为表示层状态转化。表示层背后的信息实体就是各种资源或者服务,(比如数据库中的表,或是某种功能),状态转化就是HTTP协议中的get、post、put、delete这些操作,通过这些操作可以获得服务或是改变资源的状态
-
传统单体集中式架构是适应大型机、小型机单体服务器环境的软件架构模式。微服务架构则是为了适应PC服务器的大规模集群及云计算环境的架构模式。简单来说就是网络以及硬件资源的发展给架构带来的进化
-
接口与耦合度
无耦合—数据耦合—标记耦合—控制耦合—公共耦合—内容耦合。从左到右耦合度依次递增
- 公共耦合,模块之间共享数据区或变量名,这意味着模块之间的接口不是显式地调用方式,而是隐式共享数据区或变量名
- 标记耦合,模块之间仅通过显式地调用传递复杂的数据结构,数据地结构就是双方隐含的规格约定
- 数据耦合,模块之间仅通过显示地调用传递基本数据类型
-
通用接口定义的基本方法
- 参数化上下文,通过参数传递上下文信息,而不是隐含依赖上下文环境
- 移除前置条件
- 简化后置条件
-
看待软件质量的几个不同角度
- 产品角度。软件产品本身内在的质量特点
- 用户角度。是否对用户有帮助以及用户体验
- 商业角度。商业环境下的商业价值,比如投资回报等
-
软件设计的方法和原则
设计方法论:不断地重构
设计指导原则
-
模块化
-
接口
-
抽象
-
信息隐藏
-
三、从需求分析到软件设计
-
需求
需求是期望行为的表述
需求分析就是需求分析师对用户期望的软件行为进行表述,并进一步用对象或实体的状态、属性和行为来定义需求
-
需求的类型
- 功能需求,根据需要的活动描述需要的行为
- 质量需求,描述软件必须具备的一些质量特性
- 设计约束,设计决策,比如平台或接口组件的选择
- 过程约束,对可用于构建系统的技术或资源的限制
-
高质量需求
-
需求可测试的。为形容词和副词指定数量描述,用实体的具体名称替换代词,确保每个名词都在文档的同一位置被定义
-
解决冲突,不同的利益相关者与不同的需求,这其中可能包含了潜在的冲突。所以需要权衡多种需求,可以将需求划分成三类:基本需求(一定要满足)、理想的需求(最好能满足但不是必须)、可选的需求
-
需求要具备这些特点:正确的、一致的、无二义性的、完整的、可行的、无与主要目标不相关的需求、可描绘的
-
-
需求分析的两类基本方法
-
原型化方法,可以很好地整理出用户接口方式,比如界面布局和交互操作地过程
-
建模的方法,可以快速给出有关事件发生顺序和活动同步约束问题,在逻辑上形成模型来整顿需求细节
(个人理解就是原型化面向用户,建模面向开发人员)
-
-
用例
用例是业务过程的抽象,有以下基本要素:
- 由业务领域内的某个参与者触发
- 为参与者完成一个业务任务
- 参与者明确或隐含地得到了业务任务完成地结果,用例终止
-
三个抽象层级
- 抽象用例。只指明需要做什么(借书、还书)
- 高层用例。划定了用例的范围(图书馆系统)
- 扩展用例。列举出参与者和待开发软件之间从用例开始到结束地所有交互步骤(借书还书的具体操作)
-
用例建模的步骤
- 从需求表述中找出用例
- 描述用例开始和结束的状态
- 对用例分类,描述用例与用例、用例与参与者之间的上下文关系,并画出用例图
- 进一步分析用例与参与者的详细交互过程,将参与者与待开发软件系统之间的从用例开始到结束的所有交互步骤列入表格
-
准确提取用例的方法
- 从需求中寻找业务邻域相关的动名词和动名词短语
- 验证是否是用例(要满足用例的基本要素)
- 在需求中识别出参与者、系统、子系统
-
业务领域建模的基本步骤
- 收集业务邻域的信息,聚焦在功能需求层面(获取需求阶段完成)
- 头脑风暴,列出业务领域概念,给出这些概念的属性以及之间的关系。比如列出相关领域的名词、动名词、形容词以及关系等
- 为业务领域概念分类,哪些是类、哪些是属性以及类之间的关系(根据“对象可以独立存在而属性不能”来确定类,比如常见情况下名词就是类,名词相关的动词表明类之间的关系)
- 将结果用UML类图画出
-
关联类与数据模型
关联关系是业务数据建模的关键,由关联关系引入了关联类
比如:学生类,课程类,学生选课就会有成绩,所以引入选课类
MangoDB数据库是一种文档型数据库,使用JSON格式的文档来存储数据。三种一对多建模方案
- 一对少,将多的那一方内嵌到少的那一方
- 一对多,间接引用,多的那一方需要单独存放,和一对少类似
- 一对许多,父级引用。比如日志收集中,每个及其有大量的日志信息,只在文档中存主机,在日志文档中存主机的ID。将少的那一方嵌入到多的那一方
-
瀑布模型与统一过程
-
瀑布模型(waterfall process),最基本的过程模型,比如将整个软件过程划分成需求、设计、编码、测试、部署
-
统一过程(unified process),是一种以用例驱动、以架构为中心迭代且增量的软件过程模型
-
敏捷统一过程就是进一步将软件过程中每次迭代划分成计划阶段和增量阶段
-
-
敏捷统一过程的关键步骤
- 确定需求
- 通过用例满足需求
- 分配这些用例到各增量阶段
- 具体完成各增量阶段的任务
-
增量阶段的五个阶段(这五个阶段其实相当于瀑布模型)
- 用例建模(抽象业务过程)
- 业务领域建模(确定类、属性、类之间的关系)
- 对象交互建模
- 设计类图
- 软件的编码实现和软件应用部署
-
分析与设计
- 目的不同。前者是搞清应用问题,后者是找出软件解决方案
- 建模对象不同。分析是应用业务领域建模,设计是对待开发的软件系统建模
- 分析是描述,设计是说明
- 分析基于应用问题的项目做决策,设计基于待开发软件做决策
- 分析时允许不同的设计方案,设计会减少实现上的可选择性
-
形成设计类图的基本步骤
在序列图和业务领域建模的基础上
- 识别序列图中用到的所有类并放到设计类图中
- 识别属于类的方法并填充到设计类图中
- 识别并填写属性
- 识别序列图和领域模型中类的关系并在设计类图中表示出来
-
形成软件设计方案的基本方法
- 分析,对用例进行分析形成设计类图
- 综合,将所有用例的设计类图综合成一个整体设计方案
四、软件科学基础概论
-
封装使得对象组合能够替代继承。继承虽然重用了代码,但破坏了代码的封装特性,增加了父类和子类的代码模块耦合,所以实际上应该避免使用继承
-
回调函数。不由函数的设计方直接调用,而是在特定条件下由另一方调用。回调函数提升了编程的效率
-
闭包。当函数作为返回值时,函数执行时所需的上下文环境也是返回的函数对象的一部分,这样函数对象就是一个闭包。严谨的定义是,函数对其周围状态的应用捆绑在一起构成闭包
-
异步调用。promise对象是回调函数进行封装
-
匿名函数。lamda表达式
-
软件的基本特点:前所未有的复杂度与易变性
-
软件系统分类
- S系统,程序来源自静态规范,可以被形式化的证明,比如数学计算
- P系统,程序是对真实世界的抽象,包含不确定的请况。对问题的表述以及解决方法接近真实世界,比如天气预报程序
- E系统,程序容易发生变化,程序成为它所建模世界的一部分,主要用来处理有人或真实世界参与的活动,比如操作系统(嵌入到现实世界并随着世界的变化而变化)
大多数软件系统都是E系统,因为需求或环境会发生变化,所以对软件的建模无法到达终点
-
设计模式背后的设计原则
- 开闭原则。软件应该对扩展开发,对修改关闭。要求基本需求稳定且被充分理解
- 里氏替换原则。继承必须确保超类所拥有的性质在子类中仍然成立。子类可以扩展父类的功能,但不能改变父类原有的功能,即尽量不重写父类的方法
- 依赖倒置原则。要面向接口编程,不要面向实现编程。因为实现要考虑很多细节,但抽象层相对稳定,以抽象为基础搭建起来的架构更稳定
- 单一职责原则。一个类应该有且只有一个引起它变化的原因,否则就应该拆分。该原则控制类的粒度,降低类的复杂度,提高类的内聚度
- 迪米特法则。只与直接朋友交谈,不跟陌生人说话。如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以使用第三方转发该调用。这样做降低类之间的耦合度,提高模块的相对独立性。(本地化外部接口就是这个原则的应用)
- 合成复用原则。软件复用时,优先使用组合或聚合关系,其次才考虑继承。使用继承必须遵守里氏替换原则
-
组合与聚合
- 组合,部分与整体强依赖,比如公司与部门,公司不存在,那部门也不存在,这里公司组合了部门
- 聚合,部分与整体不是强依赖,比如部门与人员,部门聚合了人员,但部门没了,人员仍然存在
-
复用
- 继承复用,破坏类的封装性,父类的细节暴露给了子类;子类和父类的耦合度高,父类的改变会导致子类的变化;继承复用限制了复用的灵活性,从父类继承的实现是静态的,在编译时已被定义,所以在运行时不可能发生变化
- 合成复用,组合或聚合复用,可以将已有对象作为一个属性放到新类中,这样维持了已有类的封装性;类之间的耦合度低;可以动态地引用与对象类型相同的对象,所以更灵活
-
MVC架构
- M模型,代表一个存取数据的对象及其数据模型
- V视图,一般为可视化界面接口
- C控制器,作用于模型和视图上,控制数据流向模型,并在数据变化时更新视图。控制器使视图和模型解耦
- 控制器创建模型和视图,并将模型和视图关联。控制器负责改变模型的状态,当模型状态改变时通知相关视图进行更新
-
MVVM架构
将视图和模型做了最大限度的分离,然后引入视图模型VM,VM负责把V的修改同步到M,把M的修改更新到V
优点:低耦合。视图和模型之间不再绑定;可重用性,可以把试图逻辑放到VM中,让很多视图重用;开发人员可以专注业务逻辑和数据开发,设计人员专注于页面设计;可测试
-
两种不同层级的架构复用:克隆与重构
-
软件架构模型的作用
- 有助于从整体上理解整个系统
- 给复用提供了一个高层视图
- 为整个项目的构建提供了一个蓝图
-
构建软件架构模型的方法:在不同层次上分解系统并抽象出关键要素
常见的分解方法
- 面向功能,用例建模
- 面向特征,根据系统的多种显著特征在不同的抽象层次上
- 面向数据,业务领域建模
- 面向并发,将系统分解到不同并发任务中,并描述时序交互过程
- 面向事件,系统要处理大量的事件,事件会触发状态转换关系,最终描述状态转换图
- 面向对象,基于对象的在不同的抽象层次上进行分解
-
软件架构
- 管道过滤器,面向数据流
- 客户-服务,CS和BS
- P2P,文件共享网络
- 发布订阅,消息队列
- CRUD,信息系统
- 层次化,OSI网络
-
架构描述方法
- 分解视图,呈现分解结构特点,用软件模块勾画系统结构。子系统,包,类,组件,库,模块,单元
- 依赖视图,展示模块之间的依赖关系
- 泛化视图,软件模块之间一般化与具体化的关系,比如类之间的继承与组合或聚合
- 执行视图,展示系统的时序结构特点,比如流程图,时序图。每个执行实体称作组件
- 实现视图,描述软件架构与源文件之间的映射关系,比如源文件目录树
- 部署视图,执行实体与计算机资源的映射关系,执行实体的粒度要与计算机资源匹配,比如进程可以对应主机,还有主机的网络拓扑结构
- 工作分配视图,将系统分解成可独立完成的工作任务
-
软件质量的视角
- 用户视角,满足或超出用户预期
- 工业生产,符合标准规范
- 产品,良好的内在特性
- 市场价值,客户的付费意愿
用户看的是外在质量,开发者看见的是内部质量
-
MyCall质量模型
- 产品修订(更改的能力)
- 产品过渡(适应新环境)
- 产品运营(基本运营特征)
-
质量生命周期(ISO/IEC 25010软件质量模型)
- 使用质量,在使用场景中,产品使得用户达到有效性、满意度、安全性、生产率等目标的能力
- 过程质量,指开发和维护的过程
因过程质量产生了过程改进模型,比如CMM/CMMI,能力成熟度模型/能力成熟度集成
-
CMM/CMMI五个层次
- 持续优化级
- 量化管理级
- 已定义级
- 管理级
- 初始级
-
重要的质量属性
- 易维护,要求进行高内聚低耦合的模块化设计
- 性能表现,响应时间短,吞吐量大,负载高
- 安全性,免疫与恢复
- 可靠性
- 健壮性,适应能力强或能从故障中恢复
- 易用性,人性化交互设计
五、软件危机和软件过程
-
没有银弹。针对软件危机,专家们找到的各种方法都是舍本逐末,无法解决软件的根本困难,即软件概念结构的复杂性,无法达成软件概念的完整性和一致性,自然无法从根本上解决软件危机带来的困难。这也是软件危机的根本问题
-
软件生命周期,分析(需求分析和定义)、设计(架构设计和软件详细设计)、实现(编码和测试)、交付(部署,交付测试,用户培训)、维护
-
不同的过程模型适用于不同的要达到的过程目标
-
过程模型
- 瀑布模型,将开发过程按顺序组织过程活动,所以广泛应用于解释项目进展情况及所处的阶段。瀑布模型要求需求被完全理解并且几乎不会再发生变更,不会有产品的迭代,这也意味着有些软件开发过程中的风险会被累积到最后
- 原型化瀑布模型,可以有效将风险前移,改善项目的技术和管理上的可控性
- V模型。将瀑布模型前后两端的过程活动结合起来,提高过程活动的内聚度,从而改善开发效率。其依据是测试就是为了验证设计,最后的交付测试则是确认需求是否得到满足,这就是说瀑布中前后两端的过程活动具有内在紧密联系
生死相依原则,开始一项工作之前先思考验证该工作完成的方法。比如对象的创建方法和销毁方法同时出现,V模型就是类似的思想,开始一个特定过程活动和评估该特定过程的过程活动成对出现
-
分阶段开发,有两种迭代和增量
- 再系统完成之前就开始交付和培训
- 频繁的软件发布可以让开发者敏捷地应对问题
- 再不同版本聚焦不同功能,提高效率
- 有助抢占市场
-
螺旋模型,引入了风险管理。每个迭代阶段都构建原型
- 计划
- 确定目标、替代方案和限制约束
- 评估替代方案和风险
- 开发及测试
-
个人软件过程
- 计划
- 项目评估
- 开发
- 分析
- 设计规格
- 编码标准规范
- 设计
- 审评
- 编码
- 审评
- 测试
- 统计记录各项工作耗时
- 测试报告
- 程序规模度量
- 开发完成后进行总结分析
- 过程改进计划
以上是PSP2.1,PSP3.0就是迭代循环上面过程
- 计划
-
敏捷宣言
- 个体和互动高于流程和文档
- 工作地软件高于详尽地文档
- 呵护合作高于合同谈判
- 相应变化高于遵循计划
-
scrum是一种迭代地增量软件项目管理方法,是敏捷方法中最常见地软件开发模型之一
-
scrum团队角色
- 项目经理
- 产品经理
- 团队
-
DevOps是一组过程、方法与系统得到统称,用于促进开发、运营、质量保证部门之间地沟通协作与整合,为了按时交付产品与服务,开发和运营必须紧密合作。是开发、技术运营和质量保证地交集
学号后三位309
参考资料: 《代码中的软件工程》(https://gitee.com/mengning997/se)