软件构造教程(预习或复习自取四)

 

第一章:软件开发概述

第二章:模块化软件构造

第三章:面向对象的软件构造

第四章:数据处理的软件构造

  • 本章讨论案例中数据处理的问题:按需选择、使用数据及数据持久性。
  • 重点是如何把算式和习题从程序变量的值转化为可共享的、持久性的文件数据;学习文件的产生、存储和读取操作,学习一个特殊的文本文件格式CSV编程方面,学习表驱动编程模式和防御性编程。
  • 测试方面,学习白盒测试的基本技术,继续学习JUnit的其他测试方法。
  • 实现数据持久性最基本的途径是文件和数据库。
     

4.1数据及其持久化

  1. 保存在程序之外(如文件、网络)的数据称为持久数据。
  2. 文件处理是编程语言支持应用程序存储和处理大量持久数据的一个最重要能力。
  3. Java 等编程语言通常都提供了文件处理和输入/输出流的功能。

从比特到文件

  • 字符由比特组成。
  • 一组字符或字节组成字段,一个字段是传递含义的一组字符或字节。
  • 若干字段构成记录,记录是一组有关系的字段。
  • 一个文件可以是一组相关的记录。

组织文件中的记录有多种不同的方式。

  • 最常见的方式是顺序文件。
  • 一组相关的文件可以组成数据库。

一个应用程序如何选择数据的存储、管理和处理方式,要考虑下面的因素。

  • 数据的持久性和使用频次
  • 生产和访问数据的难易程度
  • 共享与传输
  • 数据的量及管理
  • 数据的操作方式
     

4.2文件与输入/输出流

理解文件的概念及其分类
理解输入/输出流的概念及应用了解数据序列化的概念
掌握CSV格式的文本文件

        4.2.1文件
  •  案例的持久性问题,本质上是程序之间的一种交互,即一个程序或模块产生数据,另一个程序或模块读取并使用这些数据。
  • 程序之间可以通过文件实现输入/输出。
  • 在操作系统中,文件是组织和管理数据的基本单位,也是对物理输入/输出设备的抽象,使用者不必关心文件及其内容的存取方式、存储位置、结束标志等。
1.字符文件与字节文件
  • 在字符文件中,字节表示字符,使得人们可以查看、编辑文件。在字节文件中,字节不一定表示字符;字节组还可以表示其他类型的数据,如整数、浮点数或汉字字符。
  • 文件都是以二进制格式存储的。
  • 字节文本的存储无须任何编码,而使用文本文件时要考虑字符编码。
2.记录文件和流式文件


文件分为物理文件和逻辑文件;

逻辑文件从结构上分两种:

  • 无结构的流式文件,信息不划分单位,由一串字符流构成文件;
  • 有结构的记录文件,信息按逻辑上独立的含义划分信息单位,称为一个逻辑记录(简称记录)。

        4.2.2输入/输出流

  • 在程序中,可以把文件理解成物理概念,流是逻辑概念。程序的输入/输出操作是针对抽象的流来定义的,前提是把该文件与一个(对象)流联系起来。
  • 高级语言如C、C++、C#、Java 等保留了操作系统中的文件,同时引入了流,执行对文件的读/写操作。
  • Java 等语言没有定义文件结构,即记录,所以,程序员必须设计文件结构来满足应用的需求。

        4.2.3数据序列化
1.编写数据序列化代码
  • “线性化”函数将结构化数据转换为字符串类型的数据。当程序需要结构化数据时,再通过“结构化”函数,把线性化的数据按照原先的数据结构恢复成结构化数据。
  • 如何读/写对象:要把对象转换成字符串,在Java中类似toString。首先要把具有结构的数据元素分解,转换成字符串,然后用特殊分隔符隔离。每个数据对象还要再以分隔符隔离。
