软件工程学习笔记 Day5 ——设计工程
知识点
1. 软件设计工程概述
概念: 软件设计是把软件需求变换成软件表示的过程。
早期的软件设计: 概要设计、详细设计
-概要设计将需求转换为数据结构、软件体系结构及其接口。
-详细设计或部件级设计将软件体系结构中的结构性元素转换为软件部件的过程性描述,得到软件详细的数据结构和算法。
现在的软件设计: 数据/类设计、软件体系结构设计、接口设计和部件级设计。
1.1 软件设计的任务
使用一种设计方法,软件分析模型中通过数据、功能和行为模型所展示的软件需求信息被传送给设计阶段,产生数据/类设计、体系结构设计、接口设计、部件级设计。
分析模型到软件设计的转化:
(1)数据/类设计
数据/类设计将分析类模型变换成类的实现和软件实现所需要的数据结构。
类、由CRC中定义的数据对象和关系以及数据字典中描述的详细数据内容为数据设计活动提供了基础。
数据结构是数据的各个元素之间逻辑关系的一种表示。
数据结构设计应确定数据的组织、存取方式、相关程度以及信息的不同处理方法。
数据设计的过程:
首先,为在需求分析阶段所确定的数据对象选择逻辑表示,需要对不同结构进行算法分析,以便选择一个最有效的设计方案。
然后,确定对逻辑数据结构所必需的那些操作的程序模块,以便限制或确定各个数据设计决策的影响范围。
无论采取什么样的设计方法,如果数据设计得好,往往能产生很好得软件系统结构,具有很强的模块独立性和较低得程序复杂性。“清晰的数据定义是软件开发成功的关键”。
(2)体系结构设计
体系结构设计定义了软件的整体结构,由软件部件、外部可见的属性和它们之间的关系组成。
体系结构设计表示可以从系统规约、分析模型和分析模型中定义的子系统交互导出。
(3)接口设计
接口设计描述了软件内部、软件和协作系统之间以及软件同人之间的通信方式。体系结构设计为软件工程师提供了程序结构的全局视图,但是就像房子的蓝图一样,如果不画出门、窗、水管、电线和电话线,整个设计将是不完整的。
接口设计主要包括3方面内容:设计软件模块间的接口、设计模块与其他非人的信息生产者和消费者(如外部实体)之间的外部接口以及设计人(用户)与计算机间的人机接口。
外部接口设计起始于对分析模型的每个外部实体的评估。外部实体的数据和控制需求确定下来以后,就可以设计外部接口了。
(4)部件级设计
部件级设计将软件体系结构的结构性元素变换为对软件部件的过程性描述。从类为基础的模型、流模型、行为模型等模型中得到的信息是部件设计的基础。
1.2 软件设计的目标
McGlanghlin给出了在将需求转换为设计时,判断设计好坏的3条特征,也就是软件设计过程的目标:
- 设计必须实现分析模型中描述的所有显示需求,必须满足用户希望的所有隐式需求。
- 设计必须是可读、可理解的,使得将来易于编程、易于测试、易于维护。
- 设计应从实现角度出发,给出与数据、功能、行为相关的软件全貌。
为了达到上述目标,必须建立衡量设计的技术标准。它们包括以下内容: - 设计出来的结构应是分层结构。从而建立软件成分之间的控制
- 设计应当模块化,从逻辑上将软件划分为完成特定功能或子功能的部件。
- 设计应当既包含数据抽象,也包含过程抽象。
- 设计应当建立具有独立功能特征的模块。
- 设计应当建立能够降低模块与外部环境之间复杂连接的接口。
- 设计应能根据软件需求分析获取的信息,建立可驱动、可重复的方法。
1.3 软件设计的过程
软件设计是一个把软件需求变换成软件表示的过程,通常的软件设计过程分为如下6个步骤:
① 制订规范
② 体系结构和接口设计
③数据/类设计
④部件级(过程)设计
⑤编写设计文档
⑥设计评审
2. 软件设计工程原则
2.1 抽象与逐步求精
-
抽象
抽象是在软件设计的规模逐渐增大的情况下,控制复杂性的基本策略。“抽象”是一个心理学概念,要求人们将注意力集中在某一层次上考虑问题,忽略低层次的细节。软件工程过程的每一步都是对较高一级抽象的解进行一次具体化的描述,系统定义阶段把整个软件系统抽象成基于计算机系统的一个组成部分,需求分析阶段是对问题域的抽象,使用问题域中的术语,经过软件体系结构设计、部件级设计,抽象级别一次一次降低,到编码完成后,到达最低级别的抽象。
软件设计中的主要抽象手段有:过程抽象和数据抽象。过程抽象是指任何一个完成明确定义功能的操作都可被使用者当作单个实体看待,尽管这个操作实际上是由一系列更低级的操作来完成的。数据抽象是指定义数据类型和施加于该类型对象的操作,并限定了对象的取值范围,只能通过这些操作修改和观察数据。
-
逐步求精
逐步求精是人类解决复杂问题的基本技术之一,是把问题的求解过程分解成若干步骤或阶段,每一步都比上一更精化,更接近问题的解法。逐步求精和抽象是一对互补的概念。抽象使得设计者能够描述过程和数据而忽略低层的细节,而求精有助于设计者在设计过程中揭示低层的细节。
这两个概念对设计者在设计演化中构造出完整的设计模型都起到了至关重要的作用。
2.2 模块化
-
模块化,即把软件按照规定原则,划分为一个个较小的、相互独立的但又相互关联的部件。
-
模块化实际上是系统分解和抽象的过程。在软件工程中模块是数据说明、可执行语句等程序对象的集合,是单独命名的,并且是可以通过名字来访问的。例如,对象类、构件、过程、函数、子程序、宏等都可作为模块。模块具有名字、参数、功能等外部特征以及完成模块功能的程序代码和模块内部数据等内部特征。
-
模块化的特点:
- 对使用者来说,最感兴趣的是模块的功能和接口,而不必理解模块内部的结构和原理。
- 理想的模块只解决一个问题,每个模块的功能应该明确,使人容易理解,模块之间的联结关系简单,具有独立性。
- 用理想模块构建的系统,容易被人理解,易于编程,易于测试,易于修改和维护。
- 模块化也有助于软件项目的组织管理,一个复杂的大型软件可以由许多程序员分工编写,进而提高了开发效率。
-
如果模块使相互独立的,当模块变得越小,每个模块花费的工作量越低,但当模块数增加时,模块间的联系也随之增加,把这些模块连接起来的工作量也随之增加。
这些特性形成了图示的总成本或工作量曲线:
可以看到,存在一个模块数量的范围M可以导致最小的开发成本,但是,无法确切地预测M。模块的划分和设计还需要遵守其他的设计原则。 -
采用模块化原理使程序错误通常局限在有关的模块及它们之间的接口中,模块化使软件容易调试和测试,有助于提高软件的可靠性;同时变动往往只涉及少数几个模块,从而模块化能够提高软件的可修改性;使软件结构清晰,这样每个模块的内容不仅容易设计,而且容易阅读和理解。
2.3 信息隐藏
-
模块中包含的信息(包括数据和过程)不允许其他不需要这些信息的模块使用。
-
通常有效的模块化可以通过定义一组独立的模块来实现,这些模块相互间的通信仅使用对于实现软件功能来说是必要的信息。这意味着这些独立的模块彼此间仅仅交换那些为了完成系统功能而必须交换的信息,也就是说应该隐藏的不是模块的一切信息,而是模块的实现细节。
-
通过抽象,帮助人们确定组成软件的过程或信息实体,通过信息隐藏,则可定义和实施对模块的过程细节和局部数据结构的存取限制。
-
由于一个软件系统在整个软件生存期内要经过多次修改,所以在划分模块时要采取措施,使得大多数过程和数据对软件的其他部分是隐藏的。这样,在将来修改软件时偶然引入错误所造成的影响就可以局限在一个或几个模块内部,避免影响到软件的其他部分。
2.4 功能独立
-
在设计程序模块时,使得模块实现独立的功能并且与其他模块的接口简单,符合信息隐藏原则,模块间关联和依赖程度尽可能小。功能独立的概念是模块化、抽象概念和信息隐藏的直接结果。
-
对于功能独立的模块来说,由于其功能被分割,且接口简单,因此这种模块易于实现。此外,由于功能独立模块的功能单一,符合信息屏蔽原则,与其他模块的关联和依赖程度小,因此这种模块易于维护,修改代码而引起的副作用小。
-
功能独立已成为衡量模块设计优劣的重要指标
-
独立性可以由两项指标来衡量:内聚度与耦合度。内聚度衡量同一个模块内部的各个元素彼此结合的紧密程度,耦合度衡量不同模块彼此间相互依赖的紧密程度。
- 内聚
内聚(cohesion)是一个模块内部各个元素彼此结合的紧密程度的度量。一个内聚程度高的模块(在理想情况下)应当只做一件事。一般模块的内聚性分为7种类型:
(人们总是希望一个模块的内聚类型向高的方向靠)
(1) 巧合内聚(偶然内聚)
将几个模块中没有明确表现出独立功能的相同程序代码段独立出来建立的模块称为巧合内聚模块。
(2)逻辑内聚
逻辑内聚是指完成一组逻辑相关任务的模块,调用该模块时,由传送给模块的控制型参数来确定该模块应执行哪一种功能。
(3)时间内聚
时间内聚是指一个模块中的所有任务必须在同一时间段内执行。例如初始化模块和终止模块。
(4)过程内聚
过程内聚是指一个模块完成多个任务,这些任务必须按指定的过程执行。
(5)通信内聚
通信内聚是指一个模块内所有处理元素都集中在某个数据结构的一块区域中。
(6)顺序内聚
顺序内聚是指一个模块完成多个功能,这些功能又必须顺序执行。
(7)功能内聚
功能内聚是指一个模块中各个部分都是为完成一项具体功能而协同工作,紧密联系,不可分割。 - 耦合
耦合(coupling)是模块之间的相对独立性(互相连接的紧密程度)的度量。耦合取决于各个模块之间接口的复杂程度、调用模块的方式以及通过接口的信息类型。
一般模块之间可能的耦合方式有7种类型:
(功能独立性比较强的模块应是高内聚低耦合的模块。耦合与内聚都是功能独立性的定性标准,都反映功能独立性的良好程度。但耦合是直接的主导因素,内聚则辅助耦合共同对功能独立性进行衡量)
(1)内容耦合
如果一个模块直接访问另一个模块的内部数据;或者一个模块不通过正常入口转到另一模块内部;或者两个模块有一部分程序代码重叠;或者一个模块有多个入口,则两个模块之间就发生了内容耦合。
(2)公共耦合
若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。
(3)外部耦合
模块间通过如软件之外的环境联结(如I/O将模块耦合到特定的设备、格式、通信协议上)时,称为外部耦合。
(4)控制耦合
如果一个模块传送给另一个模块的参数种包含了控制信息,该控制信息用于控制接受模块中的执行逻辑,则称为控制耦合。
(5)标记耦合
两个模块之间通过参数表传递一个数据结构的一部分(如某一数据结构的子结构),就是标记耦合。
(6)数据耦合
两个模块之间仅通过参数表传递简单数据,则称为数据耦合。
(7)非直接耦合
如果两个模块之间没有直接关系,即它们中的任何一个都不依赖于另一个而能独立工作,这种耦合称为非直接耦合。
3. 软件体系结构设计
Bass提出体系结构重要的3个关键理由:
①方便利益相关人员的交流:绝大多数与软件系统利益相关的人员都可以借助软件体系结构来进行彼此理解、协商、达成一定程度的共识或者相互沟通;
②有利于系统设计的前期决策:它使得人员可以在众多彼此竞争的问题上进行优先级分析,突出了早期设计的前期抉择,这些抉择将对随后的所有软件工程工作产生深远的影响,也对系统作为一个可运行实体的最后成功有深远影响;
建立了一个系统的可传递的抽象:体系结构是一个相对较小的、易于理解掌握的模型,该模型描述了系统如何构成以及其部件如何在一起工作。
3.1 体系结构发展过程
- 单主机结构:通常只支持一个用户操作,不需要考虑多个用户并发操作的问题,结构简单。
- 客户/服务器(C/S)结构:应用程序由客户机和服务器分担;处理请求通常被关系型数据库处理;系统支持模块化开发。
- 浏览器/服务器(B/S)结构:3层/多层计算。
3.2 软件体系结构的风格
每种风格描述一种系统范畴,该范畴包括:
- 一些实现系统所需的功能部件(如数据库、计算机模块)。
- 一组用来连接部件“通信、协调和合作”的“连接件”。
- 定义部件之间怎样整合的系统约束。
- 使设计者能够理解整个系统属性并分析已知属性的语义模型。
普遍运用的一些软件体系结构风格:
-
数据为中心的体系结构
-
数据流风格的体系结构
-
调用和返回风格的体系结构
-
面对对象风格的体系结构
系统部件封装数据和操作数据的方法。部件之间的交互和协调通过消息来传递。 -
层次风格的体系结构
3.3 评估可选的体系结构
对于同一个软件需求,由于各种涉及方法的原理不同,会导致不同的软件结构。如下图所示,可以看出,在主程序/子程序风格体系结构中,同样是划分成4个模块,可以有多种组织结构。
所以建立一些标准来评估采用的体系结构是很重要的。
卡耐基-梅隆大学软件工程研究所(SEI)建立了一套迭代的评价过程来评测软件体系结构的合理性,这种方法称为ATAM(体系结构权衡分析法)。
4. 部件级设计技术
在部件级设计阶段,主要完成如下工作:
① 为每个部件确定采用的算法,选择某种适当的工具表达算法的过程,编写部件的详细过程性描述。
②确定每一部件内部使用的数据结构。
③在部件设计结束时,应该把上述结果写入部件级设计说明书,并且通过复审形成正式文档,作为下一阶段(编码阶段)的工作依据。
4.1 结构化程序设计方法
一种流行的定义:“如果一个程序的代码块仅仅通过顺序、选择和循环这3种基本控制结构进行联结,并且每个代码块只有一个入口和一个出口,则称这个程序是结构化的。”
4.2 图形表示法
-
程序流程图
-
N-S 图(盒图)
-
PAD(problem analysis diagram)
下图是图4.11程序的PAD表示:
4.3 判定表
对图4.11流程图进行改进:
图4.16对应的判定表如下:
4.4 设计性语言PDL
PDL是一种用于描述功能部件的算法设计和处理细节的语言,称为设计性语言。
PDL作为一种用于描述程序逻辑设计的语言,具有以下特点:
5. 设计规约与设计评审
软件设计阶段的主要输出是设计规约。为了确保文档的质量,还必须对设计文档进行评审。
5.1 设计规约
- 软件设计说明的大纲
5.2 设计评审
软件设计设计的最终目标是要取得最佳方案。“最佳”是指在所有候选方案中,就节省开发费用、降低资源消耗、缩短开发时间等条件,选择能够赢得较高的生产率、较高的可靠性和可维护性的方案。
设计评审包括以下内容:
- 可追溯性
- 接口
- 风险
- 实用性
- 技术清晰度
- 可维护性
- 质量
- 各种选择方案
- 限制
- 其他具体问题