大学计算机《软件构造》课程复习纲要代码大全部分.docx
《大学计算机《软件构造》课程复习纲要代码大全部分.docx》由会员分享,可在线阅读,更多相关《大学计算机《软件构造》课程复习纲要代码大全部分.docx(22页珍藏版)》请在冰豆网上搜索。
大学计算机《软件构造》课程复习纲要代码大全部分
《软件构造》课程复习纲要
1.软件构造的概念
1)测试和调试的区别
测试是为了发现错误调试是为了改正错误
2)软件开发包括哪些:
问题定义需求分析实现计划总体设计
详细设计(部分)编码实现系统集成单元测试(部分)
系统测试校正性的维护功能强化
3)软件构造活动主要是其中哪些部分?
(以上黑体部分)
4)软件构造的重要性
❑构造活动是开发软件的重要组成部分
❑构造活动在开发软件中处于枢纽地位
❑把主要精力集中于创建活动,可以极大地提高程序员的生产效率
❑创建活动的产品,源代码,往往是软件的唯一精确描述
❑创建活动是唯一一项必不可少的工作
5)隐喻(SoftwareMetaphors)
•常见的软件隐喻(简单了解)
●软件书写:
写代码(WritingCode)
●软件播种:
生成系统(GrowingaSystem)
●软件珍珠培植法:
系统积累(SystemAccretion)
●软件创建:
建造软件(buildingsoftware)
●实用软件技术:
智能工具箱(TheIntellectualToolbox)
●复合隐喻(CombingMetaphors)
2.软件构造的前期准备工作
1)软件构造的前期准备工作有哪些?
Ø前期准备工作并非一成不变
构建活动的准备工作要根据项目特点调整
具体细节随项目的不同会有很大变化
Ø构造开始前的准备工作有哪些
辨明软件项目的类型
明确问题定义(问题定义:
项目要解决的问题是什么)
明确需求规约(需求定义详细规定了软件系统应该做什么)
明确软件架构的各组成部分(软件设计的高层部分,用于支撑更加细节的设计的框架)
什么是软件构造的前期准备工作
Ø软件构造的前期准备工作是复查性的活动
明确前导性的工作结果有哪些
重新审视前导性工作是否完成
明确前导性工作完成的程度如何
Ø准备工作的中心目标是降低风险
糟糕的需求分析糟糕的项目计划
3.构造技术
1)模块化设计(简单了解相关概念)
模块化设计的目标是使每个子程序都成为一个“黑盒子”:
•你知道进入盒子和从盒子里出来的是什么,却不知道里边发生什么
•通过接口的定义明确其功能
“模块化”同时涉及到子程序设计和模块设计:
•单独的子程序无法实现“黑盒子”的目标,因而需要模块
•但通过对子程序的控制可以达到模块化的效果
✧通过对一组子程序定义统一对外的接口,也完全有可能达到高度模块化这一目标
2)模块和子程序的区别
•子程序是具有一定功能的,可以调用的函数或过程,它可以封装为模块
•模块是指数据及作用于数据的子程序的集合,或者
•模块也可能是指,可以提供一系列互相联系功能的子程序集合,而这些子程序之间不一定有公共的数据,它是设计的最基本单元
3)模块化:
内聚性与耦合性(重要)
模块独立程度的衡量标准:
耦合性(Coupling):
对一个软件结构内不同模块间互连程度的度量。
内聚性(Cohesion):
标志一个模块内各个处理元素彼此结合的紧密程度,理想的内聚模块只做一件事情。
模块的内聚性:
内聚性表示了在一个模块中,各种操作及数据之间互相联系的紧密程度
内聚性的强弱程度用“强度”表示
应尽量提高内聚程度
强调“强内聚”的目的:
每一个模块只需作好一项工作,而不必过分考虑其它任务
模块内聚性分类:
偶然内聚:
一组任务关系松散(低)
逻辑内聚:
一组任务在逻辑上同属一类,例如均为输出(低)
时间内聚:
一组任务必须在同一段时间内执行(低)
子程序的内聚性:
模块的强内聚性并不说明子程序内聚性强
子程序的内聚性影响到模块的实现质量
子程序内聚性分类:
可取的内聚性:
功能内聚性(最高):
功能内聚性是最强也是最好的一种内聚
当程序执行一项并且仅仅是一项工作时,属于功能内聚
顺序内聚性(较高):
子程序内包含需要按特定顺序进行的、逐步分享数据而又不形成一个完整功能的操作
通讯内聚性(中):
在一个子程序中,两个操作只是使用相同数据,而不存在其它任何联系时产生的
临时内聚性(低):
因为同时执行的原因才被放入同一个子程序里,这时产生临时内聚性
不可取的内聚性:
不可取的内聚性往往导致一些组织混乱而又难以调试和改进的代码
应尽量避免不可取的内聚性的出现
过程内聚性:
当子程序中的操作是按某一特定顺序进行的,就是过程内聚性
过程内聚性和顺序内聚性的区别:
❑顺序内聚性中的顺序操作使用的是相同数据
❑过程内聚性中的操作使用的并不是相同数据
逻辑内聚性:
当一个子程序中同时含有几个操作,而其中一个操作又被传进来的控制标志所选择时,就产生了逻辑内聚性
其内部操作仅仅是因为控制流,或者说“逻辑”的原因才联系到一起
偶然内聚性:
当同一个子程序中的操作之间无任何联系时,为偶然内聚性
偶然内聚性也叫作“无内聚性”
模块耦合性:
耦合性表示相互作用的模块之间的互连程度
应尽量降低模块之间的耦合性
强调“松耦合”的目的:
模块应被设计成可以提供一整套功能,以便程序的其它部分与它清楚地相互作用
耦合分类:
无任何连接:
两个模块中的每一个都能独立地工作而不需要另一个的存在(最低耦合)。
数据耦合:
两个模块彼此通过参数交换信息,且交换的仅仅是数据(低耦合)。
控制耦合:
两个模块之间传递的信息有控制成分(中耦合)。
公共环境耦合:
两个或多个模块通过公共环境相互作用
❑一个存数据,一个取数据(低耦合)
❑都存取数据(低--中之间)
内容耦合:
❑一个模块访问另一个模块的内部数据
❑两个模块有一部分程序代码重叠
常见的OO耦合:
简单数据参数耦合:
两个对象之间通过参数传递数据,并且为简单数据类型
简单对象耦合:
一个对象实例化另一个对象所形成的耦合关系
对象参数耦合:
两个对象之间通过对象参数传递数据
语义上的耦合
信息隐蔽
信息隐蔽有称为“封装”:
使外部的可见部分和内部的不可见部分相互隔离
信息封装是设计模块和子程序的一种重要方法
对于模块而言,封装的部分称为内部信息,公开的部分称为模块的公开信息或接口
4)结构化设计vs.面向对象设计(理解)
从模块的角度理解结构化设计和OO设计的区别:
面向对象设计中的模块与结构化设计中模块的含义是一致的
数据和功能的封装;满足信息隐蔽的要求;追求高内聚低耦合
结构化设计中模块和模块之间的关系,被紧紧局限于信息流(结构化设计是面向功能)
面向对象设计方法,充分挖掘了“关系”的表达方式,可以尽可能的将事物之间复杂的关系予以体现
面向对象中模块之间的关系:
OO中的模块由“对象”来体现
OO中模块之间的关系(主要):
包含:
“有一个……”关系;继承:
“是一个……”关系
5)抽象数据类型(ADT)
了解基本概念,能够构造出简单的ADT
⏹类的基础:
抽象数据类型
❑类是一组数据和子程序构成的集合,这些数据和子程序共同拥有一组内聚的、明确定义的职责。
❑ADT是指一组数据以及对这些数据所进行的操作的集合
⏹以上两段描述有何本质区别?
❑从数据的角度来看类和ADT没有区别
❑从OO的角度来看,类还涉及到面向对象中的概念
⏹抽象数据类型中的“数据”
❑ADT中“数据”的概念很随意
❑ADT中的“数据”可以指任意类(Class)所能描述的实体
6)ADT与类的区别
体会ADT和类在概念上有什么区别。
给你一个ADT,构造出相应的类(Java或C++)
ADT:
一些数据以及对这些数据进行操作的集合+面向对象(封装、继承、多态)=类
构造类时class{}
7)良好的类接口
参阅CC2-6.2节
给你不良好的类接口,能够指出其中的问题,并加以改进
(难度限于书中例子的难度)
8)包含和继承的选择(了解)
在不得已时通过private继承来实现“有一个”的关系
用public继承来实现“是一个”的关系
两种在效果上不同,如能用包含就不要用继承。
何时采用继承
⏹判断是否存在符合LSP原则:
派生类必须能够通过基类的接口被使用,且使用者无需了解两者之间的差异
⏹确定方法是否能够共享
❑如果多个派生类的方法内容没有共同的地方,就用接口作为抽象
❑如果多个派生类的方法含有共同的地方,就用抽象类作为抽象
4.防御式编程
1)防御式编程的主要思想
防御式编程是防御式设计(defensivedesign)的一种表现形式,它是为了保证对程序的不可预见的使用不会造成之后程序功能的破坏。
防御式编程的思想可以看成是为了尽量消除或降低墨菲法则(Murphy‘sLaw)带来的影响。
防御式编程技术主要使用在那些容易发生错误输入以及错误的使用会带来灾难性后果的程序片段上。
2)墨菲法则(了解)
墨菲法则是指“任何有可能出错的事终将出错”。
引申为“所有的程序都有缺陷”,或“若缺陷有很多个可能性,则它必然会朝往令情况最坏的方向发展”。
3)断言部分(重要)
基本概念:
断言是一种布尔表达式,用于在程序中表明该处所要满足的条件
断言是指在开发期间使用的、让程序在运行时进行自检的代码
使用方式:
一个断言通常含有两个参数一个描述假设为真时的情况的布尔表达式一个断言为假时需要显示的信息
常见的两种形式(以Java为例)assertExpression1
assertExpression1:
Expression2
a)建立自己的断言机制
能够使用Java、c++、c#等任意一种语言建立自己的断言机制
b)使用断言的指导原则/建议
用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况
避免把需要执行的代码放到断言中
用断言来注解并验证前条件和后条件
对于高健壮性的代码,应该先使用断言再处理错误
c)错误处理和断言
了解两者的主要区别
错误处理代码是用来检查不太可能经常发生的非正常情况,如检查有害输入数据
断言是用来检查永远不该发生的情况,用于检查代码中的bug
d)避免把需要执行的代码放到断言中
给出代码示例,能够指出此类问题
e)用断言来验证前置和后置条件
给出代码示例,能够指出其中断言属于哪种应用
前置条件是子程序或类的调用方代码在调用子程序或实例化对象之前要确保为真的属性;后置条件是子程序或类在执行结束后要确保为真的属性
4)错误处理技术(了解)
用来处理那些预料中可能要发生的错误情况
返回中立值(如数值返回0)
换用下一个正确的数据
返回与前次相同的数据
换用最接近的合法值
把警告信息记录到日志文件中
用语言内建的异常机制抛出一个异常
5)异常处理机制(重要)
a)异常的基本结构
子程序使用throw抛出一个异常对象,再被调用到链上层其他子程序的try-catch语句捕获
b)异常处理机制所涉及的要素
异常对象、对异常对象的抛出、对所抛出异常的捕获
c)finally一般被用来进行清理工作
在异常处理时通过finally块来执行任何清除操作
最常见的就是关闭流、关闭连接、释放或销毁资源等
d)改进使用不当的异常情况
能够识别出代码中使用不当的异常(如,pptP40,41)
在恰当的抽象层次抛出异常GetTaxld()代码应抛回一个与其所在类的接口相一致的异常
隔离可以划分错误处理和断言
理解通过隔离代码将错误处理和断言两种技术合理应用到自己的系统中。
数据(可能含有污染数据)、在输入数据时将其转化为恰当类型。
Defensive
隔离区(隔离程序)
干净数据,即可让大部分代码就无须再担负检查错误数据的职责
6)产品版和开发版
了解两者的区别
不要自动地把产品版的限制强加于开发版之上
产品级的软件要求能够快速地运行,而开发中的软件则允许运行缓慢
产品级的软件要节约使用资源,而开发中的软件在使用资源时可以比较奢侈
产品级的软件不应向用户暴露可能引起危险的操作,而开发中的软件则可以提供一些额外的、没有安全网的操作
7)进攻式编程
了解什么是进攻式编程
进攻式编程是主动暴露可能出现错误的态度
在开发阶段让它显现出来,而在产品代码运行时让它能够自我恢复
常用的进攻式方法:
确保断言语句使程序终止运行
完全填充分配到的所有内存
完全填充己分配到的所有文件或流
确保每一个case语句中的default分支或else分支都能产生严重错误(如终止程序)
在删除一个对象之前把它填满垃圾数据
5语句编写的技巧
1)条件语句(重要)
a)优先考虑正常代码路径
能够根据此原则对代码进行优化调整(难度限于CC2和PPT)
把正常情况的处理放在if后面而不要放在else后面
b)利用布尔函数调用简化复杂的检测
能够根据此原则对代码进行优化调整(难度限于CC2和PPT)
复杂的判断情况会影响代码的可读性;将复杂的判断从代码中隔离开;通过布尔函数封装判断过程
2)循环语句(重要)(难度限于CC2和PPT)
a)循环的种类
了解常见循环语句分类各自的特点
计数循环(countedloop):
执行的总次数是一定的
连续求值的循环(continuouslyevaluatedloop):
预先并不知道将要执行多少次,它会在每次送代时检查是否应该结束
无限循环(endlessloop):
一旦启动就会一直执行下去
迭代器循环(iteratorloop):
对容器类里面的每个元素执行一次操作
b)带退出的循环和半循环
能够使用带退出循环对带半循环的代码进行改写
半循环部分的代码在修改时要保持一致带退出循环对带半循环的代码改写
c)while循环和for循环
能够对while循环和for循环代码片段进行相互改写
推荐用while循环来改写上例
d)避免依赖于循环下标最终取值
能够对依赖循环下标最终取值的代码进行改进
e)用防卫子句(guardclause)(早返回或早退出)来简化复杂的错误处理
能够用防卫子句对代码进行改进
3)无条件跳转语句:
GOTO
了解goto机制的背景和应用原则
Goto是一种无条件跳转语句。
Dijistra最早提出批评:
“Goto是有害的”。
一个程序的易读性和易理解性同其中所包含的无条件转移控制的个数成反比例;转移语句的个数越多,程序就越难读、难懂。
含有goto的代码很难安排好格式
使用goto也会破坏编译器的优化特性
使用goto会使运行速度更慢,而且代码也更大
支持GOTO的观点:
如果使用位置恰当,goto可以减少重复的代码
goto在分配资源、使用资源后再释放资源的子程序里非常有用
在某些情况下,使用goto会让代码的运行速度更快,体积更小
大部分论断都反对随意使用goto
4)表驱动法(重要)(难度限于CC2和PPT)
掌握直接访问、索引访问和阶梯访问的概念,能够:
a)判断代码中使用的表驱动技术属于哪一种
b)理解代码中“表”的定义和使用
其中重点掌握直接访问表、索引访问表
表驱动是一种编程模式,适用于复杂的逻辑,可以将复杂逻辑从代码中独立出来,以便于单独维护。
表驱动法:
消息阅读子程序由一个循环组成,该循环负责读入每一个消息头,对其ID解码,在Message数组中查询其消息描述,然后每次都调用同一个子程序来解释该消息
只需要用一张表来描述每种消息的格式,而不用再把它们硬编码进程序逻辑里
定义
使用
直接访问:
指通过索引值可以直接从表中找到对应的条目。
索引访问:
是一种间接访问技术,就是先用一个基本类型的数据从一张索引表中查出一个键值,然后再用这一键值查出你感兴趣的主数据。
索引访问技术优点:
如果主查询表中的每一条记录都很大,那么索引数组就可以节省很多空间;即使你用了索引以后没有节省内存空间,操作位于索引中的记录有时也要比操作位于主表中的记录更方便更廉价;便写道表里面的数据比嵌入式代码中的数据更容易维护。
阶梯访问:
更节省空间,通过确定每项命中的阶梯层次确定其归类。
Eg.把每一区间的上限写入一张表里,然后写一个循环,按照各区间的上限来查分数。
当分数第一次超过某个区间的上限时,你就知道相应的等级了。
5)短路求值
了解短路求值的概念
短路求值也叫做最小求值或者惰性求值,描述了一些编程语言中对布尔表达式的求值策略。
6代码改善
1)软件质量的特性
a)了解外在特性和内在特性
外在特性包括:
Ø正确性,指系统规范、设计和实现方面的错误的稀少程度
Ø可用性,指用户学习和使用一个系统的容易程度
Ø效率,指软件是否尽可能少地占用系统资源,包括内存和执行时间
Ø可靠性,指在指定的必需条件下,一个系统完成所需要功能的能力,即应该有很长的平均无故障时间
Ø适应性,指为特定的应用或者环境设计的系统,在不做
修改的情况下,能够在其他应用或者环境中使用的范围
Ø完整性,指系统阻止对程序或者数据进行未经验证或者不正确访问的能力
–例如:
保证那些保存着并行数据的表格能够正确地并行修改,确保日期宇段一定含有效的日期,等等
Ø精确性,指对于一个已经开发出的系统,输出结果的误差程度,尤其在输出的是数量值的时候
–精确性和正确性的不同在于,前者是用来判断系统完成工作的优劣程度,而后者则是判断系统是否被正确开发出来
Ø健壮性,这指的是系统在接收无效输入或者处于压力环境时继续正常运行的能力
内在特性包括:
Ø可维护性,指是否能够很容易对系统进行修改,改变或者增加功能,提高性能,以及修正缺陷
Ø灵活性,指假如一个系统是为特定用途或者环境而设计的,那么当该系统被用于其他目的或者环境的时候,需要对系统做修改的程度
Ø可移植性,指为了在原来设计的特定环境之外运行,对系统所进行修改的难易程度
Ø可重用性,指系统的某些部分可被应用到其他系统中的程度,以及此项工作的难易程度
Ø可读性,指阅读并理解系统代码的难易程度,尤其是在细节语句的层次上
Ø可测试性,指的是你可以进行何种程度的单元测试或者系统测试,以及在何种程度上验证系统是否符合需求
Ø可理解性,指在系统组织和细节语句的层次上理解整个系统的难易程度
2)常见的测试
了解单元测试、组件测试、集成测试、回归测试以及系统测试的概念
单元测试(Unittesting):
将一个程序员或者一个开发团队所编写的,一个完整的类、子程序或者小程序,从完整的系统中隔离出来进行测试
组件测试(Componenttesting):
将一个类、包、小程序或者其他程序元素,从一个更加完整的系统中隔离出来进行测试
这些被测代码涉及到多个程序员或者多个团队
集成测试(Integrationtesting):
是对两个或更多的类、包、组件或者子系统进行的联合测试,这些组件由多个程序员或者开发团队所创建
回归测试(Regressiontesting):
是指重复执行以前的测试用例,以便在原先通过了相同测试集合的软件中查找缺陷
系统测试(Systemtesting):
在最终的配置下(包括同其他软硬件系统的集成)运行整个软件
3)测试的特殊性
理解所谓测试的特殊性
测试的特殊性:
Ø测试的目标与其他开发活动背道而驰,测试的目标是找出错误
Ø测试永远不可能彻底证明程序中没有错误
Ø测试本身并不能改善软件的质量
Ø测试时要求你假设会在代码里面找到错误
4)不完整的测试
理解为什么不可能穷尽测试以及测试的基本出发点
穷尽测试:
对程序的每种可能的情况都进行测试
对程序的每一种可能的输入值,以及它们之间的所有可以想象的组合进行测试
测试的基本出发点:
由于进行完全测试实际上是不可能的,因此测试的窍门就在于选择那些最有可能找到错误的测试用例
当规划测试的时候,要去除那些不会告诉你任何新情况的测试用例
使用不同的方法来有效地覆盖程序基本情况(等价类划分,临界值)
5)结构化的基础测试(重要)
掌握基本概念以及基础测试用例最少数量的简单计算方法,能够根据代码设计相应的测试用例
基本思想:
需要去测试程序中的每一条语句至少一次
要确保已经覆盖了所有的基础情况
以最小数量的测试用例覆盖所有路径
基本方法:
最简单的方法就是算一算有多少条通过程序的路径,然后据此开发出能通过程序里每条路径的最少数量的测试用例
基础测试用例最少数量的简单计算方法:
1.对通过子程序的直路,开始的时候记1
2.遇到下面的每个关键字或者其等价物时加1(圈复杂度)
–if、while、repeat、for、and以及or。
3.遇到每一个case语句就加1,如果case语句没有缺省情况,则再加1
(下面代码的测试用例)