软件构造.docx
《软件构造.docx》由会员分享,可在线阅读,更多相关《软件构造.docx(44页珍藏版)》请在冰豆网上搜索。
软件构造
《软件构造》课程复习纲要
参考教材《代码大全(第二版)》:
以下简称CC2
复习所涉及的内容以PPT中出现的为准,具体细节参阅CC2中的解释。
1.软件构造的概念
2.
1)测试和调试的区别
2)
•测试是为了发现错误
•
•调试是为了改成错误
•
3)软件开发包括哪些:
4)
•问题定义
•
•需求分析
•
•实现计划
•
•总体设计
•
•详细设计
•
•编码实现
•
•系统集成
•
•单元测试
•
•系统测试
•
•校正性的维护
•
•功能强化
•
5)软件构造活动主要是其中哪些部分?
6)
1.验证基础工作已经完成,可以进行创建工作;
2.
3.设计和编写子程序与模块
4.
5.创立数据类型并命名变量
6.
7.选择控制结构并组织语句块
8.
9.找出并修正错误
10.
11.评审其它小组的细节设计和代码,同时接受其它小组评审
12.
13.通过仔细地格式化和征集意见改进编码
14.
15.对分别完成的软件单元进行综合
16.
17.调整编码使其更小、更快
18.
编码和调试,部分的详细设计和单元测试
7)软件构造的重要性
8)
⏹构造活动是开发软件的重要组成部分
⏹
⏹构造活动在开发软件中处于枢纽地位
⏹
⏹把主要精力集中于创建活动,可以极大地提高程序员的生产效率
⏹
⏹创建活动的产品,源代码,往往是软件的唯一精确描述
⏹
⏹创建活动是唯一一项必不可少的工作
⏹
9)隐喻(SoftwareMetaphors)
10)
•常见的软件隐喻(简单了解)
•
●软件书写:
写代码(WritingCode)
●
●软件播种:
生成系统(GrowingaSystem)
●
●软件珍珠培植法:
系统积累(SystemAccretion)
●
●软件创建:
建造软件(buildingsoftware)
●
●实用软件技术:
智能工具箱(TheIntellectualToolbox)
●
●复合隐喻(CombingMetaphors)
●
隐喻可以帮助开发人员借助类比的力量加深对编程的理解
事实上,借助隐喻加深理解是各个研究领域都常用的方法
3.软件构造的前期准备工作
4.
1)软件构造的前期准备工作有哪些
2)
前期准备工作并非一成不变
⏹构建活动的准备工作要根据项目特点调整
⏹
⏹具体细节随项目的不同会有很大变化
⏹
构造开始前的准备工作有哪些
⏹辨明软件项目的类型
⏹
⏹明确问题定义
⏹
⏹明确需求规约
⏹
明确软件架构的个组成部分
什么是软件构造的前期准备工作
⏹软件构造的前期准备工作是复查性的活动
⏹
❑明确前导性的工作结果有哪些
❑
❑重新审视前导性工作是否完成
❑
❑明确前导性工作完成的程度如何
❑
⏹准备工作的中心目标是降低风险
⏹
❑糟糕的需求分析
❑
❑糟糕的项目计划
❑
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
根据CC2Page59核对表上的内容复习(只涉及ppt中有的内容)
1)问题定义先决条件
2)
3)需求定义先决条件
4)
5)软件架构先决条件
6)
5.构造技术
6.
1)模块化设计(简单了解相关概念)………………………………
2)
这一部分的复习参阅CC2书上的第5章
3)模块和子程序的区别
4)
❑子程序是具有一定功能的,可以调用的函数或过程
❑
❑模块是指数据及作用于数据的子程序的集合,或者
❑
❑模块也可能是指,可以提供一系列互相联系功能的子程序集合,而这些子程序之间不一定有公共的数据
❑
5)模块化:
内聚性与耦合性(重要)
6)
pptpage55-page73
可以参阅CC2-7章子程序内聚性的相关内容
❑耦合性(Coupling):
对一个软件结构内不同模块间互连程度的度量。
❑
❑内聚性(Cohesion):
标志一个模块内各个处理元素彼此结合的紧密程度,理想的内聚模块只做一件事情。
❑
模块化
⏹模块化设计的目标是使每个子程序都成为一个“黑盒子”
⏹
❑你知道进入盒子和从盒子里出来的是什么,却不知道里边发生什么
❑
❑通过接口的定义明确其功能
❑
⏹“模块化”同时涉及到子程序设计和模块设计
⏹
❑单独的子程序无法实现“黑盒子”的目标,因而需要模块
❑
❑但通过对子程序的控制可以达到模块化的效果
❑
通过对一组子程序定义统一对外的接口,也完全有可能达到高度模块化这一目标
模块化:
内聚性与耦合性
⏹模块独立程度的衡量标准
⏹
❑耦合性(Coupling):
对一个软件结构内不同模块间互连程度的度量。
❑
❑内聚性(Cohesion):
标志一个模块内各个处理元素彼此结合的紧密程度,理想的内聚模块只做一件事情。
❑
模块内聚性
⏹模块的内聚性
⏹
❑内聚性表示了在一个模块中,各种操作及数据之间互相联系的紧密程度
❑
❑内聚性的强弱程度用“强度”表示
❑
❑应尽量提高内聚程度
❑
⏹强调“强内聚”的目的
⏹
❑每一个模块只需作好一项工作,而不必过分考虑其它任务
❑
模块内聚性分类
⏹偶然内聚:
一组任务关系松散(低)
⏹
⏹逻辑内聚:
一组任务在逻辑上同属一类,例如均为输出(低)
⏹
⏹时间内聚:
一组任务必须在同一段时间内执行(低)
⏹
子程序的内聚性
⏹模块的强内聚性并不说明子程序内聚性强
⏹
⏹子程序的内聚性影响到模块的实现质量
⏹
⏹子程序内聚性分类
⏹
❑可取的内聚性
❑
不可取的内聚性
可取的内聚性
⏹功能内聚性(最高)
⏹
⏹顺序内聚性(较高)
⏹
⏹通讯内聚性(中)
⏹
⏹临时内聚性(低)
⏹
功能内聚性:
⏹功能内聚性是最强也是最好的一种内聚
⏹
当程序执行一项并且仅仅是一项工作时,属于功能内聚
特征:
子程序的名称与其内容相符
功能性内聚
Sin():
计算三角函数sin的值
GetCustomerName():
获得客户姓名
EraseFile():
从硬盘删除文件
顺序内聚性
子程序内包含需要按特定顺序进行的、逐步分享数据而又不形成一个完整功能的操作
通讯内聚性
⏹在一个子程序中,两个操作只是使用相同数据,而不存在其它任何联系时产生的
⏹
临时内聚性
⏹因为同时执行的原因才被放入同一个子程序里,这时产生临时内聚性
⏹
不可取的内聚性
⏹不可取的内聚性往往导致一些组织混乱而又难以调试和改进的代码
⏹
⏹应尽量避免不可取的内聚性的出现
⏹
❑过程内聚性
❑
❑逻辑内聚性
❑
偶然内聚性
过程内聚性
⏹当子程序中的操作是按某一特定顺序进行的,就是过程内聚性
⏹
⏹过程内聚性和顺序内聚性的区别
⏹
❑顺序内聚性中的顺序操作使用的是相同数据
❑
❑过程内聚性中的操作使用的并不是相同数据
❑
PrintReport():
按一定顺序打印报表
1、打印销售收入报表操作
2、打印支出报表操作
3、打印雇员名单报表操作
4、打印客户名单报表操作
FileOperation():
文件读写
1、打开文件操作
2、读文件操作
3、处理文件内容操作
4、输入结果操作
5、关闭文件操作
逻辑内聚性
⏹当一个子程序中同时含有几个操作,而其中一个操作又被传进来的控制标志所选择时,就产生了逻辑内聚性
⏹
⏹其内部操作仅仅是因为控制流,或者说“逻辑”的原因才联系到一起
⏹
InputData():
输入用户名、雇员时间卡信息或者库存数据
If输入用户名then……
If输入雇员时间卡then……
If输入库存数据then……
Else……
偶然内聚性
⏹当同一个子程序中的操作之间无任何联系时,为偶然内聚性
⏹
⏹偶然内聚性也叫作“无内聚性”
⏹
模块耦合性
⏹模块耦合性
⏹
❑耦合性表示相互作用的模块之间的互连程度
❑
❑应尽量降低模块之间的耦合性
❑
⏹强调“松耦合”的目的
⏹
❑模块应被设计成可以提供一整套功能,以便程序的其它部分与它清楚地相互作用
❑
耦合分类
⏹无任何连接:
两个模块中的每一个都能独立地工作而不需要另一个的存在(最低耦合)。
⏹
⏹数据耦合:
两个模块彼此通过参数交换信息,且交换的仅仅是数据(低耦合)。
⏹
⏹控制耦合:
两个模块之间传递的信息有控制成分(中耦合)。
⏹
⏹公共环境耦合:
两个或多个模块通过公共环境相互作用
⏹
❑一个存数据,一个取数据(低耦合)
❑
❑都存取数据(低--中之间)
❑
⏹内容耦合
⏹
❑一个模块访问另一个模块的内部数据
❑
❑两个模块有一部分程序代码重叠
❑
常见的OO耦合
⏹简单数据参数耦合
⏹
❑两个对象之间通过参数传递数据,并且为简单数据类型
❑
⏹简单对象耦合
⏹
❑一个对象实例化另一个对象所形成的耦合关系
❑
⏹对象参数耦合
⏹
❑两个对象之间通过对象参数传递数据
❑
⏹语义上的耦合
⏹
信息隐蔽
⏹信息隐蔽有称为“封装”
⏹
❑使外部的可见部分和内部的不可见部分相互隔离
❑
⏹信息封装是设计模块和子程序的一种重要方法
⏹
❑对于模块而言,封装的部分称为内部信息
❑
公开的部分称为模块的公开信息或接口
7)结构化设计vs.面向对象设计
8)
从模块的角度理解结构化设计和OO设计的区别
❑面向对象设计中的模块与结构化设计中模块的含义是一致的
❑
–数据和功能的封装
–
–满足信息隐蔽的要求
–
–追求高内聚低耦合
–
❑结构化设计中模块和模块之间的关系,被紧紧局限于信息流(结构化设计是面向功能)
❑
❑面向对象设计方法,充分挖掘了“关系”的表达方式,可以尽可能的将事物之间复杂的关系予以体现
❑
9)抽象数据类型(ADT)
10)
了解基本概念,能够构造出简单的ADTCC2–6.1节
⏹类的基础:
抽象数据类型
⏹
❑类是一组数据和子程序构成的集合,这些数据和子程序共同拥有一组内聚的、明确定义的职责。
❑
❑ADT是指一组数据以及对这些数据所进行的操作的集合
❑
⏹以上两段描述有何本质区别?
⏹
❑从数据的角度来看类和ADT没有区别
❑
❑从OO的角度来看,类还涉及到面向对象中的概念
❑
ADT中的数据
⏹抽象数据类型中的“数据”
⏹
❑ADT中“数据”的概念很随意
❑
❑ADT中的“数据”可以指任意类(Class)所能描述的实体
❑
CruiseControl
Setspeed
Getcurrentsettings
Resumeformerspeed
Deactivate
FuelTank
Filltank
Draintank
Gettankcapacity
Gettankstatus
Stack
Initializestack
Pushitemontostack
Popitemfromstack
Readtopofstack
Light
Turnon
Turnoff
File
Openfile
Readfile
Setcurrentfilelocation
Closefile
List
Initializelist
Insertiteminlist
Removeitemfromlist
Readnextitemfromlist
需要用到ADT的例子:
文本编辑程序中对字体控制的部分
11)ADT与类的区别
12)
体会ADT和类在概念上有什么区别。
(主要根据ppt上的介绍)
给你一个ADT,构造出相应的类(Java或C++)
体会ADT与类的区别
⏹使用ADT的益处
⏹
❑可以隐藏实现细节
❑
❑改动不会影响到整个程序
❑
❑让接口提供更多信息
❑
❑更容易提高性能
❑
❑让程序的正确性更显而易见
❑
❑程序更具自我说明性
❑
❑无需在程序内到处传递数据
❑
❑可以在像现实世界中那样操作实体
❑
⏹在类中对应的体现
⏹
❑隐藏私有信息
❑
❑封装性
❑
❑真正意义的接口
❑
❑更易于修改
❑
❑易于识别错误
❑
❑可读性高
❑
❑封装数据和操作
❑
更接近人的思维
从ADT到类:
完整的理解
⏹类是以面向对象概念做支撑的ADT
⏹
❑封装的概念
❑
❑继承的概念
❑
❑多态的概念
❑
⏹ADT中的封装与类的封装有本质区别
⏹
❑ADT是弱封装
❑
❑类通过可见性实现真正意义的封装(即模块化的黑盒)
❑
13)良好的类接口
14)
参阅CC2-6.2节给你不良好的类接口,能够指出其中的问题,并加以改进
(难度限于书中例子的难度)
15)包含和继承的选择(了解)
16)
类之间的包含(Containment)关系
⏹描述了一个类拥有另外一个类的关系
⏹
⏹包含可以理解为“有一个……”的关系
⏹
类之间的继承(Inheritance)关系
⏹描述了一个类是另一个类的特化的关系
⏹
⏹继承可以理解为“是一个……”的关系
⏹
7.防御式编程
8.
1)防御式编程的主要思想
2)
pptpage3
⏹防御式编程的主要思想
⏹
❑防御式编程是防御式设计(defensivedesign)的一种表现形式,它是为了保证对程序的不可预见的使用不会造成之后程序功能的破坏。
❑
❑防御式编程的思想可以看成是为了尽量消除或降低墨菲法则(Murphy‘sLaw)带来的影响。
❑
❑防御式编程技术主要使用在那些容易发生错误输入以及错误的使用会带来灾难性后果的程序片段上。
❑
⏹隐喻:
防御式驾驶
⏹
17)墨菲法则(了解)
18)
能够使用Java、墨菲法则是指“任何有可能出错的事终将出错。
(Anythingthatcangowrongwillgowrong.)”。
引申为“所有的程序都有缺陷”,或“若缺陷有很多个可能性,则它必然会朝往令情况最坏的方向发展”。
墨菲法则在设计理念上的使用
⏹3.5吋的软碟设计上,就设计成只有一种方法可以插进机中,而现代的光碟机也只有一种放光碟的摆法,把出错的机会减少
⏹
c++、c#等任意一种语言建立自己的断言机制
19)断言部分(重要)★
20)
基本概念、使用方式
⏹断言是一种布尔表达式,用于在程序中表明该处所要满足的条件
⏹
⏹断言是指在开发期间使用的、让程序在运行时进行自检的代码
⏹
⏹一个断言通常含有两个参数
⏹
❑一个描述假设为真时的情况的布尔表达式
❑
❑一个断言为假时需要显示的信息
❑
⏹常见的两种形式(以Java为例)
⏹
❑assertExpression1
❑
❑assertExpression1:
Expression2
❑
a)建立自己的断言机制
b)
常见的高级语言都支持断言
⏹如C++、Java、C#和MicrosoftVisualBasic等
⏹
如果不直接支持断言也可以自己实现
⏹J2SE1.3及早期版本没有内建的断言机制
⏹
⏹C++中标准的assert宏并不支持文本信息
⏹
c)使用断言的指导原则/建议
d)
用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况
避免把需要执行的代码放到断言中
用断言来注解并验证前条件和后条件
对于高健壮性的代码,应该先使用断言再处理错误
e)错误处理和断言
f)
了解两者的主要区别
⏹错误处理代码(error-handlingcode)是用来检查不太可能经常发生的非正常情况
⏹
❑如,检查有害的输入数据
❑
❑错误处理属于程序运行的“正常”情况
❑
⏹断言是用来检查永远不该发生的情况
⏹
❑用于检查代码中的bug
❑
❑触发了断言则应该修改程序的源代码并重新编译,然后发布软件的新版本
❑
g)避免把需要执行的代码放到断言中
h)
给出代码示例,能够指出此类问题
i)用断言来验证前置和后置条件
j)
给出代码示例,能够指出其中断言属于哪种应用
⏹前置条件是子程序或类的调用方代码在调用子程序或实例化对象之前要确保为真的属性
⏹
❑前条件是调用方代码对其所调用的代码要承担的义务
❑
⏹后置条件是子程序或类在执行结束后要确保为真的属性
⏹
❑后置条件是子程序或类对调用方代码所承担的责任
❑
21)错误处理技术(了解)
22)
错误处理技术用来处理那些预料中可能要发生的错误情况
⏹返回中立值(如数值返回0)
⏹
⏹换用下一个正确的数据
⏹
⏹返回与前次相同的数据
⏹
⏹换用最接近的合法值(如经度设置为(-180,180)之间)
⏹
⏹把警告信息记录到日志文件中
⏹
⏹用语言内建的异常机制抛出一个异常
⏹
……
⏹返回中立值
⏹
❑继续执行操作并简单地返回一个没有危害的数值
❑
❑例如:
❑
–数值计算可以返回0
–
–字符串操作可以返回空字符串
–
–指针操作可以返回一个空指针
–
⏹换用下一个正确的数据
⏹
❑在处理数据流的时候,返回下一个正确的数据即可
❑
⏹返回与前次相同的数据
⏹
❑这种错误处理的思想就是“重用上一次正确的结果”
❑
❑如windows系统崩溃后用上一次配置重启动
❑
⏹换用最接近的合法值
⏹
❑常出现在数值超出其正常设定的上下界的时候
❑
⏹把警告信息记录到日志文件中
⏹
❑在使用这种方法的时候需要对错误信息进行标示,或者将警告信息单独存放,以便快速查询定位
❑
⏹返回一个错误码
⏹
❑当只有系统的某些部分处理错误,其他部分则不在本地(局部)处理错误时
❑
❑通知系统的方法
❑
–设置一个状态变量的值
–
–用状态值作为函数的返回值
–
–用语言内建的异常机制抛出一个异常
–
⏹调用错误处理子程序或对象
⏹
❑优点:
能把错误处理的职责都集中到一起
❑
❑代价:
错误处理代码与整个程序紧密耦合
❑
⏹在局部处理错误
⏹
❑这种方法的思想是希望将错误问题限制在一个特定的区域中,而不要扩散
❑
❑缺点是会掩盖错误,而是其他部分无法获知
❑
⏹具体错误处理的选择是一种平衡选择的结果
⏹
❑例如局部处理错误和设计全局性错误处理子程序或对象就是相互对立的设计思想,但没有绝对的好坏
❑
23)异常处理机制(重要)
24)
a)异常的基本结构
b)
⏹子程序使用throw抛出一个异常对象,再被调用链上层其他子程序的try-catch语句捕获
⏹
c)异常处理机制所涉及的要素
d)
⏹异常对象
⏹
⏹对异常对象的抛出
⏹
⏹对所抛出异常的捕捉
⏹
e)finally一般被用来进行清理工作
f)
try-catch-finally与try-catch的区别在于finally
⏹finally保证了在程序执行时无论有没有异常被抛出、捕捉,finally块都会被执行
⏹
finally一般被用来进行清理工作
⏹在异常处理时通过finally块来执行任何清除操作
⏹
⏹最常见的就是关闭流、关闭连接、释放或销毁资源等
⏹
g)改进使用不当的异常情况
h)
能够识别出代码中使用不当的异常(如,pptP40,41)
i)隔离可以划分错误处理和断言
j)
理解通过隔离代码将错误处理和断言两种技术合理应用到自己的系统中。
⏹隔离部分包含了“脏数据”
⏹
❑隔离部分的程序应使用错误处理技术,在那里对数据做的任何假定都是不安全的
❑
⏹通过隔离部分之后的是“干净数据”
⏹
❑隔栏内部的程序里就应使用断言技术,因为传进来的数据应该己在通过隔栏时被清理过了
❑
25)产品版和开发版
26)
了解两者的区别
⏹不要自动地把产品版的限制强加于开发版之上
⏹
❑产品级的软件要求能够快速地运行,而开发中的软件则允许运行缓慢
❑
❑产品级的软件要节约使用资源,而开发中的软件在使用资源时可以比较奢侈
❑
❑产品级的软件不应向用户暴露可能引起危险的操作,而开发中的软件则可以提供一些额外的、没有安全网的操作
❑
⏹调试模式的使用
⏹
27)进攻式编程
28)
了解什么是进攻式编程
⏹进攻式编程是主动暴露可能出现错误的态度
⏹
❑在开发阶段让它显现出来,而在产品代码运行时让它能够自我恢复
❑
⏹常用的进攻式方法
⏹
❑确保断言语句使程序终止运行
❑
❑完全填充分配到的所有内存
❑
❑完全填充己分配到的所有文件或流
❑
❑确保每一个case语句中的default分支或else分支都能产生严重错误(如终止程序)
❑
❑在删除一个对象之前把它填满垃圾数据
❑
9.语句编写的技巧
10.
1)条件语句(重要)
2)
a)优先考虑正常代码路径
b)
能够根据此原则对代码进行优化调整(难度限于CC2和PPT)
把正常情况的处理放在if后面而不要放在else后面
❑思想:
把决策的结果代码放在尽可能靠近决策位置
❑所谓正常的处理情况指的是程序运行的最主要路径
❑将注意力集中在正常路径上便于设计分支的逻辑
以上示例代码的问题在于:
从理解代码的角度来看,难以辨识程序的正常路径
从编写代码的角度来看,难以设计分支逻辑
调整的策略:
正常的路径一致地写在前面,即if语句后面
所有的错误情况都写在后面,即else语句后面
c)利用布尔函数调用简化复杂的检测
d)
能够根据此原则对代码进行优化调整(难度限于CC2和PPT)
3)循环语句(重要)(难度限于CC2和PPT)
4)
a)循环的种类
b)
了解常见循环语句分类各自的特点
计数循环(countedloop)执行的总次数是一定的
⏹连续求值的循环(continuouslyevaluatedloop)预先并不知道将要执行多少次,它会在每次送代时检查是否应该结束
⏹无限循环(endlessloop)一旦启动就会一直执行下去
⏹迭代器循环(iteratorloop)对容器类里面的每个元素执行一次操作
c