描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302518297丛书名: 高等学校计算机专业规划教材
1.1.1 软件危机 1
1.1.2 软件危机出现的原因 3
1.1.3 软件工程的发展 4
1.2 软件工程的概念 5
1.2.1 软件工程的定义 5
1.2.2 软件工程的目标 6
1.2.3 软件工程的实施原则 8
1.3.1 软件的概念 11
1.3.2 软件的分类 12
1.3.3 软件生命周期 13
1.4 软件过程模型 16
1.4.1 瀑布模型 16
1.4.2 原型模型 17
1.4.3 增量模型 18
1.4.4 螺旋模型 19
1.4.5 喷泉模型 20
1.4.6 敏捷过程模型 21
1.4.7 渐进交付迭代模型 23
1.4.8 微软解决框架过程模型 24
1.4.9 软件过程模型的比较 26
1.5 软件开发方法 26
1.5.1 结构化开发方法 27
1.5.2 面向对象开发方法 27
1.6 案例描述 28
1.6.1 简历信息自动获取和查询系统 28
1.6.2 试卷自动生成系统 29
1.7 本章小结 30
习题 31
第2章 软件需求工程 /?33
2.1 软件需求的基本概念 33
2.1.1 需求分析的任务 33
2.1.2 需求分析的原则 34
2.1.3 需求分析的内容 35
2.2 可行性分析 36
2.2.1 可行性分析的内容 36
2.2.2 系统流程图 38
2.3 需求工程的过程 39
2.3.1 需求工程中的参与人员 39
2.3.2 需求工程中的活动 40
2.3.3 需求工程的管理 41
2.4 需求获取技术 42
2.5 结构化需求分析和建模 45
2.5.1 结构化需求分析概述 45
2.5.2 面向数据的数据建模 45
2.5.3 面向数据流的功能建模 47
2.5.4 面向状态转换的行为建模 51
2.6 数据字典 54
2.6.1 数据字典的编写要求 54
2.6.2 数据字典的定义 55
2.7 案例——简历自动获取和查询系统的需求建模 56
2.7.1 数据建模——E-R图描述 56
2.7.2 功能建模——数据流图 57
2.7.3 行为建模——状态转换图 59
2.7.4 加工逻辑——PDL语言的描述 59
2.7.5 数据字典 59
2.8 需求评审 61
2.8.1 软件需求规格说明 61
2.8.2 需求评审标准及需求验证 64
2.8.3 需求变更管理 66
2.9 本章小结 67
习题 67
第3章 软件设计基础 /?70
3.1 软件设计概述 70
3.1.1 软件设计与软件需求 70
3.1.2 软件设计的任务 71
3.1.3 软件设计的原则 73
3.2 软件体系结构设计 74
3.2.1 体系结构设计概述 74
3.2.2 以数据为中心的数据仓库模型 74
3.2.3 客户端/服务器模式的分布式结构 75
3.2.4 层次模型 77
3.2.5 MVC模型 78
3.3 模块化设计 80
3.3.1 软件模块化与分解 80
3.3.2 抽象 81
3.3.3 信息隐藏 81
3.3.4 模块独立性 82
3.3.5 启发式规则 85
3.4 界面设计 88
3.4.1 界面设计的任务 88
3.4.2 界面设计的原则 89
3.4.3 界面设计的特性 90
3.5 软件设计评审 91
3.5.1 软件设计规格说明 91
3.5.2 软件设计评审标准 94
3.5.3 软件设计验证 96
3.6 本章小结 97
习题 98
第4章 结构化设计方法 /?99
4.1 结构化设计方法概述 99
4.2 面向数据流的设计方法 100
4.2.1 层次图和结构图 100
4.2.2 变换分析法 102
4.2.3 事务分析法 106
4.2.4 混合分析法 107
4.3 案例——简历自动获取和查询系统的数据流设计方法 108
4.3.1 用变换分析法进行设计 108
4.3.2 用事务分析法进行设计 109
4.3.3 两种方法的比较 111
4.4 结构化详细设计的工具 111
4.4.1 程序流程图 111
4.4.2 盒图 113
4.4.3 问题分析图 114
4.4.4 判定树 115
4.4.5 判定表 116
4.4.6 详细设计工具的比较 116
4.5 本章小结 117
习题 118
第5章 软件实现 /?120
5.1 程序设计语言 120
5.1.1 程序设计语言的分类 120
5.1.2 程序设计语言的特性 121
5.1.3 选择程序设计语言 122
5.2 程序设计风格 124
5.2.1 程序编排和组织的准则 124
5.2.2 程序设计的效率 128
5.3 代码重用 130
5.4 代码评审 131
5.5 本章小结 135
习题 135
第6章 软件测试 /?138
6.1 软件测试基础 138
6.1.1 软件测试概念 138
6.1.2 软件测试过程模型 139
6.1.3 软件测试原则 141
6.1.4 软件测试在软件开发各阶段的工作流程 143
6.1.5 软件测试信息流 145
6.1.6 软件测试技术分类 145
6.2 白盒测试 147
6.2.1 逻辑覆盖 147
6.2.2 循环测试 150
6.2.3 路径测试 152
6.3 黑盒测试 156
6.3.1 等价类划分 156
6.3.2 边界值分析 158
6.3.3 错误推测法 158
6.3.4 因果图法 159
6.4 白盒测试和黑盒测试的比较 161
6.4.1 应用角度的不同 161
6.4.2 白盒测试的优点与不足 162
6.4.3 黑盒测试的优点与不足 162
6.5 软件测试策略 162
6.5.1 单元测试 162
6.5.2 集成测试 165
6.5.3 确认测试 169
6.5.4 系统测试 170
6.6 调试 172
6.6.1 软件调试过程 172
6.6.2 软件调试方法 173
6.7 软件测试报告 174
6.7.1 软件测试说明 174
6.7.2 软件测试报告 176
6.8 本章小结 177
习题 177
第7章 统一建模语言UML /?180
7.1 UML的发展 180
7.1.1 UML的产生 180
7.1.2 UML的构成 181
7.1.3 UML的特点 182
7.2 面向对象的基本概念 182
7.3 UML视图 188
7.4 UML的图和模型元素 189
7.4.1 用例图 189
7.4.2 类图 191
7.4.3 包图 192
7.4.4 状态图 193
7.4.5 活动图 193
7.4.6 顺序图 195
7.4.7 协作图 196
7.4.8 构件图 197
7.4.9 配置图 198
7.5 UML的关系 198
7.5.1 关联关系 199
7.5.2 泛化关系 202
7.5.3 依赖关系 205
7.5.4 实现关系 206
7.6 UML的通用机制 206
7.6.1 修饰 206
7.6.2 注释 207
7.6.3 规格说明 207
7.6.4 扩展机制 207
7.7 基于UML的软件过程 209
7.8 本章小结 211
习题 211
第8章 面向对象分析 /?213
8.1 面向对象分析概述 213
8.1.1 传统软件过程中的不足 213
8.1.2 面向对象的特点 214
8.1.3 面向对象分析的基本过程 215
8.1.4 面向对象分析的3类模型 215
8.1.5 静态模型的5个层次 216
8.2 建立功能模型(用例模型) 217
8.2.1 识别参与者 218
8.2.2 识别用例 218
8.2.3 识别用例间关系 220
8.2.4 用例描述文档 221
8.3 建立静态模型(对象模型) 222
8.3.1 识别类与对象 222
8.3.2 划分主题 224
8.3.3 确定结构 225
8.3.4 确定属性 226
8.3.5 确定服务 226
8.3.6 类图描述文档 227
8.3.7 包图描述文档 228
8.4 建立动态模型 229
8.4.1 建立顺序图及其描述文档 229
8.4.2 建立状态图及其描述文档 231
8.4.3 建立协作图及其描述文档 232
8.4.4 建立活动图及其描述文档 233
8.5 “会议中心系统”的面向对象分析案例研究 234
8.5.1 建立功能模型——用例分析 235
8.5.2 建立静态模型——5层结构 235
8.5.3 建立动态模型——交互行为 238
8.6 本章小结 239
习题 240
第9章 面向对象设计 /?241
9.1 面向对象设计概述 241
9.1.1 面向对象分析与设计的关系 241
9.1.2 面向对象设计原则 242
9.2 精化类及类间关系 243
9.2.1 设计类的属性 243
9.2.2 设计类的方法 244
9.2.3 设计类间泛化关系 244
9.2.4 设计关联类 247
9.3 数据设计 247
9.3.1 基于关系数据库的数据设计 247
9.3.2 基于其他方式的数据设计 250
9.4 人机交互设计 250
9.5 建立实现模型 251
9.5.1 构件图及其描述文档 252
9.5.2 配置图及其描述文档 253
9.6 设计模式简介 255
9.6.1 概述 255
9.6.2 Singleton模式 256
9.6.3 Abstract Factory模式 257
9.6.4 Mediator模式 258
9.6.5 Adapter模式 261
9.6.6 Iterator模式 263
9.6.7 State模式 266
9.7 面向对象的测试 268
9.7.1 面向对象测试概述 268
9.7.2 面向对象的单元测试 269
9.7.3 基于过程的面向对象单元测试 273
9.8 本章小结 273
习题 274
第10章 软件维护 /?277
10.1 软件维护概述 277
10.1.1 软件维护的任务 277
10.1.2 软件维护的特点 278
10.1.3 软件维护的分类 278
10.2 软件维护过程 279
10.2.1 软件维护方式 280
10.2.2 软件维护管理的基本内容 281
10.2.3 维护中存在的问题 285
10.2.4 维护活动记录 286
10.3 软件的可维护性 287
10.3.1 可维护性因素 287
10.3.2 提高软件的可维护性 288
10.4 逆向工程 290
10.5 软件维护评审 292
10.5.1 软件维护规格说明文档 292
10.5.2 软件维护评审 295
10.6 本章小结 296
习题 297
第11章 软件项目管理 /?299
11.1 软件项目管理概述 299
11.1.1 软件项目管理的特点和内容 299
11.1.2 软件项目管理目标 300
11.1.3 软件项目管理的4P观点 301
11.2 软件项目规模度量 302
11.2.1 代码行技术 303
11.2.2 功能点计算 304
11.2.3 代码行与功能点间的转换 307
11.3 软件项目估算 308
11.3.1 代码行和功能点的其他估算模型 308
11.3.2 专家估算模型 308
11.3.3 Putnam模型 309
11.3.4 COCOMO模型 309
11.3.5 项目估算模型的小结 312
11.4 项目进度管理 312
11.4.1 项目进度控制 312
11.4.2 甘特图 313
11.4.3 工程网络图 314
11.5 项目风险管理 316
11.5.1 软件风险概念 316
11.5.2 风险管理过程 317
11.6 项目质量管理 320
11.6.1 软件质量因素 320
11.6.2 软件质量保证活动 324
11.6.3 软件质量保证计划 325
11.7 软件配置管理 327
11.7.1 软件配置项 327
11.7.2 配置管理过程 328
11.7.3 软件配置管理计划 331
11.8 项目人员组织管理 332
11.8.1 团队组织 332
11.8.2 团队组织方式 333
11.9 软件能力成熟度模型 335
11.9.1 基本概念 335
11.9.2 软件能力成熟度模型等级 336
11.9.3 关键过程域 337
11.10 本章小结 338
习题 339
参考文献 /?341
第3版前言
本书第2版自2015年6月出版以来,被众多高校选作教材,还作为研究生入学考试的参考书,取得了良好的效果。根据作者近年来从事“软件工程基础”“软件工程综合训练”的教学,并结合软件开发的实践经验,在保持原书结构和篇幅基本不变的前提下,对第2版的内容做了以下修正和 补充:
(1)修改了第2版中出现的错误,更加规范和完善相关的图、表,对文字叙述做了进一步的加工和润色。
(2)根据软件工程的发展,以及项目实际的应用,删除了部分使用较少的内容,包括4GT过程模型、基于构建的开发模型、统一建模过程、Worrior图、管道过滤器模型、面向数据的设计方法、Jackson图等内容。新增部分内容,包括渐进交付的迭代模型、软件过程模型的比较、强调数据字典的作用、面向对象的分析过程、基于过程的面向对象集成测试、软件维护评审等内容,以反映软件工程的最新发展。
(3)新增了部分章节后的习题,目的是让读者更好地在实践中掌握基础理论。
鉴于技术人员专注技术而轻文档编写的实际情况,即使敏捷过程、极限编程等近年来广泛受到关注,也有一定程度的应用与实践,但有效的文档和管理在软件生命周期中仍有较高价值与强大的生命力。因此,本书第3版仍希望通过介绍软件工程各阶段的文档框架编写,来强化文档对软件工程实施的重要性。
下面给出本书的结构图,希望能给读者更好地学习提供帮助。
在全书的结构图中,有两类不同的学习路径:
一是按照本书的章节顺序进行学习。先以结构化程序设计为主,介绍软件工程的基本理论、方法、过程与工具;然后以面向对象为主,借助UML统一建模语言,完成对面向对象基本概念、封装性、继承性和多态性的理解,学习面向对象分析与设计的过程。
二是按照结构图中虚线对应的章节进行学习。这样的学习思路,是将软件工程生命周期的各阶段,按照结构化方法和面向对象方法相对比同时进 行。这样的学习路径,便于用户在同一阶段、对同一项目采用何种方法进行分析与设计产生同步比较。
无论使用哪种学习路径进行学习,读者通过对两类设计思想的不同及软件过程的比较,不仅能分析和总结它们各自的优缺点,还能更深入理解相同的软件工程过程结合不同的软件设计思想,对软件分析、实现和维护的影响,对软件质量和管理发展的推动。
由于作者水平有限,疏漏、欠妥、谬误之处在所难免,恳请读者指正。读者如果对本书有任何意见和建议,欢迎与作者联系:[email protected]。
作 者
于北京理工大学
2019年1月
第2版前言
本书第1版自2012年7月出版以来,作为各类学生授课的教材、不同读者的参考书,以及一些高校用书,取得了良好效果。然而,随着软件工程的发展,为更好地服务于读者,编者对原书内容做了认真修改,编写第 2版。
根据作者近年来从事“软件工程”课程教学和软件开发的实践经验,在保持原书结构和篇幅基本不变的前提下,第2版主要做了以下修正和补充:
(1)修改了第1版中出现的错误,更加规范和完善相关的图、表,对文字叙述做了进一步的加工和润色。
(2)增加软件工程中较重要的内容。增加的内容包括:基于构件的开发模型,Rational统一建模过程,可行性研究及系统流程图,需求验证,管道与过滤器模型,软件设计验证,集成测试案例,确认测试案例。
(3)作者认为软件工程基础应该注重基础理论与实践相结合的理念,因而增加了每章后的习题,特别是增加了实际分析、设计习题,让读者更好地在实践中掌握基础理论。
鉴于技术人员专注技术而轻文档编写的实际情况,本书第2版仍希望 通过介绍软件工程各阶段的编写文档框架,来强调文档对软件工程实施的 重要性。本书各章节的安排,是按照以结构化设计思想为基础,全面介绍软件工程过程各阶段的过程、方法和工具,让读者对软件工程的实施有一个完整、清晰的认识。之后,再以面向对象设计思想为指导,详细介绍基于面向对象的软件工程开发过程。
下面给出本书的结构图,希望能给读者更好地学习提供帮助。
建议学习过程:
(1)第1章通过对软件、软件生命周期和软件过程模型的介绍,让读者对软件工程的基本原理、方法、过程有一个基本认识。该章是全书的导论。
(2)第2~6章以结构化方法为依托,按照软件工程生命周期过程模型的需求分析、概要设计、详细设计、编码和测试等阶段,全面介绍各阶段涉及的过程、方法和工具,让读者对结构化软件工程的实施有一个完整、清晰的认识。
(3)第7~9章以面向对象方法为依托,详细介绍基于UML的软件工程,包括面向对象分析、面向对象设计、设计模式、数据设计和测试,使得读者对面向对象软件工程的实施有一个完整、清晰的认识。通过对这两种
方法学的比较,洞悉它们各自的优劣,从而更好地掌握和灵活应用。
(4)第10章介绍作为软件工程最后一个阶段的软件维护的内容和过程,以及如何提高软件的可维护性,实现软件再工程。
(5)第11章介绍有关软件项目管理的基本要求和内容。该章应该贯穿于整个学习过程中,或置于最初进行学习也可行。只有通过合理的软件项目管理这一平台,才能按时、保质、保量地完成满足用户需求的、高质量的、高可靠性的软件产品。
(6)最后通过软件工程综合训练,配合一定的项目开发过程,真正把所学、所掌握的知识融入到实际项目中去。
按照这样的学习过程,读者通过对两类设计思想的不同以及软件过程的比较,不仅能分析和总结它们各自的优缺点,还能更深入理解相同的软件工程过程结合不同的软件设计思想,对软件分析、实现和维护的影响,对软件质量和管理发展的推动。
由于作者水平有限,疏漏、不妥、错误之处在所难免,恳请读者指正。读者如果对本书有任何意见和建议,欢迎与作者联系:[email protected]。
作 者
于北京理工大学
2015年1月
第1版前言
软件是信息化的核心之一,软件产业展现国家科技发展的核心竞争力,体现国家的综合实力。随着计算机应用的不断普及,互联网应用的不断深入和网络技术的不断发展,使软件系统的规模和复杂度不断增加,如何确保开发出符合用户预期的、质量有保证的软件系统仍然面临巨大挑战,软件危机的障碍仍阻碍软件的发展。
作为计算机科学技术的一个重要分支——软件工程学,成为研究软件需求、开发、维护、管理的普遍原理和技术相结合的、活跃的研究领域。随着软件工程的迅猛发展,新技术、新方法、新工具不断涌现,为读者学习和研究这门学科创造了良好的基础和难得的机遇。
作为软件工程学的入门介绍,本书立足于基本的原理、概念、方法和工具,从实用的角度讲解软件系统需求、设计、实现、测试、维护和管理的内容,同时兼顾对软件工程过程介绍的全面性和系统性。
本书根据作者多年从事“软件工程”课程教学和软件开发的实践经验,在介绍相关理论和过程的基础上,着重讲解软件工程在实践中的方法、技术和工具。本书的特点体现在:
(1)减少软件工程理论的阐述,避免对不同过程和方法的学术讨论。
(2)介绍软件工程理论的基本概念和过程,它们对软件过程实践起着 基石和指导作用。
(3)每章最后对各章的主要内容进行总结,便于读者理解和掌握主要 内容。
(4)鉴于技术人员专注技术而轻文档编写的实际情况,书中介绍了软 件工程各阶段需要编写的文档框架,并通过实例不断强调文档对实施软件工程的重要性。
(5)本书中的主要案例都来自于作者的研究和实际工程项目,让读者 深切感受到书中介绍的理论是如何指导实践的。
本书各章节的安排,是以结构化设计思想为基础,全面介绍软件工程过程各阶段的过程、方法和工具,让读者对软件工程的实施有一个完整、清晰的认识;之后,再以面向对象设计思想为指导,详细介绍基于面向对象的软件工程开发过程。这样编排的目的,是使读者通过对两类设计思想的不同以及软件过程的比较,不仅能分析它们各自的优缺点,还能更深入理解相同的软件工程过程结合不同的软件设计思想,对软件分析、实现和维护的影响,
对软件质量和管理发展的推动。
下面简要介绍本书各章节的概貌,让读者对本书内容有一个提纲挈领的了解。
第1章回顾了软件危机的产生,介绍软件工程的产生和发展,包括软件工程的基本概念、目标和实施原则。通过对软件、软件生命周期和软件过程模型的介绍,让读者对软件工程的基本原理、方法、过程有一个基本认识。
第2章介绍软件需求工程的基本概念、任务和原则,并详细说明结构化分析和建模过程,包括面向数据的数据建模、面向数据流的功能建模和面向状态的行为建模。
第3章介绍软件设计的基本概念、任务和原则,以及目前主流的软件体系结构设计模型,它们分别是以数据为中心的数据仓库模型、客户端/服务器模式的分布式结构模型和层次模型。
第4章从应用角度出发,详细描述了结构化设计的两类设计方法:面向数据流的设计方法和面向数据结构的设计方法,及其它们的设计过程。
第5章从软件工程范畴讨论程序实现和编码,包括程序设计语言的分类、特性、准则及程序编写规范等。
第6章介绍进行软件测试的对象和测试技术。软件测试对象不仅包括源码,还包括设计方案、需求说明等软件工程文档。测试技术主要介绍白盒测试和黑盒测试。
第7章介绍面向对象软件工程的建模基础。UML通过图形化的表示机制,为面向对象分析和设计提供统一的、标准化的视图、图、模型元素和通用机制,以刻画面向对象方法。
第8章介绍面向对象分析的建模过程。面向对象分析模型主要由3种独立模型构成:功能模型、静态模型和动态模型。该章详细说明作为建模基础的静态模型的5个层次。
第9章介绍把面向对象分析阶段得到的需求模型转换为符合用户功能、性能,便于与某种面向对象程序设计语言编程的系统实现方案。
第10章介绍作为软件工程最后一个阶段的软件维护的内容和过程,以及如何提高软件的可维护性和实现软件再工程。
第11章介绍有关软件项目管理的基本要求和内容。通过对软件项目的估算、项目进度管理、风险管理、质量管理、配置管理等内容的介绍,明确只有对软件工程实行全过程的计划、组织和控制等一系列活动,才能得到符合用户需求的、高质量且高可靠性的软件产品。
由于作者水平有限,疏漏、欠妥、谬误之处在所难免,恳请读者指正。读者如果对本书有任何意见和建议,欢迎和作者联系:[email protected]。
作 者
于北京理工大学
2012年1月
软 件 实 现
软件实现是把软件设计的结果“翻译”成某种程序设计语言,并通过软件测试后,能正确运行且得到符合结果的程序。软件实现包括编码、测试、调测、优化等一系列工作。鉴于软件测试本身是一个较大的主题,因此将在第6章专门介绍。
本章内容主要涉及程序设计语言,但不具体介绍如何编写程序,而是从软件工程的角度在更广泛的范围来讨论程序及编码,包括程序设计语言的分类、特性、准则及程序编写规范等内容。
编码实现设计的过程,编码质量的好坏,将直接导致用户体验和软件维护。
5.1 程序设计语言
程序设计语言是机器按照人的指令完成相应任务的工具。遗憾的是,人类使用的自然语言,计算机目前还不能完全识别和理解,因而人们设计出人与机器都能理解的结构化语言。由于应用领域和设计思想的千差万别,不同的结构化语言有很大差别,因而不同的结构化语言在体现人的设计过程和实现的方式上各有千秋,对代码的编写、测试、修改、维护等都产生了深远的影响。
5.1.1 程序设计语言的分类
从程序设计语言出现、发展至今,出现了数百种不同的程序设计语言。特别是20世纪60年代以后,程序设计语言随着软件工程思想的不断发展,经历了从低级到高级、从简单到复杂、从非结构化到结构化程序设计再到面向对象程序设计的发展过程。因此,从软件工程角度来看,同时结合程序设计语言的发展历史,可以将程序设计语言大致分为如下四个阶段。
1. 第一代计算机语言——机器语言
自从有了计算机,就有了计算机语言。不过最早的语言与机器的硬件系统有着紧密联系。由机器指令组成的代码不能随意在不同机器上执行。因为这些机器指令都用二进制编写,指令地址是以绝对地址(物理地址)的形式出现的。此外,二进制的编码方式不仅将绝大多数人挡在计算机程序设计的门外,而且即使是计算机专家(当时也是数学家、逻辑学家)编写的二进制指令也经常出错,且难以改正。
2. 第二代计算机语言——汇编语言
汇编语言也是与系统硬件直接交互的机器语言,但它已经有了一定的符号指令。符号指令增加了对编码的理解,增强了对编码的记忆和使用,降低了程序的出错率,可以提高对程序的修改效率,从而增加程序的可靠性。
第一、二代语言不利于计算机应用的推广,更不具备软件工程中提出的设计、维护等过程,它们已逐渐退出历史舞台。但它们无须计算机对程序语言作更多的“理解”(即无须编译过程),就能执行,且消耗资源少,运算效率高。更重要的是,它们具有现代高级程序设计语言不具备或难以完成的系统操作,因而在一些有特殊要求的领域还有一定的应用。
3. 第三代计算机语言——高级语言
从20世纪60年代后期开始,随着计算机应用从科学计算逐步转向商业应用,直至现在的家庭、个人娱乐、工作和学习,高级程序设计语言逐步得到发展,并走上计算机研发的大舞台。
早期的高级程序设计语言,如ALGOL、FORTRAN、BASIC等,现在看上去它们对应用领域的支持还较弱,但它们已具备高级程序设计语言的基本特征:结构化设计、数据结构的定义和表示、控制逻辑的支持以及与机器硬件的无关性等。
从20世纪80年代开始,面向对象程序设计语言开始崭露头角,C 、Java、VB、C#等高级程序设计语言相继出现,定义类、对象、封装性、继承性、多态性、消息机制等面向对象程序设计技术也不断涌现。它们具有良好的可扩展性、可移植性、可维护性等,为软件质量的提高提供了可靠的工程技术支持。
与这些较为通用的高级程序设计语言相对应的,还有一些专用于某个领域的程序语言。如Lisp和Prolog语言主要应用于人工智能领域的符号推理,Mathlap用于数学工程运算等。专用语言因为有较强的应用针对性,因而有简洁的语法和高效的运算特性,但它们的可移植性、可维护性等较差。
4. 第四代计算机语言——4GL
第四代语言(Fourth Generation Language,4GL)是过程描述语言。相比第三代语言详细定义数据结构和实现过程,4GL只需要数据结构的定义和将要实现的功能,而实现的过程被隐藏起来。最典型的4GL应用是数据库的结构化查询语言(Structure Query Language,SQL)。对数据库的操作只需提供计划要完成的任务命令,而无须考虑实现 过程。
目前4GL得到了一些商业方面的发展,如报表生成、多窗口生成、菜单、工具条等的生成,都无须考虑编码。此外,用形式化定义的结构化需求描述、设计方案等都能通过4GL生成相应代码,并经过人工修改后,得到实际应用。
5.1.2 程序设计语言的特性
不同程序设计语言的特性会影响到整个软件系统的效率和质量。由于不同程序设计语言在语法上和技术上都有一些限制,会影响到设计描述和处理活动,因而要考虑程序设计语言特性对系统实现所带来的影响。
(1)一致性。程序设计语言的一致性是指语言中所用符号的兼容程度,以及对语言用法规定的限制程度。如在FORTRAN语言中,括号可以用作下标标记、表达式优先 级、子程序的参数表分隔符等。这样“一个符号,多种用途”的表示方法容易出错。但在面向对象程序设计中,由于引入了重载的概念,使运算符可以有其他含义。这是为了使自定义类与原有数据类型保持操作上的一致,让使用者在调用函数时有记忆的一致性。
(2)二义性。程序设计语言的二义性是指符合语言语法定义的语句,却出现了多种理解方式,而计算机只能用机械方式理解其中的一类,因而出现二义性。如对语句 Y=X 与Y = X,人们理解上就会产生错误。在面向对象程序中,由于提供了运算符重载机制,因而也会有符号的多重理解。但这不会造成理解上的混乱,因为重载是需要用户自定义才能实现的。用户自己定义的部分,对其理解不会出现二义性。
(3)局部性。局部性是指程序设计语言对模块化支持的程度。在程序设计语言中,对模块的定义提供了语法,如函数定义,以及用大括号“{ }”或“begin…end”描述语句的一个片段。通过这些符号,支持结构化编程,支持各类数据结构在有效范围内使用,体现了信息隐藏特征。
(4)易编码性。程序设计语言是要将设计方案转换为代码。采用的设计方案应支持对复杂数据结构表示、文件操作的便利、类对象的定义以及对常用算法、常用数学计算能力等的操作,便于将设计转换为代码,更好地体现设计者的思路。
(5)可移植性。随着软件工程的发展,技术的更新及网络的日益普及,软件系统全球开发已成为现实,并将成为软件研发的趋势。因此,对源代码跨平台的支持,逐渐成为优先考虑的问题。国际标准化组织(ISO)、美国国家标准协会(ANSI)和国际电子电气工程师协会(IEEE)不断修订代码标准,以促进代码的可移植性。但由于种种原因,如技术要求、企业知识产权保护、商业考虑等,各软件公司的编译器在支持代码的可移植性上都存在着不足。
(6)可维护性。没有不需要维护的软件系统。无论软件过程管理如何及时、有效,最终都要定位在代码的修改和完善上。因此,代码在变量命名(支持长字符串)、自动缩进排版等要求上,都要支持可维护性特征。
(7)配套的开发工具。优秀的开发支撑环境,不仅便于良好的代码编写,而且为语法纠错、测试、调试、多文件组合、代码库建设、代码的逆向工程等提供强大功能。
5.1.3 选择程序设计语言
程序设计语言的选择不是在编码时才选择。早在软件设计前就必须确定选择何种语言。从技术上说,只有提前确定程序语言,才能更好地支持设计的思路,才能更好地展现设计方案。从经济和法律上说,功能越强大的开发平台,其成本也较大。因而应选用与当前设计相符的软件开发工具,并避免由此发生的法律风险。
不同的程序语言机制,对设计的支持不尽相同,目前被广泛采用的是结构化程序设计语言和面向对象语言。
1. 结构化程序设计语言机制
结构化程序设计语言的机制基本上有如下几项:
(1)数据结构(变量和常量)的显示表示。不同数据结构的定义,会导致算法过程效率的不同。如链表与数组,在对元素的排序、插入、删除和查询的操作就完全不同。结构化语言支持复杂数据结构的定义,并能提供语法纠错。但有的语言,如BASIC,就支持不定义数据类型而直接使用变量,容易造成编码在理解和使用上的混乱。
(2)模块化编程。结构化语言支持模块独立编译。模块包括自身数据结构和算法,数据的输入和输出。它通常具备以下三个部分:
* 接口定义:模块所需数据的输入、输出。
* 模块实现:包括自身数据结构和算法过程。
* 调用方式:以何种方式运行模块。
(3)控制结构。几乎所有的高级程序设计语言都支持顺序、分支(选择)和循环结构。有时为了提高运行效率或技术上的需要,有的语言提供goto语句,以及用if…goto…构成循环结构。对于模块间调用控制,提供模块间相互调用和模块自身的递归调用。递归调用运行效率低,且容易陷入“死循环”调用而无法结束调用过程,但递归调用算法实现简洁。
2. 面向对象程序设计语言机制
面向对象设计语言除了结构化语言所支持的机制之外,还增加了面向对象特征和 机制。
(1)类。局部化设计原则是将数据结构与操作数据结构的行为集中在一起。类就很好地支持了这一原则,并且类的内部结构还提供外部访问类内部的权限(public、protected和private)。类的封装性很好地体现了模块化的信息隐藏原则。
(2)继承性。继承性是使得类自动具有其他类的属性(数据结构)和方法(功能)的机制。通过继承,使得现有系统在原有代码基础上,不加修改或稍作修改,就能实现新的需求。这不仅提高了开发效率,更保证了软件质量。
(3)多态性。多态性是指相同的模块接口定义,却有完全不同的实现过程。这样,使得具有相同语义而算法不同的模块可以共享相同的接口定义,减少调用模块时的理解和记忆负担。如鸟和兽都有“吃”这一行为,但显然它们“吃的方式”不同,因而可以各自定义如下接口:Bird_Eat()与Beast_Eat()。这样定义的结果导致将来扩展有关生物 “吃”的操作时,需要不断增加关于“吃”的新的接口定义。这不仅不利于系统的扩展和维护,而且也给设计和使用带来困难。借助多态性机制,可以把所有关于“吃”的行为统一定义为:Eat(),并借助继承性来实现不同的操作过程,这样就自然地反映了不同生物的“吃”在行为上的差异。
(4)消息机制。消息是实现多态性的重要机制之一。如前所述,鸟与兽关于“吃”都用Eat()来统一定义,如何区别调用两类不同的Eat()呢?关键在于对象。消息(如“吃”)是由对象发送的。如定义“鸟”的对象“麻雀”,则“麻雀”发送出“吃”的请求,显然应该调用“鸟”类中定义的“吃”的行为,而不会错误的调用“兽”类中定义的“吃”的行为。由此可以得出,消息由对象、方法、参数共同构成。此外,更广义的消息结构还包括消息的发送者、接收者和消息编号等。
3. 选择程序语言的准则
了解程序语言各自不同的机制,结合软件设计方案的要求,综合考虑可测性、维护性,程序语言开发环境的支撑,以及开发过程的管理和成本等问题,使得理想的程序语言选择标准有时是困难的。因此,结合实际的可操作性及实用性,应考虑以下程序设计程序语言选择的准则:
(1)工程项目规模。程序语言是用于实现工程的。工程规模的大小,需要程序语言结构的灵活性支持。因为项目规模越大,其不可预测性的因素也越多,因而需要程序语言在修改性、适应性、灵活性等方面给予更大支持。
(2)用户需求。一是用户需求的易变性;二是软件维护中用户的参与性。如果用户参与到开发、维护过程中,则应听取用户对程序语言选择的意见。
(3)开发和维护成本。这与程序语言及程序语言开发环境都密切相关。程序语言开发环境自身也是软件系统,也需要维护和技术支持。这些都将构成项目成本。
(4)编程人员对程序语言的熟悉程度。选择编程人员熟悉的程序语言,不仅开发效率高,而且也能保证软件质量。
(5)项目的领域背景。有一些应用领域(如工程计算),有本领域专用程序设计语言。这样,使得所选语言不仅有针对性,还能提高开发效率。即使采用通用程序语言,也要与应用领域相结合,并进一步考虑该领域将来的发展情形。
5.2 程序设计风格
根据软件工程观点,在选定程序语言,完成设计方案后,程序设计的风格在很大程度上影响程序的可理解性、可测试性和可维护性。程序设计风格是指在程序设计过程中,设计人员所表现出来的编程习惯、编程特点和逻辑思维。
从软件工程发展中人们认识到,程序的阅读过程是软件实现、测试和维护过程中一个重要组成部分。因此,一个良好的程序设计风格,是在不影响程序功能、性能前提下,系统地、有效地组织程序,增强代码的易读性、易理解性。
5.2.1 程序编排和组织的准则
源代码按照一定准则编排,使得逻辑清晰、易读易懂,这已成为良好程序设计的标准。程序的编排和组织,将按照源程序文件、数据说明、语句结构和输入输出来综合 体现。
1. 源程序文件
源程序文件中包含了标识符命名、注释以及排版格式。
(1)标识符命名。标识符包括常量、变量、函数名称、宏定义。这些符号命名除了遵循语言自身规定的语法外,还应尽量做到:
* 以具有实际意义的词或短语命名,使读者能望文生义。如求和用Sum,表示是否有效用isValid。函数的命名最好能体现函数功能,如从XML文件中获得记录,可以命名为GetRecordFromXML(string strXMLFileName)。这比用Record()、GetRecord()语义更明确,并能增加代码的可读性。
* 命名方式在整个程序文件中做到统一规范。一是统一用英文或汉语拼音命名;二是分类命名。如与文件操作有关的函数,可加上file作为前缀标识符;与字符串操作有关的标识符,可以加上string或str作为前缀标识符;三是尽量使用领域词汇或用户的习惯用语。
(2)代码中的注释。注释不是程序代码,但却起着正确、有效理解代码的关键作用。注释允许用自然语言来编写,书写内容要言简意赅,无须冗长。对于代码中的注释,主要包括:
* 程序文件整体的叙述,简述本文件所定义的内容。
* 程序主要的数据结构、常量、静态量、枚举量的定义说明。
* 函数接口说明,包括函数参数、返回类型、简要功能描述及代码编写者、编写 日期。
【例5.1】 下面是一个用C#语言编写的函数接口说明的实例,“///”是C#语言注释的XML表示。
///
///
/// 键(关键词)
/// 值(权重)
/// true:正确查找到键,并给出对应的值;false:键不存在
public bool TryGetValue(string strKey, out double Value)
{
for (int i = 0; i < m_iCount; i )
{
if (strKey == m_KeyWeightSet[i].m_strKey) // 查找成功
{
Value = m_KeyWeightSet[i].m_dWeight;
return true;
}
}
Value = 0;
return false; // 查找失败
}
(3)编排格式。代码的编排是在不影响程序功能和性能的前提下,加入换行、空行、空格等内容,使得源代码富有层次感,更易阅读和理解。下面是用C#语言编写的不同排版风格的相同代码,读者自能体会其优劣。
【例5.2】 不同编排风格的相同代码,对程序可理解性的影响。
代码一:没有层次感的代码
///
///
/// 输入将要进行排序的数组
/// 数组大小
void BubbleSort(int a[], int size)
{
for (i=size-1; i>=1; i–)
{
for (j=0; j if (a[j] > a[j 1])
{
t = a[j];
a[j] = a[j 1];
a[j 1]= t;
break;}}}
代码二:有层次感的代码
///
///
/// 输入将要进行排序的数组
/// 数组大小
void BubbleSort(int a[], int size)
{
for (i=size-1; i>=1; i–)
{
for (j=0; j if (a[j] > a[j 1])
{
t = a[j];
a[j] = a[j 1];
a[j 1]= t;
break;
}
}
}
2. 数据说明
作为加深对程序代码理解的重要手段之一,数据说明是首要工作,尤其对必要数据的说明显得更为重要。
(1)变量和常量的命名应遵循匈牙利命名法。即在阅读代码过程中通过变量名称,不仅知道变量的含义,还能知道变量类型。如标识符m_iStackSize,该变量表示栈的容量大小(整型),以及它是类的成员变量。简单地说,匈牙利命名法就是将标识符的命名规范为:“数据类型+标识符”。在数据类型中,各种前缀及含义如下:i表示整型,f表示单精度浮点型,d表示双精度浮点型,b表示布尔型,p表示指针,const表示常量,ch表示字符型,str表示字符串。struct表示结构型,C表示类类型。
(2)对于复杂的数据结构,以及模块中操作的文件类型,首先对数据结构的整体进行说明,再对复杂结构中的各数据进行说明,做到整体结构、主要数据都应有注释。
【例5.3】 用类模板实现保存不同数据类型元素的数组,并内嵌迭代器访问数组元素。下面的代码给出了该类模板的接口定义以及注释。
//定义数组类模板,实现保存不同数据类型元素,并内嵌迭代器来访问数组元素
template
class Array {
public:
Array( unsigned sz ); // 类模板的构造函数,并设定能保存的数组元素个数
~Array();
T& operator[ ]( unsigned i );
Array& operator=(const Array&);
friend ostream& operator<& arr);
class iterator; // 向前引用申明
friend iterator; // 友元类申明,用于类中成员函数对外部类私有数据的直接访问
// iterator是一个嵌套类,用于指向Array类中的一个元素
class iterator
{
public:
// 构造函数,参数isEnd确定迭代器的起始位置是数组的首个还是最后一个元素
iterator(Array& arr, bool isEnd = false);
T* operator (int); // 迭代器指向下一个元素
bool operator T& operator*(); // 获取迭代器指向的当前数组元素
private:
T* p; // 指向当前数组元素的指针
};
iterator Begin(); // 获取当前数组元素的起始位置
iterator End(); // 获取数组最后一个元素的位置
private:
T * values; // 数组元素列表
unsigned size; // 数组容量
};
(3)变量定义尽可能与变量的使用物理地组织在一起,便于查阅和增强理解,正如例5.3所示,将类及相关接口、结构放在一起定义。
3. 语句结构的处理
程序语句的组织,以行为单位。语句的结构除了特殊性能要求之外,应该力求表达简单、直接。可能产生歧义的语句都应重写或拆分成多条语句,以使其语义明晰。
(1)每行语句只表达一个语义信息,如赋值、运算、函数调用、判断等。不要同时具有多个表达式。如下代码:
a=0;
push(stack,a );
由于编译器在函数参数入栈、表达式求值的顺序上会略有不同。因此,对于上述的push语句,入栈时参数是自左向右还是自右向左,会造成a的值是0还是1的混乱。这样,对于不同编译器的不同“翻译”,不仅带来理解上的歧义,也给软件测试和维护带来 困扰。
(2)现阶段对编码的标准,已是可理解性第一、效率第二。随着硬件存储空间的不断扩大,运算速度越来越快,而硬件成本却又在不断下降,因此,牺牲较少的效率或通过提升硬件性能换来代码可理解性的增加是值得的。看下面代码:
void swap(int x, int y)
{
x = x y;
y = x – y;
x = x – y;
}
这段代码所实现的功能难以理解(实现两个整数互换),甚至还需要纸和笔进行演算才能帮助理解。同样的功能,换成如下形式的代码:
void swap(int x, int y)
{
int t = x;
x = y;
y = t;
}
上述代码仅增加一个整型的临时变量,但对两个整数交换的过程一目了然。
(3)尽量使用开发环境提供的各种类库、函数库、中间件等,以减少出错。
4. 输入输出设计准则
输入输出的信息和操作直接面对用户。它不仅给出数据运算的结果,还给出系统在运行过程中的一些有用提示,甚至需要与用户交互才能完成任务。因而对输入输出的设计应该做到:
(1)输入输出的格式在整个系统中应该统一。
(2)对用户的输入要进行必要的限制和检查,使整个系统能得到有效控制。
(3)对输入数据应该有必要的缺省值。
(4)给用户输出的反馈信息要及时、准确。
(5)对输出的信息要有解释、说明。
(6)异常引发的系统问题,需要有数据恢复机制和用户选择操作。
5.2.2 程序设计的效率
首先需要说明的是,强调程序编码的编排是为了增加可阅读性和理解性,甚至可以牺牲部分效率。但这并不意味着不考虑算法效率问题。
1. 设计逻辑结构清晰、高效的算法是提高程序设计效率的关键。
下面通过对已排序的数组进行关键词检索,来看算法效率对程序设计效率的影响。
算法一:顺序检索关键词
///
///
/// 输入将要进行排序的数组
/// 数组大小
/// 待检索的关键词
/// 如果检索成功,则返回关键词对应的位置,否则返回-1
int OrderRetrievd(int a[], int Size, int Key)
{
for (int i = 0; i < Size; i )
{
if (a[i] == Key) return i; // 检索成功
}
return -1; // 检索失败
}
算法二:折半法检索关键词
///
///
/// 输入将要进行排序的数组
/// 数组大小
/// 待检索的关键词
/// 如果检索成功,则返回关键词对应的位置,否则返回-1
int DichotomyRetrievd(int a[], int Size, int Key)
{
int low = 0, high = Size- 1; mid;
while (low <= high)
{
mid = (low high)/2;
if (a[mid] == key) return mid; // 检索成功
if (a[mid] > key)
high = mid – 1;
else
low = mid 1;
}
return -1; // 检索失败
}
直观上看,算法一比算法二要简单、易懂。但很容易得出它们的算法效率却大不同。算法一是线性检索,因此它的时间复杂度为O(Size),算法二是用折半法检索,因此它的时间复杂度是O(log2Size),效率明显提高,特别是当Size特别大时,算法效率更为明显。
假设Size=100 000,由于Size是216< 100 000 < 217,用折半法查找,最多查找17
评论
还没有评论。