2.对象序列化
  • Java、C#等面向对象语言都有实现对象序列化的类或接口(类似Serializable ),把对象数据转换成(二进制)字节序列的形式,与外部源共享或传输数据。
  • 程序员不必准确知道系统是如何表示对象的字节序列的。
  • 序列化(Serialization)是将对象的状态信息(成员变量)转换为可以存储或传输的形式的过程。它的逆过程则被称为反序列化(Deserialization )。
  • 在序列化期间,对象将其当前状态写入到临时或持久存储区。可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。序列化的对象是对象的字节序列,包含对象数据及其类型信息。这样,信息可以用来再创建内存中的对象。

        4.2.4 CSV格式的文本文件
  • 逗号分隔值CSV ( Comma-Separated Values)以纯文本形式存储数字和文本数据。
  • CSV是一种通用的、相对简单的文本文件格式,在电子表单和数据库中有着广泛的应用.
  • 一些程序设计语言如Java、R、Go和Python 都内置了读/写CSV格式文件的函数或类。
  •  CSV没有单一的、明确定义的格式。

在实践中,CSV泛指具有以下特征的任何文本文件:

  • 纯文本,使用某个字符集,如ASCII、Unicode、EBCDIC或GB2312;
  • 由记录组成(典型的是每行一条记录);
  • 每条记录被分隔符分隔为字段(典型分隔符有逗号、分号或制表符;有时分隔符可以包括可选的空格);每条记录都有同样的字段序列。

4.3编写健壮的程序

理解健壮性的概念

基本掌握防御性编程了解断言

        4.3.1防御性编程
  • 健壮性(robutness)是指程序对于要求之外的输入进行判断并处理、使程序保持运行状态,即使这有时可能导致不准确的结果。
  • 程序的正确性指的是程序绝不产生不准确的结果。
  • 防御性编程和断言是实现程序健壮性的技术手段。
     

 防御性编程的基本思想:

  • 程序员要预计程序使用者的过错、无效的输入、甚至有害的数据及使用者的过失,
  • 即使这种事情罕见,也要采取适当措施保护自己的程序。

保护程序无效输入破坏的基本原则∶

  • 检查每个输入参数的数据;特别要检查从程序外部进入程序的数据。
  • 一旦发现了无效数据,就要决定处理的方式。
  • 基本的方式有处理错误和使用异常。
     
1.处理错误


错误处理的方式影响软件满足正确性、健壮性及其他非功能性需求的能力。
数据错误出现时的一些建议

  • ( 1)继续运行程序、返回中性无害的数据。
  • (2) 用最接近的有效数据替换无效数据。
  • (3) 在日志中记录警告信息并继续运行程序。
  • (4)调用错误处理程序或对象。
  • (5)屏幕显示错误信息。
  • (6)尽可能在局部处理错误。
  • (7) 返回一个错误编码,让特定程序处理这个错误。
 2.使用异常


异常是处理错误的一种特殊方式,出现了错误或异常行为的程序能把错误传递给程序的调用者,让它处理。

异常处理─般有两种模型:终止模式和恢复模式。

终止模式

  • 假设错误非常关键,导致程序无法返回到异常发生的地方继续执行。
  • 一旦抛出异常,就表明错误已无法挽回,也不能回来继续执行。

恢复模式

  • 认为异常处理程序的工作是修正错误,重新尝试调用出问题的方法,并认为二次处理能成功。
  • 恢复模式希望处理异常后程序能继续执行。

        4.3.2使用断言

 断言:

  • 是让程序在运行过程中自我检查的代码。
  • 如果断言为真,就意味着程序如期望的正常;否则,就表示在代码中发现了意外。

编写代码时,我们总是会做出一些假设。断言用于在代码中捕捉这些假设。
可以将断言视为异常处理的一种高级形式。

可以使用断言在代码中记录一些假设,例如:

  • 输入参数的值在预期范围内;
  • 程序运行时文件流已打开或者在开始的地方;
  • 输入参数的数组、表或其他容器已经包含了数据;
  • 指针非空。

断言的现行形式是一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。


断言分为如下三类:

  • 前置断言:代码执行之前必须具备的特性。
  • 后置断言:代码执行之后必须具备的特性。
  • 不变断言:代码执行前后不能变化的特性。
  1. 断言的基本用途是调试和测试程序,编译器启动断言检查后才能使用断言。
  2. 程序布署完之后就关闭断言。
  3. Java使用断言的例子:Junit


下面是使用断言的一些建议:

  • 对预计出现的条件使用错误处理,对不应当出现的条件使用断言。
  • 错误处理用于检查不合理的输入数据;断言则用于检查代码中的错误。
  • 避免在断言中放置可执行的代码。因为关闭断言后,编译器可能会删除这些代码。
  • 用断言来记录和验证前置条件和后置条件。健壮性要求高的程序同时使用断言和错误处理。
     

