软件设计
在软件需求确定后,就进入软件设计阶段。软件设计是软件工程的重要阶段。软件设计的基本目的就是回答“系统应该如何实现”这个问题。软件设计的任务,就是把软件需求规格说明书中规定的功能要素,考虑实际条件,转换为满足软件系统需求的技术方案,为下个阶段的软件实施工作奠定基础。
软件设计概述
软件设计是软件开发过程中决定软件产品质量的关键阶段。在软件设计阶段所做出的决策,将最终决定软件开发能否成功,更重要的是,这些设计决策将决定软件维护的难易程度。
软件设计活动是获取高质量、低耗费、易维护软件最重要的一个环节。其主要目的是绘制软件的蓝图,权衡和比较各种技术和实施方法的利弊,合理分配各种资源,构建软件系统的详细方案和相关模型,指导软件实施工作顺利开展。
软件设计是开发时期的起始阶段,关系到整个软件开发时期的质量。软件开发时期信息流描述了软件设计从软件需求到软件编码,起到承上启下的作用,如图5-1所示。
图5-1 软件开发时期的信息流
软件设计的任务
软件设计的任务是从软件需求说明书出发,根据需求分析阶段确定的功能设计软件系统的整体结构、划分功能模块、确定每个模块的实现算法,形成软件的具体设计方案。软件设计是一种在设计者计划中通过诸如软件如何满足客户的需要,如何才能容易地实现和如何才能方便地扩展功能以适应新的需求等不同角度考虑的创造性活动。
从软件工程的角度,一般将软件设计分为概要设计和详细设计两个阶段,如图5-2所示。根据软件项目的规模和复杂度,概要设计和详细设计既可以合并为软件设计阶段,也可以反复迭代,直至完全实现软件需求内容。
图5-2 软件设计阶段的划分与任务
概要设计
概要设计也称总体设计,从需求分析阶段的工作结果出发,明确可选的技术方案,做好划分软件结构的前期工作,然后划分出组成系统的物理元素,并进行软件体系结构设计、数据设计和用户界面设计。
概要设计的主要参与者有软件分析人员、用户、软件项目管理人员以及相关的技术专家。软件分析人员完成对目标系统的物理方案和最终的软件结构设计;用户参与评价并最终审批系统的物理方案和最终的软件结构;软件项目管理人员参与评价软件分析人员设计的系统物理方案和软件结构,并对软件分析人员的设计工作进行指导;相关的技术专家则主要参与评价软件分析人员设计的系统物理方案以及软件结构。
概要设计的主要任务是完成体系结构设计、数据设计和用户界面设计。
- 体系结构设计。确定各子系统模块间的数据传递和调用关系。在结构化设计主题划分,主要确定类及类间关系。
- 数据设计。数据设计包括数据库、数据文件和全局数据结构的定义。在结构化设计中,通过需求阶段的实体-联系图、数据字典建立数据模型。在面向对象设计中,通过类的抽象与实例化,以及类的永久存储设计,完成数据设计过程。
- 用户界面设计。包括与系统交互的人机界面设计,以及模块间、系统与外部系统的接口关系。在结构化设计中,根据数据流条目,定义模块接口、全局的数据结构。在面向对象设计中,定义关联类、接口类、边界类等,既满足人机交互界面数据的统一,又完成类间数据的传递。
详细设计
详细设计的任务是在概要设计的基础上,具体实现各部分的细节,直至系统的所有内容都有足够详细的过程描述,使得编码的任务就是将详细设计的内容“翻译”成程序设计语言。确切地说,详细设计的任务是完成过程设计。
过程设计包括确定软件各模块内部的具体实现过程及局部数据结构。在结构化设计中,模块独立性约束了数据结构与算法相分离的情况,使得二者在设计时务必有局部性,减少外部对二者的影响。在面向对象设计中,类的封装性较好地体现了算法和数据结构的内部性。类的继承性提供了多个类(类家族)共同实现过程设计的机制。
软件设计原则
随着软件开发技术不断进步,一些良好的设计原则不断被提出,并指导软件设计过程,确保软件质量。
(1) 分而治之:分而治之是用于解决大型、复杂程度高的问题时所采用的策略。把大问题划分成若干小问题,把对一个大问题的求解转换为对若干小问题的解答,这样就极大地降低了问题的复杂度。模块化是软件设计实现分而治之思想的技术手段。在结构化设计中,模块可以是函数、过程,甚至是代码片段。在面向对象设计中,类是模块的主要形式。
(2) 重用设计模式:重用是指同一事物不作修改或稍作改动就能多次使用的机制。由于概要设计完成的是系统软件结构,因而重用的内容是软件设计模式。软件设计模式针对一类软件设计的过程和模型,而不是面对一次具体的软件设计。通过重用设计模式,不仅使得软件设计质量得到保证,而且把资源集中于设计中的新流程、新方法中,并在设计时更进一步考虑新流程、新方法在将来的重用。
(3) 可跟踪性:软件设计的任务之一就是确定软件各部分间的关系。因为设计系统结构就是要确定系统各部分、各模块间的相互调用或控制关系,以便在需要修改模块时,能掌握与修改模块有关的其他部分,并正确追溯问题根源。
(4) 灵活性:设计的灵活性主要指设计具有易修改性。修改包括对已有设计的增加、删除、改动等活动。发生修改的原因主要有,用户需求发生变更,设计存在缺陷,设计需要进行优化,设计利用重用。
软件设计灵活性主要通过系统描述问题的抽象来体现。抽象是对事物相同属性或操作的统一描述,具有广泛性。因此,系统设计和设计模式的抽象程度越高,覆盖的范围就越大。如“鸟”对“麻雀”的抽象,既能体现麻雀能飞的特性,也覆盖了其他鸟类的说明。但抽象是一把“双刃剑”,过度的抽象反而会引起理解和设计上的困难。如用“生物”去抽象 “麻雀”实体,则作为马的很多特征将难以在“生物”中定义。
(5) 一致性:一致性在软件设计方法和过程中都得到体现。在软件设计中,界面视图的一致性保证了用户体验和对系统的忠诚度,如 Windows 操作系统的界面,虽历经多个版本的变更,但用户操作方式基本没有改变。用统一的规则和约束规范模块接口定义,确保编码阶段对接口和数据结构的统一操作,减少数据理解上的歧义,使得软件质量得到保证。
(6)可靠性:设计应贯穿于功能设计的各个环节,在满足基本功能的同时要全面考虑影响可靠性的各种因素。在设计实现时,软件架构设计应合理,尽量降低各模块间的耦合性,提高系统容错性,以免单个模块发生故障时会影响到其它模块乃至整个系统。并且在软件开发过程中应遵循开发测试同步进行的原则,实时测试和发现问题,并反复验证,降低风险,提高软件的可靠性。
(7) 可扩展性:软件的设计和实现上确保模块作用边界明晰,使用统一标准的输入输出接口,利于软件相关业务的横向扩展,模块中包含数据类型、子功能和操作界面均独立编译动态库,利于软件相关业务的纵向扩展。
(8) 可维护性:在设计实现时,软件各配置项应提供完备的日志记录(包括过程日志和异常日志等),同时在软件出错时应有明确的异常信息提示。所有的故障状态和信息都应自动记录和存储,便于事后的故障对策分析。
改进软件设计,提高软件质量需要遵循如下原则。
1)模块高独立性
设计出软件的初步结构以后,应该进一步分解或合并模块,力求降低耦合并提高内聚。例如,多个模块公有的一个子功能可以独立定义一个模块,由这些模块调用;有时可以通过分解或合并模块以减少控制信息的传递及对全程数据的引用,并降低接口的复杂程度。
2)模块规模适中
大的模块往往是由于分解不充分,但是进一步分解必须符合问题结构,一般分解后不应该降低模块独立性。过小的模块开销大于有效操作,而且模块数目过多将使系统接口复杂。因此过小的模块有时不值得单独存在,特别是只有一个模块调用它时,通常可以把它合并到上级模块中而不必单独存在。
3)深度、宽度、扇出和扇入适当深度表示软件结构中控制的层数,能够粗略地标志一个系统的大小和复杂程度,如
图5-3所示。它和程序长度之间应该有粗略的对应关系,当然这个对应关系是在一定范围内变化的。如果层数过多,则应该考虑是否有许多管理模块过于简单,需要适当合并。宽度是软件结构内同一个层次上的模块总数的最大值。一般宽度越大系统越复杂。对宽度影响最大的因素是模块的扇出。
图5-3 软件结构有关术语