1.软件构建基础
软件构建基础包括:
- 减少复杂性
- 拥抱变革
- 构建的验证
- 重复使用
- 标准建设
前四个概念既适用于设计,也适用于施工。下面几节定义了这些概念,并描述如何将它们应用到构造中。
1.1.减少复杂性
大多数人在工作记忆中保存复杂结构和信息的能力有限,尤其是长时间记忆。这被证明是影响人们如何向计算机传达意图的主要因素,并导致软件构建中最强大的驱动力之一:最小化复杂性。减少复杂性的需要基本上适用于软件构建的每个方面,并且对软件构建的测试特别重要。
在软件构建中,通过强调代码创建的简单易读而不是聪明来降低复杂性。它是通过使用标准来实现的
(见第1.5节,施工标准),模块化设计(见第3.1节,施工设计),以及许多其他特定的技术(见3.3节,编码)。它还得到了以施工为重点的质量技术的支持(见3.7节,施工质量)。
1.2.拥抱变革
大多数软件会随着时间的推移而变化,对变化的预期会驱动软件构建的许多方面;软件运行环境的变化也会以不同的方式影响软件。
预测变化有助于软件工程师构建可扩展的软件,这意味着他们可以在不破坏底层结构的情况下增强软件产品。
许多特定的技术都支持预测变化(参见3.3节,编码)。
1.3.构建的验证
构建验证意味着以这样一种方式构建软件。即在独立测试和操作活动期间,编写软件的软件工程师以及测试人员和用户可以很容易地发现错误。
支持为验证构建的特定技术包括遵循编码标准以支持代码评审和单元测试,组织代码以支持自动化测试,以及限制使用复杂或难以理解的语言结构,等等。
1.4.重复使用
重用是指使用现有资产来解决不同的问题。在软件构建中,重用的典型资产包括库、模块、组件、源代码和商用现货资产。根据定义良好的、可重复的过程,最好是系统地进行重用。系统重用可以显著提高软件的生产率、质量和成本。
重用有两个密切相关的方面:“创建重用”和“使用重用”。前者意味着创建可重用的软件资产,而后者意味着在构建新的解决方案时重用软件资产。重用常常超越项目的边界,这意味着重用的资产可以在其他项目或组织中构建。
1.5.构建标准
在建设过程中应用外部或内部的开发标准有助于实现项目的效率、质量和成本目标。
具体来说,选择允许的编程语言子集和使用标准是实现更高安全性的重要手段。
直接影响构建问题的标准包括:
- 沟通方法(例如,文档格式和内容的标准)
- 编程语言(例如,像Java和c++)
- 编码标准(例如,命名约定、布局和缩进的标准)
- 平台(例如,操作系统调用接口标准)
- 工具(例如,符 的图解标准,如UML 统一建模语言)
2.构建管理
2.1.生命周期模型中的构建
许多模型被用来开发软件。
从构建的角度来看,有些模型更加线性化——比如瀑布模型和分期交付生命周期模型。这些模型将构建视为只有在完成了重要的先决工作(包括详细的需求工作、广泛的设计工作和详细的计划)之后才会发生的活动。更线性的方法倾向于强调构建之前的活动(需求和设计),并在活动之间创建更明显的分离。在这些模型中,构建的主要重点可能是编码。
其他模型的迭代性更强,比如演化原型和敏捷开发。这些方法倾向于将构建视为与其他软件开发活动(包括需求、设计和计划)同时发生的活动,或者与它们重叠的活动。这些方法倾向于混合设计、编码和测试活动,并且它们经常将活动的组合视为构建(参见软件管理和软件过程知识域)。
因此,所谓的“构建”在某种程度上取决于所使用的生命周期模型。一般来说,软件构建主要是编码和调试,但它也涉及构建规划、详细设计、单元测试、集成测试和其他活动。
2.2.构建规划
构建方法的选择是施工规划活动的一个关键方面。构建方法的选择影响到施工先决条件的执行程度、执行顺序以及在施工工作开始前应完成的程度。
构建的方法会影响项目团队减少复杂性、预期变更以及为验证构建的能力。这些目标中的每一个也可以在过程、需求和设计层次上进行处理——但是它们将受到构造方法的选择的影响。
构建规划还定义了组件创建和集成的顺序、集成策略(例如,分阶段的或增量的集成)、软件质量管理过程、对特定软件工程师的任务分配,以及根据所选择的方法完成的其他任务。
2.3.构建测量
许多构建活动和工件都可以度量——包括开发的代码、修改的代码、重用的代码、销毁的代码、代码复杂性、代码检查统计数据、故障修复和故障发现率、工作量和调度。这些度量对于管理施工、确保施工期间的质量、以及改进施工过程等用途是有用的(参见软件工程过程)
3.现实问题
构建过程中软件工程师必须精确的处理,有时混乱不堪和不断变化的来自于现实世界的问题。
由于现实世界约束的影响,构建比其他一些知识域更受实际考虑的驱动,而软件工程在构建活动中可能是最像工艺的。
3.1.构建设计
一些项目将相当多的设计活动分配到构建过程中,而另一些项目将设计分离到单独的阶段。不管分配的方式是什么,一些详细的设计工作将在构建过程中发生,而设计工作往往是由软件正在处理的现实世界问题所施加的约束所决定的。
正如构建物理结构的建筑工人必须进行小规模的修改,以弥补建造者计划中未预料到的缺口。同样的软件建筑工人也必须在较小或较大的规模上进行修改,以充实构建过程中的软件设计细节。
构建级设计活动的细节本质上与软件设计知识域中描述的相同,但它们应用于更具体的算法、数据结构和接口。
3.2.构建语言
构造语言包括人类可以为一个问题,指定一个可执行问题解决方案的所有通信形式。构造语言及其实现
(例如,编译器)可以影响软件的性能、可靠性、可移植性等质量属性。它们可能是安全漏洞的严重贡献者。
-
最简单的构造语言类型是配置语言,软件工程师可以从一组有限的预定义选项中选择创建新的或定制的软件安装。Windows和Unix操作系统中使用的基于文本的配置文件就是这样的例子,一些程序生成器的菜单风格的选择列表构成了配置语言的另一个例子。
-
工具箱语言用于从工具箱(特定于应用程序的可重用部件的集成集)中的元素构建应用程序;它们比配置语言更复杂。工具箱语言可以显式地定义为应用程序编程语言,或者应用程序可以简单地由工具箱的一组接口隐含。
-
脚本语言是常用的应用程序编程语言。在一些脚本语言中,脚本被称为批处理文件或宏。
-
编程语言是最灵活的构造语言类型。它们也包含关于特定应用领域和开发过程的最少信息——因此,它们需要最多的培训和技能才能有效使用。编程语言的选择对在编码过程中引入漏洞的可能性有很大的影响——例如,不加批判地使用C和c++从安全的角度来看是值得怀疑的选择。
编程语言有三种常用的符 ,即:- 语言(C/C++、Java)
语言表示法的特点是使用文本字符串来表示复杂的软件结构。将文本字符串组合成模式可能具有类似句子的语法。如果使用得当,每个这样的字符串都应该具有强大的语义内涵,从而可以立即直观地理解在执行软件构造时将会发生什么。 - 形式化(Event-B)
形式化符 较少地依赖于单词和文本字符串的直观、日常含义,而更多地依赖于由精确、明确和正式(或数学)定义支持的定义。形式构造表示法和形式方法是大多数系统编程表示法形式的语义基础,在这些形式中,准确性、时间行为和可测试性比容易映射到自然语言更重要。形式结构也使用精确定义的符 组合方式,以避免许多自然语言结构的歧义。 - 可视(MatLab)
视觉符 很少依赖于语言和形式构造的文本符 ,相反,它依赖于直接的视觉解释和代表底层软件的视觉实体的放置。可视化构造“复杂”语句只使用显示图标的排列,往往在某种程度上受到制作难度的限制。然而,当主要的编程任务只是为程序构建和“调整”一个可视化界面(程序的详细行为有一个底层定义)时,这些图标可以成为强大的工具。
- 语言(C/C++、Java)
3.3.编码
以下事项适用于软件构建编码活动:
- 创建可理解的源代码的技术,包括命名约定和源代码布局;
- 类、枚举类型、变量、命名常量和其他类似实体的使用;
- 使用控制结构;
- 处理错误条件——包括预期的和异常的(例如输入错误数据);
- 防止代码级安全漏洞(例如,缓冲区溢出或数组索引边界);
- 在连续访问可重用资源时,使用排斥机制和规则;(包括线程和数据库锁)
- 组织源代码(控制语句、程序、类、包或其他结构)
- 代码注释
- 代码调试
3.4.构建测试
构建包括两种测试形式,通常由编写代码的软件工程师执行:
- 单元测试
- 集成测试
构建测试的目的是减少代码中的错误和尽快检测到这些错误,从而减少修复它们的成本。在某些情况下,测试用例是在代码编写之后编写的。在其他情况下,测试用例可能是在编写代码之前创建的。
构造测试通常涉及各种测试类型的子集,在软件测试知识域中有描述。例如,构建测试通常不包括系统测试、alpha测试、beta测试、压力测试、配置测试、可用性测试,或者其他更专业的测试。
关于构建测试已经发布了两个标准:IEEE 829-1998 软件测试文档标准(IEEE Standard for Software Test Documentation)和IEEE1008-1987 软件单元测试标准(IEEE Standard
for Software Unit Testing)。
(更为全面的测试资料请参见软件测试知识域2.1.1单元测试和2.1.2集成测试部分)
3.5.创建重用
从广泛的、多系统的角度来看,为重用而构建的软件可以在将来被重用到当前项目或其他项目中。 用于重用的构造通常基于可变性分析和设计。为了避免代码克隆的问题,需要将可重用的代码片段封装到结构良好的库或组件中。
在编码和测试期间,与软件构建相关的任务如下:
-
通过参数化、条件编译、设计模式等机制实现可变性。
-
可变性封装,使软件资产易于配置和定制。
-
测试可重用软件资产提供的可变性。
-
可重用资产的发布。
3.6.引用重用
使用重用构建意味着使用现有软件资产的重用来创建新的软件。最流行的重用方法是重用语言、平台、所用工具或组织存储库提供的库中的代码。除此之外,今天开发的应用程序广泛使用了许多开源库。重用和现成软件通常具有与新开发的软件相同或更好的质量要求(例如,安全级别)。
在编码和测试期间,与软件构建和重用相关的任务如下:
-
可重用单元、数据库、测试过程或测试数据的选择。
-
代码或测试可重用性的评估。
-
将可重用软件资产集成到当前软件中。
-
告新代码、测试过程或测试数据的重用信息。
3.7.构建质量
除了由需求和设计导致的缺陷之外,施工期间引入的缺陷可能会导致严重的质量问题——例如安全漏洞。这不仅包括安全功能上的故障,还包括其他地方的故障,这些故障允许绕过此功能实体和其他安全弱点或违规行为。
目前有许多技术可以确保代码在构建时的质量。用于保证施工质量的主要技术包括:
- 单元测试和集成测试(见第3.4节,构建测试)
- 测试优先编程(参见软件测试KA2.2节)
- 使用断言和防御性编程
- 调试
- 检查
- 技术评审,包括安全性评审(见软件质量KA第2.3.2节)
- 静态分析(见软件质量KA第2.3节)
选择的具体技术取决于所构造软件的性质以及执行构造活动的软件工程师的技能。程序员应该了解良好做法和常见漏洞,例如,从公认的常见漏洞功能列表中了解。针对安全漏洞的自动静态代码分析可用于多种通用编程语言,并可用于对安全性要求较高的项目。
构建质量活动的重点与其他质量活动有所不同。构建质量活动关注与代码密切相关的代码和工件,例如详细设计,而不是与代码直接相关的其他工件,如需求、高级设计和计划。
3.8.集成
构建期间的一个关键活动是将单独构建的例程、类、组件和子系统集成到一个系统中。另外,特定的软件系统可能需要与其他软件或硬件系统集成在一起。
与构建集成相关的关注点包括:规划组件集成的顺序,确定需要什么硬件,创建支持软件临时版本的框架,确定在集成组件之前对组件执行的测试和质量工作的程度,以及确定项目中测试软件临时版本的点。
程序可以通过分阶段或增量方法进行集成。分阶段集成,也称为“大爆炸”集成,需要延迟组件软件部分的集成,直到一个版本中要发布的所有部分都完成。增量集成被认为比传统的分阶段集成具有许多优势,例如,更容易定位错误、改进进度监控、更早交付产品和改善客户关系。在增量集成中,开发人员将程序分成小块编写和测试,然后一次将这些部分组合起来。通常需要额外的测试基础设施,如存根、驱动程序和模拟对象,以支持增量集成。通过一次构建和集成一个单元(例如,类或组件),构建过程可以向开发人员和客户提供早期反馈。增量集成的其他优点包括更容易的错误定位、改进的进度监控、更全面的单元测试等等。
4.构建技术
4.1.API设计和使用
应用程序编程接口(API)是导出的签名集合,可供库或框架的用户用于编写应用程序。 除了签名外,API还应始终包含有关程序效果和行为(即其语义)的声明。
API设计应尽量使API易于学习和记忆,代码可读强,不易误用,容易扩展,完整,并保持向后兼容性。对于广泛使用的库或框架来说,API通常比它们的实现使用寿命要长。因此希望API简单明了并保持稳定,以便于客户端应用程序的开发和维护。
API的使用涉及到选择、学习、测试、集成以及可能扩展由库或框架提供的API的过程(参见第3.6节,重用构建)。
4.2.面向对象的运行时问题
面向对象语言支持一系列运行时机制,包括多态性和反射。这些运行时机制提高了面向对象程序的灵活性和适应性。
多态性是一种语言支持通用操作的能力,直到运行时才知道软件将包含什么样的具体对象。 因为程序事先不知道对象的确切类型,所以确切的行为是在运行时确定的(称为动态绑定)。
反射是程序在运行时观察和修改其自身结构和行为的能力。反射允许在运行时检查类、接口、字段和方法,而不必在编译时知道它们的名称。它还允许在运行时实例化新对象,并使用参数化的类名和方法名调用方法。
4.3.参数化和泛型
参数化类型,也称为泛型(Ada,Eiffel)和模板(C++),可以在不指定其使用的所有其他类型的情况下实现类型或类的定义。未指定的类型在使用时作为参数提供。参数化类型提供了在面向对象软件中组合行为的第三种方法(除了类继承和对象组合)。
4.4.断言、契约式设计和防御性编程
断言是放置在程序中的可执行谓词,通常是允许程序运行时检查的例程或宏。断言在高可靠性程序中特别有用。它们使程序员能够更快地清除不匹配的接口假设、修改代码时潜在的错误等等。断言通常在开发时编译到代码中,然后再从代码中编译出来,这样就不会降低性能。
契约式设计是一种开发方法,其中每个例程都包含前置条件和后置条件。当使用前置条件和后置条件时,每个例程或类都被称为与程序的其余部分形成一个契约。此外,契约提供了例程语义的精确规范,从而有助于理解例程的行为。契约设计被认为是提高软件建设质量的重要手段。
防御性编程是指保护程序不被无效输入破坏。 处理无效输入的常用方法包括检查所有输入参数的值以及决定如何处理错误输入。断言通常用于防御性编程以检查输入值。
4.5.错误处理、异常处理和容错
处理错误的方式会影响软件完整与正确性、健壮性和其他非功能属性相关的需求的能力。断言有时用于检查错误。还使用了其他错误处理技术,如返回中性值、替换下一个有效数据、记录警告消息、返回错误代码或关闭软件。
异常用于检测和处理错误或异常事件。异常的基本结构是例程使用throw抛出检测到的异常,异常处理块将在try-catch块中捕获异常。try-catch块可以处理例程中的错误条件,也可以将控制权返回给调用例程。异常处理策略的设计应该遵循一些共同的原则,比如在异常消息中包含导致异常的所有信息,避免空的catch块,知道库代码抛出的异常,或者构建一个集中的异常 告器,以及规范程序对异常的使用。
容错是通过检测错误并在可能的情况下从错误中恢复来提高软件可靠性的技术集合,或者在不可能恢复的情况下控制错误的影响。最常见的容错策略包括备份和重试、使用辅助代码、使用投票算法以及用虚假值替换错误值,这些都将产生良好的效果。
4.6.可执行模型
可执行模型抽象出特定编程语言的细节和有关软件组织的决策。与传统的软件模型不同,用可执行建模语言xUML(executable UML)构建的规范可以在不同的软件环境中进行部署,而无需更改。可执行模型编译器(transformer)可以使用一组关于目标硬件和软件环境的决策将可执行模型转换为实现。因此,构造可执行模型可以看作是构造可执行软件的一种方法。
可执行模型是支持对象管理组(OMG)的模型驱动体系结构(MDA)倡议的一个基础。可执行模型是一种完全指定平台无关模型(PIM)的方法;PIM是一种解决问题的模型,它不依赖于任何实现技术。然后,通过将PIM和它所依赖的平台编织在一起,可以生成一个特定于平台的模型(PSM),它是一个包含实现细节的模型。
4.7.基于状态和表驱动的构建技术
基于状态的编程,或称基于状态机的编程,是一种利用有限状态机来描述程序行为的编程技术。状态机的转换图用于软件开发的所有阶段(规范、实现、调试和文档)。其主要思想是用与工艺过程自动化相同的方式构造计算机程序。基于状态的编程通常与面向对象编程相结合,形成了一种新的复合方法,称为基于状态的面向对象编程。
表驱动方法是一种模式,它使用表来查找信息,而不是使用逻辑语句(如if和case)。在适当的情况下使用,表驱动代码比复杂的逻辑更简单,更容易修改。当使用表驱动方法时,程序员要解决两个问题:在一个或多个表中存储什么信息,以及如何有效地访问表中的信息。
4.8.运行时配置和国际化
为了获得更大的灵活性,通常构造一个程序来支持其变量的后期绑定时间。运行时配置是一种在程序运行时绑定变量值和程序设置的技术,通常是在实时模式下更新和读取配置文件。
国际化是准备一个程序(通常是交互式软件)以支持多个语言环境的技术活动。相应的活动本地化是修改程序以支持特定的本地语言的活动。交互式软件可能包含几十个或数百个提示、状态显示、帮助消息、错误消息等。设计和构造过程应考虑字符串和字符集问题,包括要使用哪个字符集、使用什么类型的字符串、如何在不更改代码的情况下维护字符串,以及将字符串翻译成不同的语言,而对处理代码和用户界面的影响最小。
4.9.基于语法的输入处理
基于语法的输入处理涉及输入令牌流的语法分析或解析。它包括创建表示输入数据的数据结构(称为解析树或语法树)。解析树的有序遍历通常给出刚刚解析的表达式。解析器检查符 表中是否存在填充树的程序员定义的变量。在建立解析树之后,程序使用它作为计算过程的输入。
4.10.并发原语
同步原语是由编程语言或操作系统提供的一种编程抽象,有助于并发和同步。众所周知的并发原语包括信 量、监视器和互斥体。
信 量是一种受保护的变量或抽象数据类型,它提供了一个简单但有用的抽象,用于控制并发编程环境中多个进程或线程对公共资源的访问。
监视器是一种抽象数据类型,它表示一组程序员定义的操作,这些操作以互斥方式执行。监视器包含共享变量的声明以及对这些变量进行操作的过程或函数。monitor构造确保一次只有一个进程在监视器中处于活动状态。
互斥(互斥)是一种同步原语,它一次只允许一个进程或线程独占访问共享资源。
4.11. 中间件
中间件是提供操作系统层以上而应用程序层以下服务的软件的广泛分类。中间件可以为软件组件提供运行时容器,以便在 络上提供消息传递、持久性和透明位置。中间件可以看作是使用中间件的组件之间的连接器。现代面向消息的中间件通常提供企业服务总线(enterpriseservicebus,ESB),它支持多个软件应用程序之间面向服务的交互和通信。
4.12. 分布式软件的构造方法
分布式系统是物理上分离的、可能是异构的计算机系统的集合,这些计算机系统通过 络向用户提供对系统维护的各种资源的访问。分布式软件的构造与传统的软件构造不同,主要有并行性、通信性和容错性等问题。
分布式编程通常分为几个基本体系结构类别之一:客户机服务器、三层体系结构、n层体系结构、分布式对象、松耦合或紧耦合(参见计算基础知识卡第14.3节和软件设计KA第3.2节)。
4.13. 构建异构系统
异构系统由各种不同类型的专用计算单元组成,例如数字信 处理器(dsp)、微控制器和外围处理器。这些计算单元是独立控制并相互通信的。嵌入式系统是典型的异构系统。
异构系统的设计可能需要多种规范语言的组合,以设计系统的不同部分,换句话说,硬件/软件协同设计。关键问题包括多语言验证、协同模拟和接口。
在软硬件协同设计过程中,软件开发和虚拟硬件开发通过逐步分解并行进行。硬件部分通常在现场可编程门阵列(FPGA)或专用集成电路(ASIC)中进行模拟。软件部分被翻译成低级编程语言。
4.14. 性能分析和调整
由体系结构、详细设计决策、数据结构和算法选择决定的代码效率会影响执行速度和大小。性能分析是利用程序执行时收集的信息来调查程序的行为,目的是确定程序中可能需要改进的热点。
代码调优可以提高代码级别的性能,它是以更有效的方式修改正确代码的实践。代码调优通常只涉及影响单个类、单个例程或更常见的几行代码的小规模更改。项目中可以使用多种代码调优技术,包括用于调优逻辑表达式、循环、数据转换、表达式和例程的技术。使用低级语言是改进程序中某些热点的另一种常用技术。
4.15. 平台标准
平台标准使程序员能够开发可移植的应用程序,这些应用程序可以在兼容的环境中执行而无需更改。平台标准通常涉及一组标准服务和api,兼容的平台实现必须实现这些服务和api。平台标准的典型例子是Java 2 Platform Enterprise Edition (J2EE)和POSIX操作系统标准(Portable Operating System Interface),它们代表了一组主要为基于UNIX的操作系统实现的标准。
4.16. 测试优先编程
测试优先编程(也称为测试驱动开发-TDD)是一种流行的开发风格,在编写任何代码之前先编写测试用例。测试优先编程通常可以比传统的编程风格更早地发现缺陷并更容易地纠正它们。此外,编写测试用例首先迫使程序员在编码之前考虑需求和设计,从而更快地暴露需求和设计问题。
5. 软件构建工具
5.1. 开发环境
开发环境或集成开发环境(integrateddevelopmentenvironment,IDE)通过集成一组开发工具为程序员提供软件构建的综合设施。开发环境的选择会影响软件构建的效率和质量。
5.2.GUI生成器
由于当前的GUI应用程序通常遵循事件驱动的样式(其中程序的流程由事件和事件处理决定),GUI构建器工具通常提供代码生成助手,它自动执行事件处理所需的最重复的任务。支持代码将小部件与触发提供应用程序逻辑的函数的传出和传入事件连接起来。
一些现代的ide提供了集成的GUI构建器或GUI构建器插件。还有许多独立的GUI构建器。
5.3. 单元测试工具
单元测试验证软件模块的功能是否独立于其他可单独测试的软件元素(例如,类、例程、组件)。单元测试通常是自动化的。开发人员可以使用单元测试工具和框架来扩展和创建自动化测试环境。通过单元测试工具和框架,开发人员可以将标准编码到测试中,以验证单元在各种数据集下的正确性。每个单独的测试都作为一个对象实现,测试运行程序运行所有测试。在测试执行期间,那些失败的测试用例将被自动标记和 告。
5.4. 分析、性能分析和切片工具
性能分析工具通常用于支持代码调优。最常见的分析工具是性能分析工具。执行分析工具在代码运行时监视代码,并记录每个语句的执行次数或程序在每个语句或执行路径上花费的时间。在代码运行时对其进行评测,可以深入了解程序的工作原理、热点所在以及开发人员应将代码调优工作的重点放在哪里。
程序切片涉及对一组程序语句(即程序切片)的计算,这些语句可能在某个关注点影响指定变量的值,这被称为切片准则。程序切片可用于定位错误源、程序理解和优化分析。使用各种静态程序切片或程序分析工具对程序进行动态切片分析。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!