4.4字符串处理与正则

  • 在操作文本文件时,数据都是字符串。
  • 基本类型(如32.5)或结构化的数据(如32+5),存储在文本文件中都是字符串“32.5”和“32+5”。
  • 字符串作为基本的内置类型,编程语言也提供了大量的字符串操作,如:查找一个字符、查找一个字串、置换一个字串、合并两个字串、字符串复制等。
  • 自定义的数据类型或对象,则需要用户自己编写转换程序。
  • 如,C#、Java等需要为自定义的类编写对象的字符串显示方法toString(),覆盖从根类继承的默认显示。

现代编程语言提供了更丰富的字符串处理库,其中重要而又易用的是正则表达式。

正则表达式 Regular Expressions

  • 是一串字符,它所定义的模式可用来查找、显示或修改输入序列中出现的某个模式的一部分或全部。
  • Java在String类中提供了boolean matches (Stringregxep)、void replaceAll (String regxep, Stringreplacement)和String [] split(String regxep)三种基本方法,它们的作用分别是匹配、替换全部匹配内容、分割。参数都包含了正则表达式regxep。

4.5程序中数据集的使用

理解算式基的概念

掌握表驱动编程的应用

        4.5.1算式基
  1. 案例的算式及习题都是在使用时由程序随机产生,即数据是按需生成和使用的。
  2. 产生的算式、习题数据都以程序变量的值出现在计算机的内存中,一旦程序结束,数据立即丢失。
  3. 数据的这种处理方式有局限性:
  • (1)效率。
  • (2)  复用。
  • (3)共享。

 利用算式基产生算式

  • 随机地生成两个整数i和j,0<i , js100,然后从加法基或减法基选择一个数组元素[i,j]构成算式。
  • 习题的创建与算式基的创建称为两个独立的活动。
  • 算式基实现了按需选择数据和数据重用,为数据共享提供了支持。
  • 如果能持久地、以通用的格式存储算式基及产生的习题,就可以在不同程序、甚至不同语言的程序之间实现数据共享。

        4.5.2表驱动编程

算式基的设计把加法、减法算式的约束条件,以及算式产生的信息都放在了一张表中,使程序在表中通过选择条件而不使用逻辑语句( if 或case)得到算式及其运算结果,如此编写程序的方式称为表驱动编程。

逻辑语句与表驱动编程

  • 理论上,任何使用逻辑语句的情况都可以用存储了信息的读取表的操作。
  • 如果条件简单,则逻辑语句直截了当、易用。
  • 逻辑链越复杂,表驱动编程就越有吸引力。
  • 使用得当,表驱动编程把复杂的逻辑编织在表中,而不是编织在代码中,使得程序结构简洁、逻辑清晰、容易修改和扩展。
     

使用表驱动编程方法需要考虑以下两点。

  • 表项的内容。查询表得到的可以是直接结果或者是动作。在这种情况下,可以在表中存放描述动作的代码,或者对某些语言可以存放引用实现动作的函数。在表中存放动作使表的内容及其处理变得复杂了。
  • 表项的查询。有三种基本的查询方式:直接访问、阶梯访问和索引访问。

1.直接访问
表中的项表示一个结果或动作,通过数组下标(一维表
)或矩阵下标(二维表)直接访问表。
例如,计算某个月的天数,可以用下标是1~12的数组存放每个月份的天数,用月份作为数据直接得到当月的天数。


2.阶梯访问
表中的项表示一个数据范围而不是对应不同的单个数据,而且数据的排列按照一定的顺序,这样就避免了显示比较,实际上是隐含了比较。

3.索引访问

  • 首先为查询的数据建立一个检索表,用索引数据在索引表中得到关键字。然后用该值在另一个表(主表)中检索感兴趣的主数据。
  • 散列表或哈希表(Hash Table)是索引访问的一个例子。


索引访问的优势如下。

(1)节省空间。
(2)操作检索表项要比操作主表项简单。
 

