type
status
date
slug
summary
tags
category
icon
password
😀
文章前言:本文主要讲述软件设计基础。
 

软件设计的目标和任务

  • 体系结构设计定义软件主要结构性元素之间的关系。体系结构设计表示可以从系统规约、分析模型以及分析模型中所定义子系统的交互导出。
  • 数据设计将分析阶段创建的信息模型转变成实现软件所需的数据结构。
  • 接口设计描述软件内部模块之间以及软件与人之间是如何通信的。数据和制流图提供了接口设计所需的信息。
  • 构件级设计将软件体系结构的结构性元素转变成对软件构件的过程性描述,即描述软件构件的详细内部设计细节。

软件设计基本概念

1 模块

模块是指这样一组程序语句,它包括输入、输出和逻辑处理功能、内部信息及其运行计划。
模块指可单独命名且可通过名字访问的过程函数、子程序或宏调用。
模块具有以下几种特征:
  • 接口,模块的输入输出;
  • 功能,指模块实现什么功能,有什么作用;
  • 逻辑,描述模块内部如何实现需求及所需数据;
  • 状态,该模块的运行环境,模块间调用与被调用关系。
 

2 模块化

模块化就是将程序划分成若干个独立的模块,每个模块完成一个特定子功能,每个模块既是相对独立的,又是相互联系的,它们共同完成系统指定的各项功能。
  • 模块化的目的是为了降低软件的复杂性。
notion image
 

3 抽象

抽象是指从一些事物中抽取其本质的共同的特性,而忽略其非本质细节的差异。
  • 在抽象的最高层次使用问题环境的语言,在较低层次上使用更过程化的方法,把面向问题的术语和面向实现的术语结合起来描述问题的解法。
抽象化的实现方法:自顶向下,逐步求精
  • 逐步求精是一种先总体、后局部的思维原则,也就是一种逐层分解、分而治之的方法。
 

4 信息隐藏

信息隐蔽是在设计和确定模块时,使得一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说,是不能访问的。
应用这一思想,在软件开发中先后出现了:
  • “数据封装”(Data Encapsulation,指在一个模块中包含一个数据结构和在此数据结构上执行的操作);
  • “抽象数据类型”(Abstract Datatype,指一种数据类型,其中可包含在该类型实例上执行的操作)等设计方法;
  • 在OOD中进一步发展为具有“继承”特性的“类”(可以看成是一个支持继承的抽象数据类型)和“对象”。
模块独立性(Module Independence)概括了把软件划分为模块时要遵守的准则,也是判断模块构造是不是合理的标准,同时也是模块化和抽象及信息隐藏概念的直接产物。
坚持模块的独立性,一般认为是获得良好设计的关键。
第一,有效的模块化(即具有独立的模块)软件比较容易开发出来。
第二,独立模块比较容易测试和维护。
独立性可以从两个方面来度量,即模块本身的内聚(Cohesion)和模块之间的耦合(Coupling)。
 

5 内聚

这是从功能角度对模块内部聚合能力的量度。按照由弱到强的顺序,Myers把模块内部聚合能力分为7类。
内聚是模块功能强度(一个模块内部各个元素彼此结合的紧密程度)的度量。
notion image
  • 偶然性内聚:块内各组成成分在功能上是互不相关的。
  • 逻辑性内聚:这种模块把几种相关、相似功能组合在一起,每次被调用时,由传递给模块的参数来确定该模块应完成哪一种功能。
  • 时间性内聚:如果一个模块所包含的任务必须在同一“时间”内完成,则这个模块的块内联系称为时间性内聚。
  • 过程化内聚:当一个模块中包含的一组任务必需按照某一特定的次序执行时,就称为过程性内聚模块。
  • 通信性内聚:模块内部的各个成分都使用同一种输入数据,或者产生同一个输出数据。它们靠公用数据而联系在一起,故称为通信性内聚。
  • 顺序性内聚:如果一个模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行,通常一个处理元素的输出数据作为下一个处理元素的输入数据,则成为顺序性内聚。
  • 功能性内聚:如果一个模块包括仅为完成某一具体任务所必需的所有成分,或者说模块中所有成分结合起来是为了完成一个具体的任务,此模块则为功能强模块。
 

6 耦合

