高级软件工程学习总结

目录

1 码农的自我修养

1.1 软件工程的定义

1.2 软件工程的本质特征

1.3 软件工程的基本原理

1.4 课程教学目标

2 工欲善其事必先利其器

2.1 Typing

2.2 Visual Studio Code

2.2.1 简介

2.2.2 vscode为什么能这么牛

2.3 Git

2.3.1 Git与版本控制

2.3.2 Git的基本操作逻辑

2.3.3 Git的设计理念和时间线

2.4 Vi/Vim

2.4.1 简介

2.4.2 vi/vim的三种模式

2.5 正则表达式

2.5.1 简介

2.5.2 为什么使用正则表达式

3 工程化编程实战

3.1 代码规范和代码风格

3.1.1 代码风格原则

3.1.2 代码风格三重境界

3.1.3 代码风格规范中介

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

3.2 模块化软件设计

3.2.1 模块化的基本原理

3.2.2 模块化代码的基本写法

3.3 可重用软件设计

3.3.1 消费者重用和生产者重用

3.3.2 接口的基本概念

3.3.3 接口规格包含五个基本要素

3.3.4 微服务的概念

3.4 可重入函数与线程安全

3.4.1 线程的基本概念

3.4.2 可重入函数定义

3.4.3 可重入函数的基本要求

3.4.4 什么是线程安全

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

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

4.1 需求及需求分析

4.1.1 什么是需求

4.1.2 什么是需求分析

4.2 从需求进行分析与建模

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

4.2.2 什么是用例

4.2.3 用例的三个抽象层级

4.3 从需求分析到软件设计

4.3.1 瀑布模型

4.3.2 统一过程

4.3.3 敏捷统一过程的计划阶段

4.3.4 敏捷统一过程的四个关键步骤

4.3.4 敏捷统一过程的增量阶段

4.4 总结

5 软件科学基础概论

5.1 软件是什么

5.1.1 软件的基本构成元素

5.1.2 软件的基本结构

5.1.3 软件中的一些特殊机制        

5.2 软件设计模式初步

5.2.1 设计模式设计的基本概念

5.2.2 什么是设计模式

5.2.3 设计模式的优点

5.2.4 设计模式的分类

5.2.5 常用的设计模式

5.2.6 设计模式背后的设计原则

5.3 常见的软件架构

5.3.1 三层架构

5.3.2 MVC架构

5.3.3 MVVM架构

5.4 软件架构风格与策略

5.4.1 管道-过滤器

5.4.2 客户-服务

5.4.3 P2P

5.4.4 发布-订阅

5.4.5 CRUD

5.4.6 层次化

6 软件危机和软件过程

6.1 软件危机

6.1.1 产生背景

6.1.2 软件危机的展望

6.2 软件过程

6.2.1 软件的生命周期概述

6.2.2 软件的故障率曲线

6.2.3 瀑布模型

6.2.4 V模型

6.3 CMM/CMMI

6.3.1 简介

6.3.2 CMM/CMMI的5个等级

6.3.3 CMM/CMMI的作用

6.4 总结

7 课程总结


1 码农的自我修养

1.1 软件工程的定义

        根据1993年IEEE为软件工程赋予的规范定义:软件工程是把系统的、规范的、可度量的途径应用于软件开发、运行和维护过程,也就是把工程应用于软件。

1.2 软件工程的本质特征

(1)软件工程堆较大型的程序构造起着重要作用
(2)软件工程的研究中心是会程序复杂性的控制
(3)软件工程需要对软件进行经常化
(4)软件工程中软件的开发速度与质量至关重要
(5)开发软件是一件需要多方进行配合与合作的事情
(6)软件需要为其用户提供支持与存在感

1.3 软件工程的基本原理

(1)要对生命的周期做出有计划的分阶段专业管理
(2)要坚持进行阶段性的评析和审视
(3)对产品质量与走向进行严格控制
(4)不断采取和学习现代程序设计新技术
(5)对结果要有清晰明了的审查
(6)软件开发人员要重质量轻数量
(7)要不断的进行软件工程的实践探究

1.4 课程教学目标

        需求分析:分析现有软件, 归纳初步需求;基础差的同学学习技术
        设计阶段:用快速发布来证明设计是有效的,  能适应变化的
        实现阶段:用各种软件工程的衡量手段来证明大家实现的能力。
        稳定阶段:证明测试能否覆盖代码的大部分。
        发布阶段: 如期发布 , 用户量 用户评价
        维护阶段:网上的观众或下一个年级的同学能很愿意接手你们的软件。
        最后大部分同学们能说:  自己做了一个有人用,有生命的软件 然后下个学期,新的一批学生进来提高这一过程 … 

2 工欲善其事必先利其器

2.1 Typing

        通过 Learn Touch Typing Free - TypingClub学习标准指法,并测试打字速度。虽然码农的优秀与否与打字速度毫无关系,但其却是每个码农必备的且不可忽视的,要求打字速度>50WPM为优秀,>30WPM为及格。

2.2 Visual Studio Code