4.6基于程序结构的调试

        4.6.1语句覆盖测试
  • 良好编程的一个基本原则,程序中每个组成(语句和变量等)都要有用,即完成指定的功能,不多也不少。
  • 结构性测试试图证实这个原则得到落实或落实的程度。
  • 普通的编译程序能检查程序的语法错误,有些编译也能检查程序的语义错误,甚至逻辑上的缺陷。
  • 测试程序更关注语句、语句的组成及语句之间的关系。
     

 语句覆盖测试

  • 设计测试,检测程序的每条语句是否都能执行。
  • 如果一个测试用例没有使所有的语句都得到执行,就增加测试,试图增加执行的语句数量,直至所有语句都能执行。
  • 否则,要么测试用例不够,不能使所有语句都执行﹔;要么程序有缺陷,出现了不可能执行的语句。

 要明确语句的概念
计算机程序的语句一般分为简单语句和复合语句。

语句覆盖的基本准则∶
设计测试用例,使得程序的每条基本语句都得到执行。

运用语句覆盖的测试目标:

  • 使用尽可能少的测试用例,实现最大的语句覆盖,用语句覆盖率来量化:
  • 语句覆盖率=覆盖的语句数/语句总数

即使是100%的语句覆盖测试也不能保证程序正确无误。
语句覆盖是最容易实现、也是最弱的覆盖准则。
 

        4.6.2逻辑覆盖测试

1﹒判定覆盖
判定覆盖准则测试的含义是,设计测试用例,使得程序中的每个判断分支都至少经历一次。
由于一个判定往往代表着程序的一个分支,所以判定覆
盖也称分支覆盖。


2.条件覆盖

  • 布尔条件分简单条件和复合条件。
  • 简单条件指的是布尔变量或原子布尔表达式,即不含布尔运算的布尔表达式。
  • 复合条件则是至少用一个布尔运算连接的简单条件。

条件覆盖的准则是:

  • 设计测试用例,使得程序中的每个简单布尔条件的所有可能的值都至少满足一次。

        4.6.3程序控制流图

程序控制流图(Control Flow Graph ,CFG)

  • 表示程序的控制流向,指的是控制从一条指令到另一条指令的流动。
  • 控制流动的方式多种多样,如按照指令的先后顺序、函数调用、消息传递或中断。
  • 条件语句改变程序中的控制的正常的、顺序的流动。
     

控制流图的画法如下:

  • (1)节点:圆圈表示一条可执行的基本语句,可以增加标记,如语句号或节点顺序号;
  • (2)控制线或弧︰用带箭头的有向线表示连结的两个语句的执行顺序;
  • (3)程序执行(控制流)的分叉或交汇处,可以用节点表示;
  • (4)每个函数或程序都有唯一的开始和结束节点;
  • (5)不含执行程序不可达到的语句或不能使程序停止的语句;
  • ( 6)控制线必须连接两个节点,开始节点和结束节点除外。

        4.6.4路径覆盖测试

 程序的执行路径(简称路径)

  • 可以抽象语句序列,即从进入程序的第一条语句到程序停止、一次运行的语句序列。
  • 对于r函数而言,停止语句可以是自然的最后一条语句也可以是一条return语句。


用CFG再次简化和抽象程序的执行路径。用图的路径表示程序的执行路径∶
        一条路径是从CFG的开始节点经过连线到达结束节点的节点序列或控制线序列。

  • (1)分解复合条件:把复合条件中每个简单条件用一个节点表示,并调整控制线。
  • (2)合并简单的顺序语句:赋值语句、打印语句等不含条件,如果是顺序排列,它们的执行不会改变程序的控制顺序,即不会产生新的路径,可以将它们合并在一起,用一个节点表示。

  1.  路径有长有短,如果有循环语句,一条路径可能会出现在每一次的循环路径中。
  2. 按照等价类的思路,我们希望测试的每条路径都有所不同,但又尽量包含可能多的路径,通常采用基本路径覆盖法。
  3. 简单地说,一条基本路径是指,和其他基本路径相比,至少引入一个节点或一个新的控制线的路径。
  4. 用圈复杂数计算一个程序的基本路径数:一个CFG图G的圈复杂数是VG) = e-n+2其中e是G中的边数,n是G中的节点数。

第五章:用户交互的软件构造

第六章:软件重构与交付

第七章:GUI软件构造

第八章:应用数据库

第九章:基于复用的软件构造

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值