耦合是对软件内部模块之间相互联系的度量。
notion image
  • 非直接耦合:若两个模块没有直接关系,它们之间的联系完全是通过主程序的控制和调用来实现的,便称这两个模块为非直接耦合,这样独立性最强。
  • 数据耦合:若一个模块访问另一个模块,且被访问模块的输入和输出都是数据项参数,则称这两个模块之间的联系为数据耦合。
  • 特征耦合:若两个以上的模块都需要其余某一数据结构的子结构时,不使用其余全局变量的方式而是用记录传递的方式,这样的耦合称为特征耦合。
    • 说明
      notion image
      模块1与模块2为同级模块,相互之间没有信息传递,属于非直接耦合。模块3、4都是模块1的下属模块。模块1调用它们时,可通过参数表与它们交换数据。如果交换的都是简单变量,便构成数据耦合(如模块1、3之间);如果交换的是数据结构,便构成特征耦合(如模块1与模块4之间)。
      notion image
      房租水电=房租+用水量+用电量
控制耦合:控制耦合是中等强度的耦合。此时在模块间传递的信息不是一般的数据,而是用作控制信号的开关值或标志量(Flag)
notion image
外部耦合:若允许一组模块访问同一个全局变量,可称它们为外部耦合。
notion image
公共耦合:若允许一级模块访问同一个全局性数据结构,则称之为公共耦合。
notion image
内容耦合:最强的一类耦合称为内容耦合。
如果发生下列情形,两个模块之间就发生了内容耦合:
(1)一个模块直接访问另一个模块的内部数据;
(2)一个模块不通过正常入口转到另一模块内部;
(3)两个模块有一部分程序代码重迭(只可能出现在汇编语言中);
(4)一个模块有多个入口。
notion image
 

软件设计绘图

1 程序结构

程序结构表明了程序各个部件(模块)的组织情况,是软件的过程表示。
notion image
 

2 结构图

结构图反映程序中模块之间的层次调用关系和联系:它以特定的符号表示模块、模块间的调用关系和模块间信息的传递。
① 模块:模块用矩形框表示,并用模块的名字标记它。
notion image
② 模块的调用关系和接口:模块之间用单向箭头联结,箭头从调用模块指向被调用模块。
notion image
③ 模块间的信息传递:当一个模块调用另一个模块时,调用模块把数据或控制信息传送给被调用模块,以使被调用模块能够运行。而被调用模块在执行过程中又把它产生的数据或控制信息回送给调用模块。
④ 在模块A的箭头尾部标以一个菱形符号,表示模块A有条件地调用另一个模块B。当一个在调用箭头尾部标以一个弧形符号,表示模块A反复调用模块C和模块D
notion image
  • 程序的系统结构图
notion image
 

软件设计原则

  • 设计过程应该能够预测和评估。一名好的设计者应该考虑以往开发的经验和数据,并根据问题的要求、可用的资源和前文提到的设计概念来做出判断。
  • 设计对于分析模型应该是可跟踪的。因为设计模型的单独一个元素经常会跟踪到多个需求上,所以对设计模型进行跟踪是必要的。
  • 设计应该重视资源重用。系统是使用一系列设计模式构造的,很多模式很可能在以前就遇到过。这些模式应该能够为新的软件设计提供参考和甚至重复使用。设计时间应该投入到表示真正的新思想和集成那些已有模式上面去。
  • 设计应该使最终软件尽可能和现实世界中问题的“相似”,也就是说,软件设计的结构应该(尽可能)模拟问题域的结构。
  • 设计应该表现出一致性和集成性。开发软件必然会是一个集体活动,会有很多合作者一起进行软件开发。我们要尽可能使一项设计整体上看上去像是一个人完成的,那么这就要求它是一致的。在设计工作开始之前,设计小组应该定义风格和格式的规则,如果注意了定义设计构件之间的接口,那么,设计就是和谐的。
  • 设计应该适应扩展和变更。如果不考虑这一点,那么软件设计以及实现的软件就没有修改和适应发展而进行扩展的机会。
  • 设计应该表现出一致性和集成性。开发软件必然会是一个集体活动,会有很多合作者一起进行软件开发。我们要尽可能使一项设计整体上看上去像是一个人完成的,那么这就要求它是一致的。在设计工作开始之前,设计小组应该定义风格和格式的规则,如果注意了定义设计构件之间的接口,那么,设计就是和谐的。
  • 设计应该适应扩展和变更。如果不考虑这一点,那么软件设计以及实现的软件就没有修改和适应发展而进行扩展的机会。
  • 在创建设计时就应该能够评估质量,而不是在事情完成之后。有许多设计概念和设计方法可以帮助设计者评估质量。
  • 应该评审设计以减少概念性(语义性)错误。有时人们在评审设计时,倾向于注重细节,只见树木不见森林。在关注设计模型的语法之前,设计小组应该确保已经检查过设计的主要概念性元素(疏忽、歧义性、不一致性)。
 