2.2.1 简介

        Visual Studio Code(以下简称vscode)是一个轻量且强大的代码编辑器,支持Windows,OS X和Linux。内置JavaScript、TypeScript和Node.js支持,而且拥有丰富的插件生态系统,可通过安装插件来支持C++、C#、Python、PHP等其他语言。

2.2.2 vscode为什么能这么牛

        简介而聚焦的产品定位,贯穿始终
        进程隔离的插件模型
        UI渲染与业务逻辑隔离,一致的用户体验
        代码理解和调试——LSP和DAP两大协议
        集大成的Remote Development

2.3 Git

2.3.1 Git与版本控制

        版本控制系统有很多:独立文件方式,比如另存为;补丁方式,不如diff;中心版本控制系统,比如Concurrent Version System/cvs和Subversion/svn。分布式版本控制系统,比如Git,是目前世界上最先进的版本控制系统(没有之一)。

2.3.2 Git的基本操作逻辑

高级软件工程学习总结_第1张图片

        对于本地Repo,可能有多个branch,至少有一个叫master
        本地Repo,可能有多个branch,至少有一个叫master

2.3.3 Git的设计理念和时间线

高级软件工程学习总结_第2张图片

2.4 Vi/Vim

2.4.1 简介

        几乎所有的Unix-Like系统一般都会预装vi文本编辑器,其他的文本剪辑器不一定预装。POSIX标准中就有vi命令。
        vim具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确性,方便程序设计。
        vim是vi发展出来的一个文本剪辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。
        简单的来说,vi仅仅是文本编辑器,不过功能已经很齐全了。vim则是程序开发者的一项很好用的工具。连vim的官方网站(http://www.vim.org)也说vim是一个程序开发工具而不仅是文本编辑器。

2.4.2 vi/vim的三种模式

        命令模式(Command mode),用户刚刚启动vi/vim,便进入了命令模式。此状态下敲击键盘动作会被vim识别为命令,而非输入字符。比如我们此时按下i,并不会输入一个字符,i被当作了一个命令。命令模式只有一些最基本的命令,因此仍要依靠底线命令模式输入更多命令。
        输入模式(Insert mode),在命令模式下按下i就进入了输入模式,按ESC退出输入模式,切换到命令模式。
        底线命令模式(Last line mode),在命令模式下按下:(英文冒号)就进入了底线命令模式。底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。基本的命令有q(退出程序)、w(保存文件)等。按ESC键可随时退出底线命令模式。

高级软件工程学习总结_第3张图片

2.5 正则表达式

2.5.1 简介

        正则表达式是对字符串操作的一种逻辑公式。
        正则表达式的应用范围非常之广泛,最初是由Unix普及开来的,后来在广泛运用于Scala、PHP、C#、Objective-c、Perl、Swift、VBScript、JavaScript、Ruby以及Python等等。
        学习正则表达式,实际上是在学习一种非常灵活的逻辑思维,通过简单快速的方法达到对于字符串的控制。
        正则表达式是程序员手中一把威力无比强大的武器!

2.5.2 为什么使用正则表达式

        测试字符串内的模式。例如,可以测试输入字符串,以查看字符串内是否出现电话号码模·式或信用卡号码模式。这称为数据验证。
        替换文本。可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
        基于模式匹配从字符串中提取子字符串。可以查找文档内或输入域内特定的文本。
        例如,您可能需要搜索整个网站,删除过时的材料,以及替换某些 HTML 格式标记。在这种情况下,可以使用正则表达式来确定在每个文件中是否出现该材料或该 HTML 格式标记。此过程将受影响的文件列表缩小到包含需要删除或更改的材料的那些文件。然后可以使用正则表达式来删除过时的材料。最后,可以使用正则表达式来搜索和替换标记。

3 工程化编程实战

3.1 代码规范和代码风格

3.1.1 代码风格原则

        代码风格的原则:简明、易读、无二义性
        一个项目代码的风格就如同一个人给人的印象,代码风格之所以那么重要,是因为它往往决定了代码是否规范、是否易于阅读。
        代码虽然最终是要给机器看的,但毕竟还是面向程序猿们的编程,程序猿们是要陪伴整个项目开发过程的。在编写代码的过程中,尤其是在协作开发的过程中,如果对方的代码杂乱无章,读起来都费劲,更别说还需要在此基础上进一步开发,这对程序员来说是个巨大的挑战。
        好的代码风格不仅易于代码的阅读和理解,还能在很大程度上减少一些不必要的语法错误,例如少了 "}" ,如果在编码的时候严格遵循了花括号的对齐规则,那此类错误将容易被避免

3.1.2 代码风格三重境界

        一是规范整洁。遵守常规语言规范,合理使用空格、空行、缩进、注释等;
        二是逻辑清晰。没有代码冗余、重复,让人清晰明了的命名规则。做到逻辑清晰不仅要求程序员的编程能力,更重要的是提高设计能力,选用合适的设计模式、软件架构风格可以有效改善代码的逻辑结构,会让代码简洁清晰;
        三是优雅。优雅的代码是设计的艺术,是编码的艺术,是编程的最高追求。
        一般来讲,我们对代码风格的基本原则要求是简明、易读、无二义性。

3.1.3 代码风格规范中介

        命名:合适的命名会大大增加代码的可读性;
        类名、函数名、变量名等的命名一定要与程序里的含义保持一致,以便于阅读理解;
        类型的成员变量通常用m_或者_来做前缀以示区别;
        一般变量名、对象名等使用LowerCamel风格,即第一个单词首字母小写,之后的单词        都首字母大写,第一个单词一般都表示变量类型,比如int型变量iCounter;
        类型、类、函数名等一般都用Pascal风格,即所有单词首字母大写;
        类型、类、变量一般用名词或者组合名词,如Member
        函数名一般使用动词或者动宾短语,如get/set,RenderPage 注释和版权信息:
        注释也要使用英文,不要使用中文或特殊字符,要保持源代码是ASCII字符格式文件;        
        不要解释程序是如何工作的,要解释程序做什么,为什么这么做,以及特别需要注意的地方;
        每个源文件头部应该有版权、作者、版本、描述等相关信息。

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

        通过控制结构简化代码
        通过数据结构简化代码
        一定要有错误处理
        性能优先策略背后隐藏的代价
        拒绝修修补补要不断重构代码
        极限编程中不同的两类参与者的合作
        结合编程中同类参与者的合作

3.2 模块化软件设计

3.2.1 模块化的基本原理

        模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独立地进行设计和开发。这个做法背后的基本原理是关注点的分离 (SoC, Separation of Concerns),是由软件工程领域的奠基性人物Edsger Wybe Dijkstra(1930~2002)在1974年提出,没错就是Dijkstra最短路径算法的作者。
        关注点的分离在软件工程领域是最重要的原则,我们习惯上称为模块化,翻译成我们中文的表述其实就是“分而治之”的方法。
        关注点的分离的思想背后的根源是由于人脑处理复杂问题时容易出错,把复杂问题分解成一个个简单问题,从而减少出错的情形。
        模块化软件设计的方法如果应用的比较好,最终每一个软件模块都将只有一个单一的功能目标,并相对独立于其他软件模块,使得每一个软件模块都容易理解容易开发。
        从而整个软件系统也更容易定位软件缺陷bug,因为每一个软件缺陷bug都局限在很少的一两个软件模块内。
        而且整个系统的变更和维护也更容易,因为一个软件模块内的变更只影响很少的几个软件模块。
        因此,软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度。

3.2.2 模块化代码的基本写法

        命令行菜单在开源社区中常见的写法
        将数据结构和它的操作与菜单业务处理进行分离处理,尽管还是在同一个源代码文件中,但是已经在逻辑上做了切分,可以认为有了初步的模块化。
        进行了模块化设计之后我们往往将设计的模块与实现的源代码文件有个映射对应关系,因此我们需要将数据结构和它的操作独立放到单独的源代码文件中,这时就需要设计合适的接口,以便于模块之间互相调用。
        KISS(Keep It Simple & Stupid)原则
        使用本地化外部接口来提高代码的适应能力
        不要和陌生人说话原则
        先写伪代码的代码结构更好一些

3.3 可重用软件设计

3.3.1 消费者重用和生产者重用

Consumer Reuse
        消费者重用是指软件开发者在项目中重用已有的一些软件模块代码,以加快项目工作进度。软件开发者在重用已有的软件模块代码时一般会重点考虑如下四个关键因素:
该软件模块是否能满足项目所要求的功能;
        采用该软件模块代码是否比从头构建一个需要更少的工作量,包括构建软件模块和集成软件模块等相关的工作;
        该软件模块是否有完善的文档说明;
        该软件模块是否有完整的测试及修订记录;
        如上四个关键因素需要按照顺序依次评估。
Producer Reuse
        我们清楚了消费者重用时考虑的因素,那么生产者在进行可重用软件设计时需要重点考虑的因素也就清楚了,但是除此之外还有一些事项在进行可重用软件设计时牢记在心,我们简要列举如下:
        通用的模块才有更多重用的机会;
        给软件模块设计通用的接口,并对接口进行清晰完善的定义描述;
        记录下发现的缺陷及修订缺陷的情况;
        使用清晰一致的命名规则;
        对用到的数据结构和算法要给出清晰的文档描述;
        与外部的参数传递及错误处理部分要单独存放易于修改;

3.3.2 接口的基本概念

         接口就是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定义一组API函数来约定软件模块之间的沟通方式。换句话说,接口具体定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。
        在面向过程的编程中,接口一般定义了数据结构及操作这些数据结构的函数;而在面向对象的编程中,接口是对象对外开放(public)的一组属性和方法的集合。函数或方法具体包括名称、参数和返回值等。

3.3.3 接口规格包含五个基本要素

        接口规格是软件系统的开发者正确使用一个软件模块需要知道的所有信息,那么这个软件模块的接口规格定义就必须清晰明确地说明正确使用本软件模块的信息。一般来说,接口规格包含五个基本要素:
        接口的目的;
        接口使用前所需要满足的条件,一般称为前置条件或假定条件;
        使用接口的双方遵守的协议规范;
        接口使用之后的效果,一般称为后置条件;
        接口所隐含的质量属性。

3.3.4 微服务的概念

        由一系列独立的微服务共同组成软件系统的一种架构模式;
        每个微服务单独部署,跑在自己的进程中,也就是说每个微服务可以有一个自己独立的运行环境和软件堆栈;
        每个微服务为独立的业务功能开发,一般每个微服务应分解到最小可变产品(MVP),达到功能内聚的理想状态。微服务一般通过RESTful API接口方式进行封装
        系统中的各微服务是分布式管理的,各微服务之间非常强调隔离性,互相之间无耦合或者极为松散的耦合,系统通过前端应用或API网关来聚合各微服务完成整体系统的业务功能。        
        微服务架构的基本概念可以简单概括为通过模块化的思想垂直划分业务功能,传统单体集中式架构和微服务架构如下图示意
高级软件工程学习总结_第4张图片

3.4 可重入函数与线程安全

3.4.1 线程的基本概念

        线程(thread)是操作系统能够进行运算调度的最小单位。它包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一般默认一个进程中只包含一个线程。
        操作系统中的线程概念也被延伸到CPU硬件上,多线程CPU就是在一个CPU上支持同时运行多个指令流,而多核CPU就是在一块芯片上集成了多个CPU核,比如4核8线程CPU芯片就是在集成了4个CPU核,每个CPU核上支持2个线程。
        有了多核多线程CPU,操作系统就可以让不同进程运行在不同的CPU核的不同线程上,从而大大减少进程调度进程切换的资源消耗。传统上操作系统工作在单核单线程CPU上是通过分时共享CPU来模拟出多个指令执行流,从而实现多进程和多线程的。

3.4.2 可重入函数定义

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

3.4.3 可重入函数的基本要求

        不为连续的调用持有静态数据;
        不返回指向静态数据的指针;
        所有数据都由函数的调用者提供;
        使用局部变量,或者通过制作全局数据的局部变量拷贝来保护全局数据;
        使用静态数据或全局变量时做周密的并行时序分析,通过临界区互斥避免临界区冲突;
        绝不调用任何不可重入函数。

3.4.4 什么是线程安全

        如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
        线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

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

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

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

4.1 需求及需求分析

4.1.1 什么是需求

        需求是期望行为的表述。这么说起来比较抽象,我们具体分析一下:
        期望是谁的期望?主要是用户的期望,当然也包括待开发软件其他利益相关者的期望;
        行为是谁的行为?自然是软件的行为,具体来说就是是待开发软件中的对象和实体的行为;
        表述是谁来表述?是软件开发者来表述,具体来说就是需求分析师来表述和定义需求。
        根据以上分析,我们可以进一步来得出:需求分析就是需求分析师对用户期望的软件行为进行表述,并进一步用对象或实体的状态、属性和行为来定义需求。

4.1.2 什么是需求分析

        需求就是对用户期望的软件行为的表述;
          获取需求就是需求分析师通过关注用户的期望和需要,从而获得用户期望的软件行为,然后对其进行表述的工作;
        需求分析是在获取需求的基础上进一步对软件涉及的对象或实体的状态、特征和行为进行准确描述或建模的工作。

4.2 从需求进行分析与建模

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

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

4.2.2 什么是用例

        用例(Use Case)的核心概念中首先它是一个业务过程(business process),经过逻辑整理抽象出来的一个业务过程,这是用例的实质。什么是业务过程?在待开发软件所处的业务领域内完成特定业务任务(business task)的一系列活动就是业务过程
接下来我们具体看看用例的几个基本要素:
        A use case is initiated by (or begins with) an actor. 一个用例应该由业务领域内的某个参与者(Actor)所触发。
        A use case must accomplish a business task (for the actor).用例必须能为特定的参与者完成一个特定的业务任务。
        A use case must end with an actor. 一个用例必须终止于某个特定参与者,也就是特定参与者明确地或者隐含地得到了业务任务完成的结果。

4.2.3 用例的三个抽象层级

        在准确理解用例概念的基础上,我们可以进一步将用例划分为三个抽象层级:
        抽象用例(Abstract use case)。只要用一个干什么、做什么或完成什么业务任务的动名词短语,就可以非常精简地指明一个用例
         高层用例(High level use case)。 需要给用例的范围划定一个边界,也就是用例在什么时候什么地方开始,以及在什么时候什么地方结束
        扩展用例(Expanded use case)。需要将参与者和待开发软件系统为了完成用例所规定的业务任务的交互过程一步一步详细地描述出来,一般我们使用一个两列的表格将参与者和待开发软件系统之间从用例开始到用例结束的所有交互步骤都列举出来。

4.3 从需求分析到软件设计

4.3.1 瀑布模型

        在继续进行从需求分析到软件设计的后续部分之前,我们有必要从整体上探讨一下我们所遵循的软件过程——敏捷统一过程。为了理解敏捷统一过程,我们先从瀑布模型(Waterfall Process)说起
        瀑布模型是最基本的过程模型,它把整个软件过程按顺序划分成了需求、设计、编码、测试和部署五个阶段。瀑布模型的根本特点是按顺序划分阶段,至于是像我们这样划分成五阶段,还是划分三个阶段或八个阶段,并不是关键。这一点需要读者注意,以免在阅读其他资料时产生困惑。

高级软件工程学习总结_第5张图片

4.3.2 统一过程

         统一过程(UP,Unified Process)的核心要义是用例驱动(Use case driven)、 以架构为中心(Architecture centric)、 增量且迭代(Incremental and Iterative)的过程。用例驱动就是我们前文中用例建模得到的用例作为驱动软件开发的目标;以架构为中心的架构是后续软件设计的结果,就是保持软件架构相对稳定,减小软件架构层面的重构造成的混乱;增量且迭代体现在下图中。  
高级软件工程学习总结_第6张图片

4.3.3 敏捷统一过程的计划阶段

        在项目正式动手开工之前,敏捷统一过程要求进行精心周密的构思完成计划阶段。计划阶段要做的工作有如下几点:
        首先明确项目的动机、业务上的实际需求,以及对项目动机和业务需求可供替代选择的多种可能性;
        然后充分调研获取需求并明确定义需求规格;
        在明确需求规格的基础上进行项目的可行性研究;
        如果项目可行,接下来接着进行用例建模并给出用例图;
        同时明确需求和用例之间的可跟踪矩阵;
        从而形成项目的概念模型草案;
        以及项目可能的日程安排、需要的资源以及大致的预算范围。

4.3.4 敏捷统一过程的四个关键步骤

        第一,确定需求;
        第二,通过用例的方式来满足这些需求;
        第三,分配这些用例到各增量阶段;
        第四,具体完成各增量阶段所计划的任务。
显然,第一到第三步主要是计划阶段的工作,第四步是接下来要进一步详述的增量阶段的工作。

4.3.4 敏捷统一过程的增量阶段

        在每一次增量阶段的迭代过程中,都要进行从需求分析到软件设计实现的过程,具体敏捷统一过程将增量阶段分为五个步骤:
        用例建模(Use case modeling);
        业务领域建模(Domain modeling);
        对象交互建模(Object Interaction modeling);
        形成设计类图(design class diagram);
        软件的编码实现和软件应用部署;

高级软件工程学习总结_第7张图片

4.4 总结

        这一部分我们以面向对象的分析和设计为思想方法的主线,提供了一种从需求分析到软件设计的基本建模方法,相信您完整地学习了这种从需求分析到软件设计的基本建模方法之后,您对面向对象的概念有了切身体会。
        以对象作为基本元素构建起来了一种主流的看待软件的范型,形成了从编程语言、UML、开发方法、设计模式、软件架构以及工程思想方法等一系列成果。
        面向对象方法本身也引入了不是软件所固有的一些复杂性,试图以坚实的科学模型基础来建构软件世界的努力逐渐重新走入经验主义的巢臼。比如设计模式就是典型的经验模型;再比如最新的一些语言特性融合了面向过程编程的优点以及函数式编程的优点;甚至软件开发方法也走向了颇具工匠精神的思路,不断迭代和重构来优化软件设计以及代码结构。这些都让我们重新审视将抽象的对象作为认识软件的基础是否根基牢固。

5 软件科学基础概论

5.1 软件是什么

5.1.1 软件的基本构成元素

        对象(Object)
        函数和变量/常量
        指令和操作数
        0和1是什么

5.1.2 软件的基本结构

        顺序结构
        分支结构
        循环结构
        函数调用框架
        继承和对象组合

5.1.3 软件中的一些特殊机制        

        回调函数
        多态
        闭包
        异步调用
        匿名函数

5.2 软件设计模式初步

5.2.1 设计模式设计的基本概念

        在面向对象分析中我们涉及到了类、对象、属性以及类与类之间的关系,在此基础上我们进一步理解面向对象设计和实现所涉及的基本术语之间的关系
高级软件工程学习总结_第8张图片

5.2.2 什么是设计模式

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

5.2.3 设计模式的优点

        正确使用设计模式具有以下优点。
        可以提高程序员的思维能力、编程能力和设计能力。
        使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
        使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

5.2.4 设计模式的分类

        根据设计模式可以完成的任务类型来划分的话,可以分为创建型模式、结构型模式和行为型模式 3 种类型的设计模式
        创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。比如单例模式、原型模式、建造者模式等属于创建型模式。
        结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,比如代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式等属于结构型模式。结构型模式分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,所以对象结构型模式比类结构型模式具有更大的灵活性。
        行为型模式:用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。比如模板方法模式、策略模式、命令模式、职责链模式、观察者模式等属于行为型模式。行为型模式分为类行为模式和对象行为模式,前者采用继承在类间分配行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,所以对象行为模式比类行为模式具有更大的灵活性。

5.2.5 常用的设计模式

        单例(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)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
        模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。模版方法是继承和重载机制的应用,属于类模式。
        职责链(Chain of Responsibility)模式:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。通过这种方式将多个请求处理者串联为一个链表,去除请求发送者与它们之间的耦合。
        中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者,采用“中介者模式”大大降低了对象之间的耦合性,提高系统的灵活性

5.2.6 设计模式背后的设计原则

        开闭原则
        Liskov替换原则
        依赖倒置原则
        单一职责原则
        迪米特法则
        合成复用原则

5.3 常见的软件架构

5.3.1 三层架构

        层次化架构是利用面向接口编程的原则将层次化的结构型设计模式作为软件的主体结构。比如三层架构是层次化架构中比较典型的代表,如下图所示我们以层次化架构为例来介绍。

高级软件工程学习总结_第9张图片

5.3.2 MVC架构

        MVC即为Model-View-Controller(模型-视图-控制器),MVC是一种设计模式,以MVC设计模式为主体结构实现的基础代码框架一般称为MVC框架,如果MVC设计模式决定了整个软件的架构,不管是直接实现了MVC模式还是以某一种MVC框架为基础,只要软件的整体结构主要表现为MVC模式,我们就称为该软件的架构为MVC架构。
        MVC中M、V和C所代表的含义如下:
        Model(模型)代表一个存取数据的对象及其数据模型。
        View(视图)代表模型包含的数据的表达方式,一般表达为可视化的界面接口。
        Controller(控制器)作用于模型和视图上,控制数据流向模型对象,并在数据变化时更新视图。控制器可以使视图与模型分离开解耦合。

5.3.3 MVVM架构

        MVVM即 Model-View-ViewModel,最早由微软提出来,借鉴了桌面应用程序的MVC模式的思想,是一种针对WPF、Silverlight、Windows Phone的设计模式,目前广泛应用于复杂的Javacript前端项目中
        随着前端页面越来越复杂,用户对于交互性要求也越来越高,jQuery是远远不够的,于是MVVM被引入到Javascript前端开发中。

5.4 软件架构风格与策略

5.4.1 管道-过滤器

        管道-过滤器风格的软件架构是面向数据流的软件体系结构,最典型的应用是编译系统。 一个普通的编译系统包括词法分析器、语法分析器、语义分析与中间代码生成器、目标代码生成器等一系列对源代码进行处理的过程。就像如图:管道-过滤器风格示意图,对源代码处理的过滤器通过管道连接起来,实现了端到端的从源代码到编译目标的完整系统。
高级软件工程学习总结_第10张图片

    

5.4.2 客户-服务

        Client/Server(C/ S)和Browser / Server(B / S)是我们常用的对软件的网络结构特点的表述方式,但它们背后蕴含着一种普遍存在的软件架构风格,即客户-服务模式的架构风格
        客户-服务模式的架构风格是指客户代码通过请求和应答的方式访问或者调用服务代码。这里的请求和应答可以是函数调用和返回值,也可以是TCP Socket中的send和recv,还可以是HTTP协议中的GET请求和响应。
        在客户-服务模式中,客户是主动的,服务是被动的。客户知道它向哪个服务发出请求,而服务却不知道它正在为哪个客户提供服务,甚至不知道正在为多少客户提供服务。
        客户-服务模式的架构风格具有典型的模块化特征,降低了系统中客户和服务构件之间耦合度,提高了服务构件的可重用性。

5.4.3 P2P

        P2P(peer-to-peer)架构是客户-服务模式的一种特殊情形,P2P架构中每一个构件既是客户端又是服务端,即每一个构件都有一种接口,该接口不仅定义了构件提供的服务,同时也指定了向同类构件发送的服务请求。这样众多构件一起形成了一种对等的网络结构,如图:P2P网络结构示意图。
        P2P架构典型的应用有文件共享网络、比特币网络等。

高级软件工程学习总结_第11张图片

5.4.4 发布-订阅

        在发布-订阅架构中,有两类构件:发布者和订阅者。如果订阅者订阅了某一事件,则该事件一旦发生,发布者就会发布通知给该订阅者。观察者模式体现了发布-订阅架构的基本结构。
        在实际应用中往往会需要不同的订阅组,以及不同的发布者。由于订阅者数量庞大往往在消息推送时采用消息队列的方式延时推送。如图:包含消息队列的发布-订阅架构示意图。
高级软件工程学习总结_第12张图片

5.4.5 CRUD

        CRUD 是创建(Create)、 读取(Read)、更新(Update)和删除(Delete)四种数据库持久化信息的基本操作的助记符,表示对于存储的信息可以进行这四种持久化操作。CRUD也代表了一种围绕中心化管理系统关键数据的软件架构风格。一般常见的各类信息系统,比如ERP、CRM、医院信息化平台等都可以用CRUD架构来概括。
        如图:医院信息基础平台示意图中心位置为运营数据、临床数据、基础数据等,各种应用系统围绕了中心数据做CRUD持久化操作,即呈现出典型的CRUD架构风格。
高级软件工程学习总结_第13张图片

5.4.6 层次化

        较为复杂的系统中的软件单元,仅仅从平面展开的角度进行模块化分解是不够的,还需要从垂直纵深的角度将软件单元按层次化组织,每一层为它的上一层提供服务,同时又作为下一层的客户。
        通信网络中的OSI(Open System Interconnection)参考模型是典型的层次化架构风格,如图:OSI参考模型示意图。在OSI参考模型中每一层都将相邻底层提供的通信服务抽象化,隐藏它的实现细节,同时为相邻的上一层提供服务。
高级软件工程学习总结_第14张图片

6 软件危机和软件过程

6.1 软件危机

6.1.1 产生背景

        1960年代以前,计算机刚刚投入实际使用,软件往往只是为了一个特定的目标而在指定的计算机硬件环境上设计和编制的程序,采用密切依赖于计算机硬件环境的机器代码或汇编语言来编写,软件的规模比较小,也谈不上什么系统化的软件开发方法,设计软件往往等同于编写程序,基本上是个人设计、个人使用、个人操作、自给自足的软件生产方式。
        1960年代中期,大容量、高速度计算机的出现,使计算机的应用范围迅速扩大,因此软件开发需求和软件的规模都急剧增长。高级语言开始出现、操作系统迅猛发展、大量数据处理导致数据库管理系统的诞生等,导致软件开发的基础环境发生了重大变化,从与计算机硬件基础环境直接打交道,变为在一个更高抽象层级之上编写软件。软件系统的规模越来越大,复杂程度越来越高,软件可靠性问题也越来越突出。原来的个人设计、个人使用的方式不再能满足要求,迫切需要改变软件生产方式,提高软件开发效率,软件危机开始爆发 。
        在1968、1969年连续召开两次北大西洋公约组织(NATO)国际学术会议,也就是著名的NATO会议,会上定义了软件危机(Software crisis),同时提出软件工程的概念。
        由此开始,人们一直寻找解决软件危机的方法,诞生了包括结构化程序设计、专家系统、面向对象的分析和设计方法等,当人们意识到大型软件中打造抽象软件的复杂概念结构的根本困难是缺乏有效的管理,而非技术本身时,于是针对软件开发活动及软件本身的管理,对软件生命周期建模,形成了众多有效管理软件开发过程的理论和方法,经过研究和实践逐渐演化成如今的CMM/CMMI、敏捷方法、DevOps等软件过程模型。

6.1.2 软件危机的展望

        如今来看,Brooks的预测是千真万确的,软件危机的困境依然没有从根本上找到解决方法。从复杂系统研究的结果看,人类过去的科学理性范式没有能力处理应对多个独立变量相互作用的复杂系统,而大型软件系统就是高维度复杂系统,人类的理性力量无法有效跟踪分析高维度复杂系统的概念完整性和一致性。换句话说,基于规则的编程模型,找不到解决软件危机的杀手锏(银弹)。
        基于联结的编程模型,比如机器学习和深度神经网络,在应对高维度复杂系统上提供了可能性,而且已经在人脸识别、语音识别和自然语言处理等领域取得突破。基于联结的编程模型能不能在应对软件危机的困境中也取得突破,值得期待。

6.2 软件过程

6.2.1 软件的生命周期概述

        一般来讲,我们将软件的生命周期划分为:分析、设计、实现、交付和维护这么五个阶段。
        分析阶段的任务是需求分析和定义,比如在敏捷统一过程中用例建模和业务领域建模就属于分析阶段。分析阶段一般会在深入理解业务的情况下,形成业务概念原型,业务概念原型是业务功能和业务数据模型的有机统一体,比如用例的集合和业务数据模型,每一个用例在逻辑上都可以通过操作业务数据模型完成关键的业务过程。
        设计阶段分为软件架构设计和软件详细设计,前者一般和分析阶段联系紧密,一般合称为“分析与设计”;后者一般和实现阶段联系紧密,一般合称为“设计与实现”。
        实现阶段分为编码和测试,其中测试又涉及到单元测试、集成测试、系统测试等。
        交付阶段主要是部署、交付测试和用户培训等。
        维护阶段一般是软件生命周期中持续时间最长的一个阶段,而且在维护阶段很可能会形成单独的项目,从而经历分析、设计、实现、交付几个阶段,最终又合并进维护阶段。

6.2.2 软件的故障率曲线

        软件开发过程从形成软件概念原型到实现、交付使用和维护的整个过程,软件的故障率有着一定的规律,如下图所示,软件在维护阶段往往会出现故障率上升的情况
高级软件工程学习总结_第15张图片

6.2.3 瀑布模型

        瀑布模型(Waterfall Model)是第一个软件过程开发模型,对于能够完全透彻理解的需求且几乎不会发生需求变更的项目瀑布模型是适用的。但是由于瀑布模型能够将软件开发过程按顺序组织过程活动,非常简单和易于理解,因此瀑布模型被广泛应用于解释项目进展情况及所处的阶段。瀑布模型中的主要阶段通过里程碑(milestones)和交付产出来划分的。
高级软件工程学习总结_第16张图片
        瀑布模型是一个过程活动的顺序结构,没有任何迭代,而大多数软件开发过程都会包含大量迭代过程。瀑布模型不能为处理开发过程中的变更提供任何指导意义,因为瀑布模型假定需求不会发生任何变化。由此看来,瀑布模型将软件开发过程看作类似于工业生产制造的过程,而不是一个创造性的开发过程。工业生产制造的过程就是没有任何迭代活动,直接产出最终产品。瀑布模型视角下的软件开发过程也一样,只有等待整个软件开发过程完成后,才能看到最终软件产品。

6.2.4 V模型

        V模型也是在瀑布模型基础上发展出来的,我们发现单元测试、集成测试和系统测试是为了在不同层面验证设计,而交付测试则是确认需求是否得到满足。也就是瀑布模型中前后两端的过程活动具有内在的紧密联系,如果将模块化设计的思想拿到软件开发过程活动的组织中来,可以发现通过将瀑布模型前后两端的过程活动结合起来,可以提高过程活动的内聚度,从而改善软件开发效率。这就是V模型。
高级软件工程学习总结_第17张图片

6.3 CMM/CMMI

6.3.1 简介

        为了应对软件危机保证软件产品的质量,80年代中期,美国联邦政府提出对软件承包商的软件开发能力进行评估的要求。因此,美国卡内基-梅隆大学软件工程研究所 (CMU/SEI) 于1987年研究发布了软件过程成熟度框架,并提供了软件过程评估和软件能力评价两种评估方法。4年之后1991年,CMU/SEI将软件过程成熟度框架发展成为软件能力成熟度模型CMM(Capability Maturity Model For Software,准确的简称为SW-CMM ), 并发布了最早的SW -CMM 1.0版。经过两年的试用,1993年CMU/ SEI正式发布了SW -CMM 1.1版。
        继SW-CMM首次发布后,CMU/SEI还开发了其他领域的成熟度模型,包括系统工程、采购、人力资源管理和集成产品开发等领域。虽然各个模型针对的专业领域不同,但彼此之间也有一定的重叠,毕竟它们同出一辙。当CMU/SEI开始开发新一代成熟度模型的时候,开始整合不同模型中的最佳实践,建立统一模型,从而覆盖不同领域,以便为企业提供整个组织的全面过程改进。2001年12月正式发布了能力成熟度集成模型CMMI(Capability Maturity Model Integration) 1.1版本。

6.3.2 CMM/CMMI的5个等级

高级软件工程学习总结_第18张图片

6.3.3 CMM/CMMI的作用

        CMM/CMMI是基于政府评估软件承包商的软件能力发展而来的,有两种通用的评估方法用以评估组织软件过程的成熟度:软件过程评估和软件能力评价。
        软件过程评估:用于确定一个组织当前的软件工程过程状态及组织所面临的软件过程的优先改善问题,为组织领导层提供报告以获得组织对软件过程改善的支持。软件过程评估集中关注组织自身的软件过程,在一种合作的、开放的环境中进行。评估的成功取决于管理者和专业人员对组织软件过程改善的支持。
        软件能力评价:用于识别合格的软件承包商或者监控软件承包商开发软件的过程状态。软件能力评价集中关注识别在预算和进度要求范围内完成开发出高质量的软件产品的软件合同及相关风险。评价在一种审核的环境中进行,重点在于揭示组织实际执行软件过程的文档化的审核记录。

6.4 总结

        为了应对软件危机,首先想到的是通过简化和抽象的方法“就事论事”地处理软件本身的问题,从而诞生了结构化程序设计、面向对象分析和设计、模块化方法、设计模式、软件架构等一系列技术。这些技术确实在一定程度上缓解了软件危机的表现,这些技术本质上都是通过对软件本身的抽象来有效管控软件的复杂性。但在大型复杂软件系统中,这些技术依然力有不逮。
        行有不得,反求诸己。难以为复杂软件建立完整且一致的抽象概念模型,这一本质问题显现出来后,逐渐认识到相对于软件本身的管理这一局部问题,项目管理上的全局问题是更为主要的矛盾,于是开始反思软件开发过程本身,因此将软件过程改进纳入到应对软件危机的视野中,从而提出了各种软件生命周期模型及软件过程改进方法,以PSP和TSP的基本方法为支撑的CMM/CMMI软件成熟度模型最具有代表性。

7 课程总结

        学习完这门课程,通过老师的多元化教课,不但让我从理论上掌握了软件工程,还有从不同的实例,让理论和实践得到了很好的结合。整一个学期下来,总的来说还是学到了很多东西的,有很多地方是能对我今后工作还有学习都有很大帮助的。其实在我看来,软件工程与其说是一门课程,不如说是一门思想,是一个如何去分析和处理问题的过程,应该说其范畴已经远远不止局限于该门课程,成为了一个综合的一个能解决问题的思想集合。
        整个教学过程老师的内容逻辑清晰明了,由浅入深循序渐进,从码农必备的技能、工具,到编程过程中需要注意的编码规范和代码风格,接着到需求分析和软件设计,再到设计模式和软件架构,最后到软件危机和软件过程,其中结合着项目实战以及测验闯关,提高了学习的趣味,也让整个课程更加生动。总的来说,学习完整个课程我受益匪浅!

参考资料

代码中的软件工程  软件工程: 《代码中的软件工程》一书的配套ppt和源代码

你可能感兴趣的:(软件工程,学习,需求分析)