1、软件设计及原则,为什么需要设计?,软件设计是后续开发步骤及软件维护工作的基础。如果没有设计,只能建立一个不稳定的系统结构,软件设计任务,从工程管理的角度来看,软件设计分两步完成。 概要设计,将软件需求转化为数据结构和软件的系统结构。 详细设计,即过程设计。通过对结构表示进行细化,得到软件的详细的数据结构和算法,软件设计分类,软件开发过程中软件设计包含: 体系结构设计 数据库设计 UI用户界面设计 数据结构与算法设计我们本讲讨论的是面向对象的体系结构设计应该遵循的原则,了解这些原则可以很好的指导我们如何去设计满足要求软件.,软件设计过程,1.制定规范在进入软件开发阶段之初,首先应为软件开发组制定
2、在设计时应该共同遵守的标准,以便协调组内各成员的工作。包括:,阅读和理解软件需求说明书,确认用户要求能否实现,明确实现的条件,从而确定设计的目标,以及它们的优先顺序根据目标确定最合适的设计方法规定设计文档的编制标准规定编码的信息形式,与硬件,操作系统的接口规约,命名规则,2.软件系统结构的总体设计 基于功能层次结构建立系统。采用某种设计方法,将系统按功能划分成模块的层次结构确定每个模块的功能建立与已确定的软件需求的对应关系确定模块间的调用关系确定模块间的接口评估模块划分的质量,3.处理方式设计 确定为实现系统的功能需求所必需的算法,评估算法的性能 确定为满足系统的性能需求所必需的算法和模块间的
3、控制方式周转时间响应时间吞吐量精度 确定外部信号的接收发送形式,4.数据结构设计 确定软件涉及的文件系统的结构以及数据库的模式、子模式,进行数据完整性和安全性的设计 确定输入,输出文件的详细的数据结构 结合算法设计,确定算法所必需的逻辑数据结构及其操作 确定对逻辑数据结构所必需的那些操作的程序模块(软件包),限制和确定各个数据设计决策的影响范围 若需要与操作系统或调度程序接口所必须的控制表等数据时,确定其详细的数据结构和使用规则 数据的保护性设计防卫性设计:在软件设计中就插入自动检错,报错和纠错的功能,一致性设计:保证软件运行过程中所使用的数据的类型和取值范围不变在并发处理过程中使用封锁和解除
4、封锁机制保持数据不被破坏 冗余性设计:针对同一问题,由两个开发者采用不同的程序设计风格不同的算法设计软件,当两者运行结果之差不在允许范围内时,利用检错系统予以纠正,或使用表决技术决定一个正确结果。,5.可靠性设计 可靠性设计也叫做质量设计 在运行过程中,为了适应环境的变化和用户新的要求,需经常对软件进行改造和修正。在软件开发的一开始就要确定软件可靠性和其它质量指标,考虑相应措施,以使得软件易于修改和易于维护。,6.编写概要设计阶段的文档 概要设计阶段完成时应编写以下文档:概要设计说明书数据库设计说明书用户手册制定初步的测试计划,7.概要设计评审 可追溯性: 确认该设计是否覆盖了所有已确定的软件
5、需求,软件每一成份是否可追溯到某一项需求,不要做无用功 接口: 确认该软件的内部接口与外部接口是否已经明确定义。模块是否满足高内聚和低耦合的要求。模块作用范围是否在其控制范围之内 风险: 确认该设计在现有技术条件下和预算范围内是否能按时实现,实用性: 确认该设计对于需求的解决方案是否实用 技术清晰度: 确认该设计是否以一种易于翻译成代码的形式表达 可维护性: 确认该设计是否考虑了方便未来的维护 质量: 确认该设计是否表现出良好的质量特征,各种选择方案: 看是否考虑过其它方案,比较各种选择方案的标准是什么 限制: 评估对该软件的限制是否现实,是否与需求一致 其它具体问题: 对于文档、可测试性、设
6、计过程等进行评估,在详细设计过程中,需要完成的工作是: 确定软件各个组成部分内的算法以及各部分的内部数据组织 选定某种过程的表达形式来描述各种算法 进行详细设计的评审,详细设计,面向对象设计(OOD),面向对象设计继续做面向对象分析阶段的工作,建立软件的结构。 主要工作分为两个阶段:高层设计类设计,高层设计,高层设计阶段开发系统的结构,即构造应用软件的总体模型。 高层设计阶段标识在计算机环境中进行问题解决工作所需要的概念,并增加了一批需要的类。 这些类包括那些可使应用软件与系统的外部世界交互的类。 此阶段的输出是适合应用要求的类、类间的关系、应用的子系统视图规格说明。,高层设计模型,高层设计的
7、特点,高层设计可以表征为标识和定义模块的过程。 模块可以是一个单个的类,也可以是由一些类组合成的子系统。 定义过程是职责驱动的。 类接口的协议如同“合同” :需方提出的请求必须列在协议表中,供方则必须提供所有协议的服务。,高层设计应遵循的原则,应使得在子系统的各个高层部件之间的通信量达到最小(低耦合) 子系统应当把那些成组的类打包,形成高度的内聚(高内聚) 逻辑功能分组,提供一个一个单元,识别并定位问题事件,类设计,类与具有概念封装的子系统十分类似。 每个子系统都可以被当做一个类来实现,这个类聚集它的部件,提供了一组操作。 类和子系统的结构是正交的,一个单个类的实例可能是不止一个子系统的一部分
8、。 高层设计和类设计这两个阶段是相对封闭的,又是相互连接的。,Coad 与 Yourdon 高层设计方法,Coad与Yourdon在设计阶段中继续采用分析阶段中提到的五个层次。 (1)找出类和对象类和对象层 (2)定义属性属性层 (3)识别结构与关系结构层; (4)确定主题主题层; (5)定义服务服务层。,在设计阶段中,这五个层次用于建立系统的四个组成成份。问题论域部分人机交互部分任务管理部分数据管理部分,Coad 与 Yourdon 高层设计方法,问题论域部分,问题论域部分包括与应用问题直接有关的所有类和对象。 识别和定义这些类和对象的工作在OOA 中已经开始,在OOA阶段得到的有关应用的概
9、念模型描述了我们要解决的问题。 在OOD阶段,应当继续OOA阶段的工作,对在OOA中得到的结果进行改进和增补。,问题论域部分的设计,在OOA阶段得到的概念模型描述要解决的问题 在OOD阶段,继续OOA阶段的工作,对在OOA中得到的结果进行改进和增补。 对OOA模型中的某些类与对象、结构、属性、操作进行组合与分解。 要考虑对时间与空间的折衷、内存管理、开发人员的变更、以及类的调整等。,在做面向对象分析时,分析员往往专注于较高层的类和对象,避免考虑太多较低层的实现细节。 在做面向对象设计时,设计师在找出高层的类和对象时,必须考虑到底需要用到哪些较低层的类和对象。,用户界面设计原则,一致性: 采用一
10、致的术语、一致的步骤和一致的活动。 操作步骤少: 减少敲键和鼠标点取的次数,减少完成某件事所需的下拉菜单的距离。 不要“哑播放”: 每当用户等待系统完成一个活动时,要给出一些反馈信息。,Undo: 在操作出现错误时,要恢复或部分恢复状态。 减少人脑的记忆负担: 不应在一个窗口使用在另一个窗口中记忆或写下的信息;需要人按特定次序记忆的东西应当组织得容易记忆 学习的时间和效果: 提供联机的帮助信息。 趣味性: 尽量采取图形界面,符合人类习惯.,任务管理部分的设计,任务,是进程的别称,是执行一系列活动的一段程序。 当系统中有许多并发行为时,需要依照各个行为的协调和通信关系,划分各种任务,以简化并发行
11、为的设计和编码。 任务管理主要包括任务的选择和调整,它的工作有以下几种。,识别事件驱动任务: 一些负责与硬件设备通信的任务是事件驱动的,也就是说,这种任务可由事件来激发。 识别时钟驱动任务: 以固定的时间间隔激发这种事件,以执行某些处理。某些人机界面、子系统、任务、处理机或与其它系统需要周期性的通信,因此时钟驱动任务应运而生。,识别优先任务和关键任务: 根据处理的优先级别来安排各个任务。识别协调者: 当有三个或更多的任务时,应当增加一个追加任务,起协调者的作用。评审各个任务: 对各任务进行评审,确保它能满足选择任务的工程标准事件驱动?时钟驱动?优先级/关键任务?协调者?,定义各个任务,定义任务
12、的工作主要包括:它是什么任务、如何协调工作及如何通信。(1) 它是什么任务为任务命名,并简要说明这个任务。(2) 如何协调工作定义各个任务如何协调工作。指出它是事件驱动还是时钟驱动。(3) 如何通信定义各个任务之间如何通信。任务从哪里取值,结果送往何方。,一个模版任务的定义如下:Name (任务名)Description (描述) Priority (优先级)Servicesincluded (包含的操作)、Communication Via (经由谁通信)。,数据管理部分的设计,数据管理部分提供了在数据管理系统中存储和检索对象的基本结构,包括对永久性数据的访问和管理。 它分离了数据管理机构所
13、关心的事项,包括文件、关系型DBMS或面向对象DBMS等。,数据管理方法,数据管理方法主要有3种:文件管理提供基本的文件处理能力。关系数据库管理系统关系数据库管理系统使用若干表格来管理数据。 面向对象数据库管理系统,类的设计,应用分析过程包括了对问题论域所需的类的模型化 但在最终实现应用时不只有这些类,还需要追加一些类 在类设计的过程中应当做这些工作。,单一概念的模型 使用多个类来表示一个“概念”。 常常把一个概念进行分解,用一组类来表示这个概念。 也可只用一个单个类来表示一 个概念。 在类的文档中应对类的用途做出清楚的标识和精确的陈述,类的共有界面应当使用操作的特征、先决条件和后置条件加以定
14、义。,类设计的目标,可复用的“插接相容性”部件部件可以在未来的应用中使用。界面的标准化类的“插接相容性” 可靠的部件可靠的(健壮的和正确定义的)部件。每个部件必须经过充分的测试。每个操作尽可能小和作用单一。,可集成的部件 类的界面应当尽可能小 一个类所需要的数据和操作都定义在类定义中 避免命名冲突 封装特性保证了把一个概念的所有细节都组合在一个界面下 信息隐蔽保证了实现级的名字将不会与其它类的名字互相干扰。,类设计的方针,信息隐蔽保护抽象数据类型的存储表示不被抽象数据类型实例的用户直接存取。对其表示的唯一存取途径只能是接口。,消息限制避开直接引用另一个类的数据类A的数据表示中包括了类C的实例,
15、类B的数据表示则直接使用了类C。如果类A的实例发送一个消息给类B的一个实例,则类A必须知道类B的实现是如何使用类C的实例的,并把这种知识包括到它自己的实现中去。当类B需要改变自己的实现,改动类C的数据表示时,类A的实现也必须随之改变。,狭窄界面 不是所有的操作都是公共的。 对于一个HashTable类,界面应包括插入和检索表的操作,而不应包括使用一个表项的关键码计算散列值的操作。散列函数不应由类的实例的用户来访问。它应是一个单独的操作,以便容易调整或改变散列函数,它应是隐蔽实现的部分。,强内聚模块内部各个部分之间应有较强的关系,它们不能分别标识。 弱耦合一个单独模块应尽量不依赖于其它模块。如果
16、: 在类A的实例中建立了类B的实例 类A的操作需要类B的实例做为参数 如果类A是类B的一个派生类则称类A“依赖于”类B。 一个类应当尽可能少地依赖于其它类。,显式信息传递在类之间全局变量的共享隐含了信息的传递,并且是一种依赖形式。因此,两个类之间的交互应当仅涉及显式信息传递。显式信息传递是通过参数表来完成的。借助于显式地列出将要通过参数表传递给一个操作的值,可以循特定的路径来跟踪错误。显式信息传递要最小化,派生类当做派生类型在继承结构中,每个派生类应该当做基类的特殊化来开发,而基类所具有的公共接口成为派生类的共有接口的一个子集。 如果基类是共有的,则其共有接口将成为新的派生类的共有界面部分,这
17、类似于类型与派生类型之间的关系。如果基类是私有的,它的行为将不是派生类的公共行为部分而是实现部分。它的提出是为了提供实现新类的服务。在实现一个新类时通过声明一个类的实例,就可以使得该类的服务有效。,抽象类某些语言提供了一个类,用它做为继承结构的开始点,所有用户定义的类都直接或间接以这个类为基类。 每个继承结构的根类应当是目标概念的一个抽象模型这个抽象模型起始于一个根类,它不产生实例。它定义了一个最小的共有界面,许多派生类可以加到这个界面上以给出概念的一个特定视图。考虑一组涉及 “List” 概念的类,根类应提供一组操作做为接口而不考虑是什么表。这个抽象类可以提供某些操作的缺省实现,但在派生类中
18、将根据特殊化要求给出特定实现。,通过复用设计类,利用既存类来设计类,有4种方式:选择,分解,配置和演变。 选择 设计一个类最简单的服务是从既存部件中简单地选择合乎需要的软件部件。,部件库,一个面向对象开发环境应提供一个常用部件库。 大多数语言环境都带有一个初始部件库,如整数、实数和字符,它是提供其它所有功能的基础层。 任一基本部件库(如“基本数据结构”部件)都应建立在这些原始层上。 这个层还包括一组提供其它应用论域方法的一般类,如窗口系统和图形图元。,一个面向对象部件库的层次,特定组的部件 (一个小组为他们自己组内所有成员使用而开发) 特定项目的部件 (一个小组为某一个项目而开发) 特定问题论
19、域的部件 (购自某一个特定论域的软件销售商) 一般部件 (购自专门提供部件的销售商) 特定语言原操作 (购自一个编译器的销售商),分解最初标识的“类”常常是几个概念的组合。在着手设计时,必须把一个类分成几个类,希望新标识的类容易实现,或它们已经存在。 配置在设计类时,我们可能会要求由既存类的实例提供类的某些特性。通过把相应类的实例声明为新类的属性来配置新类。,一种仿真服务器可能要求使用一个计时器来跟踪服务时间。设计者应当找到计时器类,并在服务器类的定义中声明它。这个服务器还要求有一个队列类的实例来作客户排队工作。对每一个客户的服务时间由一个已知的概率分布来确定,因此,可能使用一个具有泊松分布或
20、具有均匀分布的随机变量的类的实例。,演化 要求开发的新类可能与一个既存类非常类似,但不完全相同。此时,可以利用继承机制。一般化-特殊化处理有三种可能的方式。,软件设计基础,自顶向下,逐步细化软件结构程序结构结构图模块化抽象化信息隐蔽,自顶向下,逐步细化,将软件的体系结构按自顶向下方式,对各个层次的过程细节和数据细节逐层细化,直到用程序设计语言的语句能够实现为止,从而最后确立整个的体系结构。,软件结构,软件结构包括两部分: 程序的模块结构和数据的结构 软件的体系结构通过一个划分过程来完成。该划分过程从需求分析确立的目标系统的模型出发,对整个问题进行分割,使其每个部分用一个或几个软件成份加以解决,
21、整个问题就解决了,程序结构,程序结构表明了程序各个部件(模块)的组织情况,是软件的过程表示。,结构图,结构图反映程序中模块之间的层次调用关系和联系:它以特定的符号表示模块、模块间的调用关系和模块间信息的传递,模块:模块用矩形框表示,并用模块的名字标记它。,模块的调用关系和接口:模块之间用单向箭头联结,箭头从调用模块指向被调用模块。,模块间的信息传递: 当一个模块调用另一个模块时,调用模块把数据或控制信息传送给被调用模块,以使被调用模块能够运行。而被调用模块在执行过程中又把它产生的数据或控制信息回送给调用模块,在模块A的箭头尾部标以一个菱形符号,表示模块A有条件地调用另一个模块B。当一个在调用箭
22、头尾部标以一个弧形符号,表示模块A反复调用模块C和模块D。,程序的系统结构图,模块化,软件系统的模块化是指整个软件被划分成若干单独命名和可编址的部分,称之为模块。这些模块可以被组装起来以满足整个问题的需求。 把问题子问题的分解与软件开发中的系统子系统或系统模块对应起来,就能够把一个大而复杂的软件系统划分成易于理解的比较单纯的模块结构。,抽象化,软件系统进行模块设计时,可有不同的抽象层次。 在最高的抽象层次上,可以使用问题所处环境的语言概括地描述问题的解法。 在较低的抽象层次上,则采用过程化的方法。,过程的抽象 在软件工程中,从系统定义到实现,每进展一步都可以看做是对软件解决方法的抽象化过程的一
23、次细化。 在软件需求分析阶段,用“问题所处环境的为大家所熟悉的术语”来描述软件的解决方法。 在从概要设计到详细设计的过程中,抽象化的层次逐次降低。当产生源程序时到达最低抽象层次。,数据抽象 在不同层次上描述数据对象的细节,定义与该数据对象相关的操作。 数据抽象本身由另外一些数据抽象构成 定义抽象数据类型之后,可引用它来定义其它数据对象,而不必涉及其内部细节,信息隐蔽,信息隐蔽: 每个模块的实现细节对于其它模块来说是隐蔽的。也就是说,模块中所包含的信息(包括数据和过程)不允许其它不需要这些信息的模块使用。,模块的独立性,模块(Module) “模块”,又称“组件”。它一般具有如下三个基本属性:
24、功能:描述该模块实现什么功能 逻辑:描述模块内部怎么做 状态:该模块使用时的环境和条件,在描述一个模块时,还必须按模块的外部特性与内部特性分别描述 模块的外部特性模块的模块名、参数表、其中的输入参数和输出参数,以及给程序以至整个系统造成的影响 模块的内部特性完成其功能的程序代码和仅供该模块内部使用的数据,模块独立性模块独立性, 是指软件系统中每个模块只涉及软件要求的具体的子功能, 而和软件系统中其它的模块的接口是简单的, 例如, 若一个模块只具有单一的功能且与其它模块没有太多的联系, 则称此模块具有模块独立性。一般采用两个准则度量模块独立性。即模块间耦合和模块内聚,耦合是模块之间的互相连接的紧
25、密程度的度量。内聚是模块功能强度(一个模块内部各个元素彼此结合的紧密程度)的度量。模块独立性比较强的模块应是高内聚低耦合的模块。,模块间的耦合,两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。非直接耦合的模块独立性最强。,非直接耦合,数据耦合 (Data Coupling) 一个模块访问另一个模块时,彼此之间是通过简单数据参数 (不是控制参数、公共数据结构或外部变量) 来交换输入、输出信息的。 标记耦合 (Stamp Coupling) 一组模块通过参数表传递记录信息,就是标记耦合。这个记录是某一数据结构的子结构,而不是简单变量。,p0 (x0, y0) p1 (
26、x1, y1),数据耦合 LINE ( x0, y0, x1, y1)标记耦合 LINE ( p0, p1 ),控制耦合 (Control Coupling) 如果一个模 块通过传送开 关、标志、名 字等控制信息, 明显地控制选 择另一模块的 功能,就是控 制耦合。,外部耦合(External Coupling) 一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。 公共耦合(Common Coupling) 若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共
27、覆盖区等。,公共耦合的复杂程度随耦合模块的个数增加而显著增加。若只是两模块间有公共数据环境,则公共耦合有两种情况。松散公共耦合和紧密公共耦合。,内容耦合 (Content Coupling) 下列情形,两个模块之间就发生了内容耦合 (1) 一个模块直接访问另一个模块的内部数据; (2) 一个模块不通过正常入口转到另一模块内部 (3) 一个模块有多个入口。,c,模块内聚,功能内聚(Functional Cohesion) 一个模块中各个部分都是完成某一具体功能必不可少的组成部分,或者说该模块中所有部分都是为了完成一项具体功能而协同工作,紧密联系,不可分割的。则称该模块为功能内聚模块。,信息内聚(
28、Informational Cohesion)这种模块完成多个功能,各个功能都在同一数据结构上操作,每一项功能有一个唯一的入口点。这个模块将根据不同的要求,确定该执行哪一个功能。由于这个模块的所有功能都是基于同一个数据结构(符号表),因此,它是一个信息内聚的模块。,信息内聚模块可以看成是多个功能内聚模块的组合,并且达到信息的隐蔽。即把某个数据结构、资源或设备隐蔽在一个模块内,不为别的模块所知晓。,通信内聚(Communication Cohesion) 如果一个模块内各功能部分都使用了相同的输入数据,或产生了相同的输出数据,则称之为通信内聚模块。通常,通信内聚模块是通过数据流图来定义的。,过程
29、内聚(Procedural Cohesion) 使用流程图做为工具设计程序时,把流程图中的某一部分划出组成模块,就得到过程内聚模块。例如,把流程图中的循环部分、判定部分、计算部分分成三个模块,这三个模块都是过程内聚模块。,时间内聚(Classical Cohesion) 时间内聚又称为经典内聚。这种模块大多为多功能模块,但模块的各个功能的执行与时间有关,通常要求所有功能必须在同一时间段内执行。例如初始化模块和终止模块。,逻辑内聚(Logical Cohesion) 这种模块把几种相关的功能组合在一起,每次被调用时,由传送给模块的判定参数来确定该模块应执行哪一种功能。,巧合内聚(Coincide
30、ntal Cohesion) 巧合内聚 (偶然内聚)。当模块内各部分之间没有联系,或者即使有联系,这种联系也很松散,则称这种模块为巧合内聚模块,它是内聚程度最低的模块。,软件体系结构,软件体系结构的三要素程序构件(模块)的层次结构构件之间交互的方式数据的结构 软件设计的一个目标是建立软件的体系结构表示。将这个表示当作一个框架,从事更详细的设计活动。,在体系结构设计中应保持的性质,结构 体系结构设计定义系统的构件、构件打包的方式和相互交互的方式 附属的功能 体系结构设计描述设计出的体系结构如何实现功能、性能、可靠性、安全性、适应性,以及其它的系统需求。 可复用 体系结构设计应描述为一种可复用的模
31、式,能复用已有构造块,并可供以后类似系统族的设计使用。,过程调用: 在某一特定执行路径中传递执行指针。如普通过程调用、远程过程调用。 数据流: 相互独立的处理通过数据流进行交互,在得到数据的同时被赋予控制权限。如UNIX系统中的管道。 间接激活: 处理因事件的发生而激活,在处理间没有直接交互。例如,事件驱动系统、自动垃圾回收等。 消息传递: 相互独立的处理间有明确交互,通过显式数据传递。传递可以是同步的,也可以是异步的。如TCP/IP。 共享数据: 构件通过同一个数据空间进行并发操作。如多用户数据库, 数据黑板。,构件之间的连接方式,系统结构,体系结构设计的第一步是将系统分解为一系列相互作用的
32、子系统。 在最抽象层次,系统可视为一个块图。图中每个盒子表示一个子系统。 每个盒子内的盒子表明子系统本身还可分解为子系统。 箭头表示一个子系统向另一子系统传送数据或控制。,体系结构块图表示一个系统结构的概貌。软件工程师很容易理解它。 根据各子系统如何共享数据、如何分布、如何相互交互,可开发更加特定的模型。数据仓库模型客户机服务器模型抽象机模型,数据仓库模型,所有共享数据都存放于数据库中, 这些数据可为所有子系统存取。 每个子系统保有各自的数据库。通过传送消息, 可在子系统之间交换数据。 大量的数据围绕一个共享数据库或数据仓库来组织。 这种系统主要适用于控制系统, 信息管理系统, CAD系统,
33、CASE工具集。,集成的CASE工具集的体系结构 以数据仓库为核心,设计分析器,报告生成器,项目数据仓库,设计编辑器,代码生成器,设计翻译器,程序编辑器,客户机 服务器模型,分布式系统模型,表明各种数据和处理如何分布到各个处理器上。 有一组功能各自独立的服务器,为其他子系统提供服务。如打印服务器, 文件服务器, 编译服务器等。 有一组客户机, 他们调用服务器提供的服务。也可能存在一些客户机可并发执行的客户机程序。 有一个网络, 使得客户机能够访问服务器。,Film & Picture library system 的体系结构,Catalogue server,Vodio server,Pict
34、ure server,Hypertext server,Catalogue,Film clip files,Digitised photographs,Hypertext web,Wide - bandwidth network,Client 1,Client 2,Client 3,Client 4,客户机-服务器系统多用于具有多个分布式处理器的网络系统。 它易于增加一个新的服务器并将其与系统的其他集成在一起。系统中服务器的升级对用户是透明的,且不影响系统的其他部分。,抽象机模型,一个体系结构的抽象机模型也称为分层模型 模型提供各个子系统的接口 每一层次提供一组服务,定义一个抽象机 每一个抽象
35、机提供自己的语言,用以实现上一层的抽象机 例如,网络协议的参考模型OSI,OperatingSystem,Database Mana- gement System,Object ManagementSystem,Version Management System,抽象机模型支持系统的增量式开发。具有可变更性和可移植性。 当一个层次开发出来后,就可以为其用户提供有效的某些服务。 如果接口是预定义的,则一个层次可为另一个层次所替换。 若一个层次的接口发生变更,仅相邻层次受到影响 层次系统将机器依赖性局部化到它的内部层次上,控制模型,构造系统模型涉及到一个系统如何分解为子系统 为使系统正常工作,必须
36、对各子系统加以控制,使得它们的服务能够在正确的时刻被导向到正确的地方。 因而,必须按照控制模型组织各子系统,并满足结构模型的要求。 在体系结构层,应考虑子系统之间的控制流。,集中控制模型,在集中控制模型中,将一个子系统设计为系统控制器,其职责是管理其他子系统的执行。 集中控制模型分为两类:控制子系统顺序执行控制子系统并发执行,调用-返回模型,此即熟悉的自顶向下的子程序模型。 控制始于子程序层次的顶部,通过子程序调用,从层次结构较高层的程序向较低层的程序传递控制信息。程序执行结束将向较高层的父程序返回。 这种程序模型仅应用于顺序系统。 该模型可以在模块层使用以控制函数或对象。,在调用-返回系统结
37、构中, 每一层都只与上下相邻的两层通信。 每一层在利用下层基础服务的条件下,为上层提供服务。 这种结构的优点是:提供逐步抽象的编程支持,支持复用及系统升级。缺点是:不是所有的系统都适合于建成层次结构,不能提供最佳性能。,管理者模型,这种模型应用于并发系统。 模型中有一个系统构件被设计为系统管理者,它控制开始、终止,并协调其他系统进程。 一个进程可以是子系统,或者模块,它可以与其他进程并行执行。 这种模型也可应用于顺序系统。管理例程根据某些状态变量的值调用特定的子系统。,事件驱动系统,事件驱动模型是通过外部生成的事件来驱动的。 事件与简单输入间的区别在于事件的时序存在于事件处理进程控制的外部。
38、一个子系统可能需要存取控制信息来处理这些事件,但控制信息通常不确定控制流。广播模型中断驱动模型,广播模型,在这种模型中,一个事件向所有的子系统广播,由能够处理此事件的子系统响应它。 对于在网络中跨越不同计算机分布的集成的子系统,广播模型十分有效。 在广播模型中,子系统存储有关特定事件的信息。一旦事件发生,控制将转移到能够处理该事件的子系统。,所有事件可能会广播给所有的子系统,但会消耗大量的处理开销。 事件处理器通常还支持点对点通信。 实时系统一定是事件驱动的,它要求快速处理外部生成的事件。,事件和消息处理器,子系统 1,子系统 2,子系统 3,子系统 4,模块分解,在设计出一个结构性体系结构后
39、, 下一步就是将子系统分解为模块。 在系统分解和模块分解之间没有严格的区别。 将子系统分解为模块时用到两种模型:面向对象模型数据流模型,面向对象模型,在面向对象模型中,将系统分解为一组对象。对象具有松散耦合和仔细定义的界面,对象的状态是私有的,对象的操作是基于其状态定义的 对象具有诸如封装、隐蔽、继承等良好的特性。对象必须自己维护其数据的一致性。 对象是系统的构件。,这种体系结构的优点是:将具体的实现部分隐蔽在对象中,使得代码之间的独立性很好,有利于将复杂的系统分解为相互操纵的子任务。 缺点是:对象间进行一般的调用时必须知道对方的标识。如果一个对象的标识发生变化,所有显式调用这个对象操作的地方
40、都要修改。对象之间的同步等还缺乏现成的机制。,数据流模型,在数据流模型中,将系统分解为一系列功能模块 这种结构包括批处理和管道及过滤器。 在体系结构中的每一个成份都有一套输入和输出数据,都依输入-处理-输出的方式工作。 进行数据变换的构件叫做过滤器。 把数据从一个过滤器的输出导入到另一个过滤器的输入,就叫做管道。,在系统中,各个过滤器必须是相互独立的,每一个过滤器对它的上游或下游的过滤器的情况是不知道的,也不能做任何假设。 如果要求最终的输出结果与各个过滤器的执行次序相关,就是一个数据流方式的体系结构。 这种结构的优点是:数据流程设计明确,直接支持复用,系统容易维护和升级,可以进行某些性能分析
41、(如流量、死锁等),容易支持并行计算。,在系统结构图中的模块,传入模块 从下属模块取得数据,经过某些处理,再将其传送给上级模块。它传送的数据流叫做逻辑输入数据流。 传出模块 从上级模块获得数据,进行某些处理,再将其传送给下属模块。它传送的数据流叫做逻辑输出数据流。,变换模块 它从上级模块取得数据,进行特定的处理,转换成其它形式,再传送回上级模块。它加工的数据流叫做变换数据流。 协调模块 对所有下属模块进行协调和管理的模块。, 在选择模块设计的次序时,必须对一个模块的全部直接下属模块都设计完成之后,才能转向另一个模块的下层模块的设计。, 在设计下层模块时,应考虑模块的耦合和内聚问题,以提高初始结
42、构图的质量。 使用“黑箱”技术: 在设计当前模块时,先把这个模块的所有下层模块定义成“黑箱”,在设计中利用它们时,暂时不考虑其内部结构和实现。在这一步定义好的“黑箱”,在下一步就可以对它们进行设计和加工。这样,又会导致更多的“黑箱”。最后,全部“黑箱”的内容和结构应完全被确定。, 在模块划分时,一个模块的直接下属模块一般在5个左右。如果直接下属模块超过10个,可设立中间层次。 如果出现了以下情况,就停止模块的功能分解 1.当模块不能再细分为明显的子任务时; 2.当分解成用户提供的模块或程序库的子程序时 3.当模块的界面是输入输出设备传送的信息时 4.当模块不宜再分解得过小时。,设计的后处理,为
43、每一个模块写一份处理说明 为每一个模块提供一份接口说明 确定全局数据结构和局部数据结构 指出所有的设计约束和限制 进行概要设计的评审 进行设计的优化(如果需要和可能的话),过程设计,从软件开发的工程化观点来看,在使用程序设计语言编制程序以前,需要对所采用算法的逻辑关系进行分析,设计出全部必要的过程细节,并给予清晰的表达。这就是过程设计的任务。,在过程设计阶段,要决定各个模块的实现算法,并精确地表达这些算法。表达过程规格说明的工具叫做详细设计工具,它可以分为以下三类:图形工具表格工具语言工具,程序流程图,程序流程图也称为程序框图,程序流程图使用五种基本控制结构是:,程序流程图的标准符号,功能分解
44、,功能分解是非常朴素、普通的思想。然而,也是非常容易遗忘的思想。很多的设计人员总是雄心勃勃,试图设计出非常复杂的算法,非常完美的结构,陶醉于自己定义的世外桃源,不是将问题简化,而是将问题复杂化。而实践证明,这些出发点就是有偏差的。软件领域以外的很多实践和经验,都证明了分工、分解是处理复杂系统的基本前提。何况较很多系统更加复杂的软件系统?,代码重用,很多的软件方案中,开宗明义自己的设计原则是面向对象,那么,我们把需求影射成为一个一个的对象,就是好的设计吗?事实上,如何划分、设计真正好的对象,是非常难以掌握的,如果面向对象在实践中没有很大的问题,就不会有时髦的重构了。实际上,翻开任何一本解释面向对象的书,都会提到,面向对象的基本初衷是代码重用。那么,如果把代码重用作为设计原则,设计人员总是考虑如何能够代码重用的设计,则其效果会大大高于没有什么可操作性的面向对象原则,减少耦合,如果将设计好的对象之间用有向箭头连在一起,很多时候会变成了一张网,如果将对象的方法之间用有向箭头连在一起,则看起来像信手涂鸦。对于设计人员,能够将其中的关系一一说明白,已经是不错的了,而对于的开发人员,则变成了黑洞。如果过一段时间、或者用户需求有些变化,对很多人讲,这样的设计会成为噩梦,这是比较极端的情况。但是也能说明,在设计各个层面减少耦合是设计人员需要随时提醒自己的。,