软件程序结构的启发式设计准则与优化

1.改进软件结构,降低耦合并提高内聚,以提高模块独立性
设计出软件的初步结构以后,应该审查分析这个结构,通过对模块进行分割、合并和变动调用关系,力求降低耦合、提高内聚,简化模块接口,以及少用全局性数据和控制型信息等。
2.模块规模应该适中
经验表明,一个模块的规模不应过大,模块的总行数应控制在10到100行的范围内,最好为30至60行,能在一张打印纸内容纳下,这样理解和阅读都较方便。当一个模块包含的语句数超过30以后,模块的可理解程度迅速下降。
3.保持适当的扇入和扇出
一个模块的扇入数表明有多少个上级模块直接调用它扇入数越大则共享该模块的上级模块数目越多,能够增加模块的利用率,但是,不能违背模块独立原理单纯追求高扇入数。 扇出数衡量的是被一个模块直接控制的其他模块的数量,扇出数过大意味着模块过分复杂,需要控制和协调过多的下级模块;扇出数过小也不好。 通常扇出数3—4为宜,最好不超过5—7。
4.模块的作用域应该在控制域内
一个模块的控制域,是模块本身及其所有从属以及最终的从属模块(即所有可供它调用的下级模块)。
一个模块的作用域,是受这个模块中决策影响的其他模块。只要模块中含有一些依赖于这个判定的操作,这个模块就在这个判定的作用范围之内。
5.降低模块接口的复杂程度冗余并提高一致性。
模块接口复杂性是软件出现错误的首要原因,接口应该设计成简单地传递信息并且应该同模块的功能保持一致。
例如,一个求一元二次方程的根的模块定义为:QUAD_ROOT(TBL,X)
其中数组TBL表示方程中的系数,数组X回送求得的根。这种传递信息的方式不便于理解,容易产生错误。可以改为:QUAD_ROOT(A,B,C,ROOT1,ROOT2)
其中A,B,C表示方程的系数,ROOT1,ROOT2是算出的根。这样的接口既简单,又与模块QUAD_ROOT的功能一致。
6.定义功能可以预测的模块,但要避免过分限制性的模块。
当模块可以作为黑盒对待时就是可预测的;也就是说,同样的外部数据可以在不考虑内部处理细节的情况下生成。
7.设计单入口单出口的模块
这条启发式规则警告软件设计师不要使模块间出现内容耦合。当从顶部进入模块并且从底部退出来时,软件是比较容易理解的,因此也是比较容易维护的。
 

设计规格说明书与评审

软件设计说明书通常包括以下内容:
  • 作用范围:描述设计工作的整体范围,其大部分内容来自软件需求说明书。
  • 参考文档:包括现有的软件文档、系统的文档资料、外购产品文档,包括硬件和软件以及技术参考资料。
  • 数据设计:描述数据对象和形成的数据结构、外部文件和数据库结构、内部数据结构等。
  • 体系结构设计:说明从需求模型导出的软件体系结构,包括模块的层次结构。
  • 接口设计:描述人机界面以及人机界面的设计规则,外部数据、系统或设备接口和内部接口及其设计规则。
  • 模块的过程设计:描述每个模块的处理说明、设计语言描述、调用其他模块和内部设计结构等。
  • 需求与模块的相互对照表。
  • 测试准备:包括测试大纲、整体策略、专门的考虑等。
  • 其他:包括设计约束和一些特殊注解等内容。
 
 
💡
有关问题,欢迎您在底部评论区留言,一起交流~
React Native编译原理:文法和语言
Loading...
Koreyoshi
Koreyoshi
一个无可救药的乐观主义者
Latest posts
DeepSeek本地部署
2025-3-7
React Native
2025-3-7
低代码开发平台介绍
2025-3-7
编译原理:文法和语言
2025-3-7
OpenHarmony应用开发准备
2025-3-7
软件工程: 软件设计基础
2025-3-6
Announcement
🎉写给自己的2025心愿🎉
保研
国奖
完善博客
学一门乐器
发表一篇论文
拍摄人生照片
去3个城市旅游
专业课知识视频
拍摄毕业季视频
----- 2025 ------
👏希望我们一起变好👏