系统设计阶段又称为物理设计阶段,它是信息系统开发过程中一个非常重要的阶段。其任务是根据系统规格说明书中规定的功能要求,考虑实际条件,具体设计实现逻辑模型的技术方案,也就是设计新系统的物理模型,为下一阶段的系统实施工作奠定基础。
系统设计的主要内容包括概要设计和详细设计。概要设计主要任务是将系统的功能需求分配给软件模块,确定每个模块的功能和调用关系,形成软件的模块结构图,即系统结构图。在概要设计中,将系统开发的总任务分解成许多个基本的、具体的任务,为每个具体任务选择适当的技术手段和处理方法的过程称为详细设计。根据任务的不同,详细设计又可分为多种,例如,网络设计、代码设计、输入/输出设计、处理流程设计、数据存储设计、用户界面设计、安全性和可靠性设计等。
1.网络设计
网络设计的主要任务是,根据系统的要求选择网络结构,按照系统结构的划分,安排网络和设备的分布,然后根据物理位置考虑网络布线和设备的部署,还要根据实际业务的要求划定各网络节点的权限、级别和管理方式等,选择相应的系统软件和管理软件。
2.代码设计
进行代码设计的主要目的是确保代码的唯一化、规范化和系统化。在进行代码设计时,首先需要考虑系统的编码问题,编码问题的关键在于分类,有了一个科学的分类方式,系统要建立编码规范就相对较为容易。
3.输入/输出设计
输入设计的目的是确保向系统输入的数据的完整性、正确性和一致性,其主要内容包括确定输入数据的内容、输入方式设计、输入格式设计和检验方式的设计;输出设计的目的是确保系统输出数据的完整性、正确性和一致性,其主要内容包括确定输出的内容、选择输出设备与介质,以及确定输出格式等。
输入设计需要遵循以下原则:
(1)输入数据最少原则。在满足需求的前提下尽量提供较少的数据输入,数据的输入量越少,出错的几率越低,花费的时间也越少。
(2)简单性原则。输入过程应尽量简单,如性别、出生日期等数据设计为选择项,一方面方便用户的使用,节省输入时间,同时可以降低出错的可能性。
(3)尽早验证原则。对输入数据的检验尽量接近数据的输入点,及时发现输入中存在的错误,以便能够尽早进行改正。
(4)少转换原则。输入数据尽量采用原始的数据格式,避免在数据转换过程中发生错误。
最常用的最终输出方式有两种,一种是报表输出,一种是图形输出。一般来说,对于普通用户或具体数据的管理者和查阅者,应该以报表方式给出详细的数据记录;而对于企业的高层领导和宏观或综合管理部门,则应该使用图形方式给出比例或综合发展趋势的信息,可以通过曲线图、柱状图、饼状图等图形方式来呈现。
4.处理流程设计
处理流程设计是系统详细设计的重要组成部分,它的主要目的是确定各个系统模块的内部结构,即内部执行过程,包括局部数据组织和控制流,以及每个具体加工过程和实施细节。
5.数据存储设计
数据存储设计主要是根据数据处理要求、处理方式、存储的信息量大小、数据使用的频率和所能提供的设备条件等,选择数据存储的方式、存储介质、数据组织方式和记录格式,并估算数据的容量。
6.用户界面设计
通常情况下,良好的用户界面设计需要遵循如下一些基本原则:
(1)置于用户控制之下。在定义人机交互方式时,不强迫用户采用不是必须的或者不情愿的方式来进行操作,允许交互的中断和撤销。当用户操作技能等级提高时,可以实现流水化的交互方式,允许用户定制交互方式,以便使用户界面与内部技术细节隔离,允许用户和出现在屏幕上的对象直接进行交互。
(2)减轻用户的记忆负担。尽量减轻对用户记忆的要求,创建有意义的缺省设置,定义一些符合用户直觉的访问途径,适当定义一些快捷方式,界面的视觉布局应该尽量与真实世界保持一致,并能够以不断扩展的方式呈现信息。用户可以快速学习并使用系统,提供尽量“傻瓜式”的操作界面,方便用户使用。界面中各个元素的名称应该易懂,用词准确,避免模棱两可的字眼,能够做到“望文知意”,理想的情况是用户不用查阅帮助,就能知道该界面元素的功能,并正确地进行相关操作。
(3)保持界面一致性。用户应以一致的方式提供或获取信息,所有可视信息的组织需要按照统一的设计标准,在系列化的应用软件中需要保持一致性,用户已经很熟悉的一些界面交互模型不到万不得已时,不要随意进行修改。需要确保用户界面操作和使用的一致性,用户界面的—致性可以使用户能够统一地对待系统的各个不同的功能界面,以及系列化的系统,从而降低培训和支持成本。
7.安全性和可靠性设计
安全性和可靠性设计的目的是确保系统的安全性和可靠性,对系统的运行环境和数据处理进行有效的控制,保证系统安全、有效地运行。其主要内容包括系统运行环境安全性分析和控制,对数据处理的控制。
处理流程设计的任务是设计出系统所有模块以及它们之间的相互关系,并具体设计出每个模块内部的功能和处理过程,为开发人员提供详细的技术资料。
1.流程
流程具有目标性、内在性、整体性、动态性、层次性和结构性等特点。一般来说,流程包括六个基本要素,分别是输入资源、活动、活动的相互作用(结构)、输出结果、用户和价值。
2.工作流
工作流是一类能够完全或者部分自动执行的业务过程,根据一系列过程规则、文档、信息或任务,在不同的执行者之间传递和执行。
3.活动及其所有者
活动是流程定义中的一个基本要素,一次活动可以改变流程处理数据的内容、流程的状态,并可能将流程推动到其他活动中去。活动可以由人来完成,也可以是系统自动进行处理。每个活动均有输入、处理和输出。在输入和输出的转换过程中需要进行业务逻辑的判断。
活动的所有者是流程参与者(包括人或其他系统)之一,他们有权决定该活动是否结束,当活动结束时,可以将活动推动到其他活动中,可能是下一个活动,也可能是前一个活动。活动的所有者是有权整体控制流程实例执行过程的参与者,通常活动的所有者是流程的发起人,他们对流程的各项活动都很关注,而且可以整体控制流程实例的执行。
4.工作项
工作项代表流程实例中活动的参与者将要执行的工作,工作流管理系统包括若干个工作项,一个参与者也可对应多个工作项。
工作流管理系统(WorkFlow Management System,WFMS)通过软件定义、创建工作流并管理其执行。
1.WFMS的基本功能
WFMS的基本功能体现在以下几个方面:
(1)对工作流进行建模。即定义工作流,包括具体的活动和规则等,所创建的模型是同时可以被人和计算机所“理解”的,工作流对应现实世界的业务处理过程,不能改变真实业务的处理逻辑。
(2)工作流执行。遵循工作流模型来创建和执行实际的工作流,即通过WFMS可以执行多个工作项。
(3)业务过程的管理和分析。监控和管理执行中的业务(工作流),例如,进度完成情况和数据所处状态、工作分配与均衡情况等。
2.WFMS的组成
工作流参考模型(Workflow Reference Model,WRM)包含六个基本模块,分别是工作流执行服务、工作流引擎、流程定义工具、客户端应用、调用应用和管理监控工具。
(1)工作流执行服务。工作流执行服务是WFMS的核心模块,它的功能包括创建和管理流程定义,创建、管理和执行流程实例。
(2)工作流引擎。工作流引擎是为流程实例提供运行环境,并解释执行流程实例的软件模块,即负责流程处理的软件模块。
(3)流程定义工具。流程定义工具是管理流程定义的工具,它可以通过图形方式把复杂的流程定义显示出来并加以操作,流程定义工具与工作流执行服务交互,一般该模块为设计人员提供图形化的用户界面。
(4)客户端应用。客户端应用是通过请求的方式与工作流执行服务交互的应用,也就是说,是客户端应用调用工作流执行服务。
(5)调用应用。调用应用是被工作流执行服务调用的应用,调用应用与工作流执行服务交互。为了协作完成一个流程实例的执行,不同的工作流执行服务之间进行交互,它通常是工作流所携带数据的处理程序,常用的是电子文档的处理程序,它们在工作流执行过程中被调用,并向最终用户展示数据,这些应用程序的信息包括名称、调用方式和参数等。
(6)管理监控工具。管理监控工具主要指组织机构和参与者等数据的维护管理和流程执行情况的监控,管理监控工具与工作流执行服务交互。
为了降低这六个模块的耦合度,使得模块之间相互独立,可通过接口来进行连接和调用,这六个模块之间可以通过以下五个接口进行交互:
(1)工作流定义交换接口(接口一)。用于在流程定义工具与执行服务之间交换工作流定义,当工作流定义发生改变时,其处理流程将发生变化,执行服务也应该相应进行调整。
(2)工作流客户端应用接口(接口二)。用于工作流客户端应用访问工作流引擎和工作列表,客户端应用是最终用户直接操作的界面,只要设计合理,可以实现多个不同的客户端应用调用同一个工作流引擎。
(3)调用应用接口(接口三)。用于调用不同的应用系统。
(4)WFMS互操作接口(接口四)。用于不同的WFMS之间的互操作。
(5)系统管理和监控接口(接口五)。用于系统管理应用访问工作流执行服务。
1.程序流程图
程序流程图(Program Flow Diagram,PFD)用一些图框表示各种操作,它独立于任何一种程序设计语言,比较直观、清晰,易于学习掌握。
2.IPO图
IPO图用来描述每个模块的输入、输出和数据加工。IPO图中的输入、输出与功能模块、文件及系统外部项都需要通过数据字典来描述,同时需要为其中的某些元素添加注释。
3.N-S图
N-S图中包括五种控制结构,分别是顺序型、选择型、WHILE循环型(当型循环)、UNTIL循环型(直到型循环)和多分支选择型,任何一个N-S图都是这五种基本控制结构相互组合与嵌套的结果。在N-S图中,过程的作用域明确;它没有箭头,不能随意转移控制;而且容易表示嵌套关系和层次关系;并具有强烈的结构化特征。但是当问题很复杂时,N-S图可能很大。
4.问题分析图
问题分析图(Problem Analysis Diagram,PAD)也包含五种基本控制结构,并允许递归使用。
5.过程设计语言
过程设计语言(Process Design Language,PDL)也称为结构化语言或伪代码(pseudo code),它是一种混合语言,采用自然语言的词汇和结构化程序设计语言的语法,用于描述处理过程怎么做,类似于编程语言。
6.判定表
判定表采用表格形式来表达逻辑判断问题,表格分成四个部分,左上部分为条件说明,左下部分为行动说明,右上部分为各种条件的组合说明,右下部分为各条件组合下相应的行动。在表的右上部分中列出所有条件,“T”表示该条件取值为真,“F”表示该条件取值为假,空白表示这个条件无论取何值对动作的选择不产生影响,在判定表右下部分中列出所有的处理动作,“Y”表示执行对应的动作,空白表示不执行该动作;判定表右半部分的每一列实质上是一条规则,规定了与特定条件取值组合相对应的动作。
7.判定树
判定树(decision tree)也是用来表示逻辑判断问题的一种常用的图形工具,它用树来表达不同条件下的不同处理流程,比语言、表格的方式更为直观。判定树的左侧(称为树根)为加工名,中间是各种条件,所有的行动都列于最右侧。
结构化设计(Structured Design,SD)是一种面向数据流的方法,它以SRS和SA阶段所产生的数据流图和数据字典等文档为基础,是一个自顶向下、逐步求精和模块化的过程。
SD方法的基本思想是将软件设计成由相对独立且具有单一功能的模块组成的结构,分为概要设计和详细设计两个阶段,其中概要设计的主要任务是确定软件系统的结构,对系统进行模块划分,确定每个模块的功能、接口和模块之间的调用关系;详细设计的主要任务是为每个模块设计实现的细节。
1.信息隐蔽与抽象
信息隐蔽原则要求采用封装技术,将程序模块的实现细节(过程或数据)隐藏起来,对于不需要这些信息的其他模块来说是不能访问的,使模块接口尽量简单。
抽象原则要求抽取事物最基本的特性和行为,忽略非本质的细节,采用分层次抽象的方式可以控制软件开发过程的复杂性,有利于软件的可理解性和开发过程的管理。通常,抽象层次包括过程抽象、数据抽象和控制抽象。
2.模块化
在SD方法中,模块是实现功能的基本单位,它一般具有功能、逻辑和状态三个基本属性,其中功能是指该模块“做什么”,逻辑是描述模块内部“怎么做”,状态是该模块使用时的环境和条件。
在描述一个模块时,必须按模块的外部特性与内部特性分别描述。模块的外部特性是指模块的模块名、参数表和给程序乃至整个系统造成的影响,而模块的内部特性则是指完成其功能的程序代码和仅供该模块内部使用的数据。
在SD方法中,系统由多个逻辑上相对独立的模块组成,在模块划分时需要遵循如下原则:
(1)模块的大小要适中。系统分解时需要考虑模块的规模,过大的模块可能导致系统分解不充分,其内部可能包括不同类型的功能,需要进一步划分,尽量使得各个模块的功能单一;过小的模块将导致系统的复杂度增加,模块之间的调用过于频繁,反而降低了模块的独立性。一般来说,一个模块的大小使其实现代码在1~2页纸之内,或者其实现代码行数在50~200行之间,这种规模的模块易于实现和维护。
(2)模块的扇入和扇出要合理。一个模块的扇出是指该模块直接调用的下级模块的个数;扇出大表示模块的复杂度高,需要控制和协调过多的下级模块。扇出过大一般是因为缺乏中间层次,应该适当增加中间层次的控制模块;扇出太小时可以把下级模块进一步分解成若干个子功能模块,或者合并到它的上级模块中去。一个模块的扇入是指直接调用该模块的上级模块的个数;扇入大表示模块的复用程度高。设计良好的软件结构通常顶层扇出比较大,中间扇出较少,底层模块则有大扇入。一般来说,系统的平均扇入和扇出系数为3或4,不应该超过7,否则会增大出错的概率。
(3)深度和宽度适当。深度表示软件结构中模块的层数,如果层数过多,则应考虑是否有些模块设计过于简单,看能否适当合并。宽度是软件结构中同一个层次上的模块总数的最大值,一般说来,宽度越大系统越复杂,对宽度影响最大的因素是模块的扇出。在系统设计时,需要权衡系统的深度和宽度,尽量降低系统的复杂性,减少实施过程的难度,提高开发和维护的效率。
3.耦合
耦合表示模块之间联系的程度。紧密耦合表示模块之间联系非常强,松散耦合表示模块之间联系比较弱,非耦合则表示模块之间无任何联系,是完全独立的。
4.内聚
内聚表示模块内部各成分之间的联系程度,是从功能角度来度量模块内的联系,一个好的内聚模
块应当恰好做目标单一的一件事情。
一般说来,系统中各模块的内聚越高,则模块间的耦合就越低,但这种关系并不是绝对的。耦合低使得模块间尽可能相对独立,从而各模块可以单独开发和维护;内聚高使得模块的可理解性和维护性大大增强。因此,在模块的分解中应尽量减少模块的耦合,力求增加模块的内聚,遵循“高内聚、低耦合”的设计原则。
5.模块类型
在系统结构图中不能再分解的底层模块称为原子模块。如果一个系统的全部实际加工(数据计算或处理)都由底层的原子模块来完成,而其他所有非原子模块仅仅执行控制或协调功能,这样的系统就是完全因子分解的系统。如果SC是完全因子分解的,就是最好的系统。一般而言,在SC中存在四种类型的模块:
(1)传入模块。传入模块从下属模块中获取数据,经过某些处理,再将其传送给上级模块。
(2)传出模块。传出模块从上级模块中获取数据,进行某些处理,再将其传送给下属模块。
(3)变换模块。变换模块也称为加工模块,它从上级模块获取数据,进行特定的处理,然后转换成其他形式,再传送回上级模块,如图13-7(c)所示。大多数计算模块(原子模块)都属于这一类。
(4)协调模块。协调模块是对所有下属模块进行协调和管理的模块。在系统的I/O部分或数据加工部分可以找到这样的模块,在一个好的SC中,协调模块应在较高层出现。
系统结构图(Structure Chart,SC)又称为模块结构图,它是软件概要设计阶段的工具,反映系统的功能实现和模块之间的联系与通信,包括各模块之间的层次结构,即反映了系统的总体结构。常用的SC主要有变换型、事务型和混合型三种。
1.SC的组成
SC包括模块、模块之间的调用关系、模块之间的通信和辅助控制符号等四个部分。
(1)模块。在SC中,模块用矩形框表示,框中标注模块的名字,对于已定义或者已开发的模块,可以用双纵边矩形框表示。
(2)模块之间的调用关系。绘制方法是两个模块一上一下布局,以箭头相连,上面的模块是调用模块,箭头指向的模块是被调用的模块。
(3)模块之间的通信。模块间的通信以表示调用关系的长箭头旁边的短箭头表示,短箭头的方向和名字分别表示调用模块和被调用模块之间信息的传递方向和内容。
(4)辅助控制符号。当模块A有条件地调用模块B时,在箭头的起点标以菱形,模块A反复调用模块C时,在调用关系的连线上增加一个环状的箭头。在SC中,条件调用时所依赖的判断条件和循环调用时所依赖的控制条件通常都无须注明。
2.变换型SC
信息沿着输入通道进入系统,然后通过变换中心(也称为主加工)处理,再沿着输出通道离开系统,具有这一特性的信息流称为变换流。
具有变换流型的SC可明显地分成输入、变换(主加工)和输出三大部分,它的功能是将输入的数据经过加工后输出。
3.事务型SC
信息沿着输入通道到达一个事务中心,事务中心根据输入信息(即事务)的类型在若干个动作序列(称为活动流)中选择一个来执行,这种信息流称为事务流。
事务流有明显的事务中心,各活动以事务中心为起点呈辐射状流出。事务型系统一般由三层组成,分别是事务层、操作层和细节层,它的功能是对接收的事务,按其类型选择某一类事务处理。
4.混合型SC
在规模较大的系统中,其DFD往往是变换型和事务型的混合结构,可把变换分析和事务分析应用在同一DFD的不同部分。先找出主处理,设计出结构图的上层,然后根据DFD各部分的结构特点,适当选用变换分析或事务分析就可得出SC的某个初始化方案。
OOD是OOA方法的延续,其基本思想包括抽象、封装和可扩展性,其中可扩展性主要通过继承和多态来实现。在OOD中,数据结构和在数据结构上定义的操作算法封装在一个对象之中。
设计类是OOD中最重要的组成部分,也是最复杂和最耗时的部分。在系统设计过程中,类可以分为三种类型,分别是实体类、边界类和控制类。
1.实体类
实体类映射需求中的每个实体,实体类保存需要存储在永久存储体中的信息。实体类通常都是永久性的,它们所具有的属性和关系是长期需要的,有时甚至在系统的整个生存期都需要。
实体类是对用户来说最有意义的类,通常采用业务领域术语命名,一般来说是一个名词,在用例模型向领域模型的转化中,一个参与者一般对应于实体类。
2.控制类
控制类是用于控制用例工作的类,一般是由动宾结构的短语(“动词+名词”或“名词+动词”)转化来的名词。控制类用于对一个或几个用例所特有的控制行为进行建模,控制对象(控制类的实例)通常控制其他对象,因此,它们的行为具有协调性。
控制类将用例的特有行为进行封装,控制对象的行为与特定用例的实现密切相关,当系统执行用例的时候,就产生了一个控制对象,控制对象经常在其对应的用例执行完毕后消亡。
3.边界类
边界类用于封装在用例内、外流动的信息或数据流。边界类位于系统与外界的交接处,包括所有窗体、报表、打印机和扫描仪等硬件的接口,以及与其他系统的接口。要寻找和定义边界类,可以检查用例模型,每个参与者和用例交互至少要有一个边界类,边界类使参与者能与系统交互。
边界类是一种用于对系统外部环境与其内部运作之间的交互进行建模的类。常见的边界类有窗口、通信协议、打印机接口、传感器和终端等。实际上,在系统设计时,产生的报表都可以作为边界类来处理。
对象持久化是把内存中的对象保存到数据库或可永久保存的存储设备中。
在多层软件设计和开发中,为了降低系统的耦合度,一般会引入持久层(persistence layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联,持久层的设计实现了数据处理层内部的业务逻辑和数据逻辑的解耦。
数据持久化技术封装了数据访问细节,为大部分业务逻辑提供面向对象的API。通过持久化技术,可以减少访问数据库数据次数,增加应用程序执行速度;其代码重用性高,能够完成大部分数据库操作;松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码。
1.CMP
CMP(Container-Managed Persistence,容器管理持久化)是由EJB容器来管理实体EJB 的持久化,EJB容器封装了对象/关系的映射和数据访问细节。
2.Hibernate
Hibernate和iBatis都是ORM解决方案,不同的是两者各有侧重。
3.iBatis
iBatis提供Java对象到SQL(面向参数和结果集)的映射实现,实际的数据库操作需要通过手动编写SQL实现,与Hibernate相比,iBatis最大的特点就是小巧,上手较快。
4.JDO
JDO(Java Data Object,Java数据对象)是 SUN 公司制定的描述对象持久化语义的标准API,它是Java对象持久化的新规范。JDO提供了透明的对象存储,对开发人员来说,存储数据对象完全不需要额外的代码(例如,JDBC API的使用)
常用的OOD原则包括开闭原则、里氏替换原则、依赖倒置原则、组合/聚合复用原则、接口隔离原则和最少知识原则等。这些设计原则首先都是面向复用的原则,遵循这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性。
1.开闭原则
开闭原则是指软件实体应对扩展开放,而对修改关闭,即尽量在不修改原有代码的情况下进行扩展。
2.里氏替换原则
里氏替换原则基本思想是,一个软件实体如果使用的是一个基类对象,那么一定适用于其子类对象,而且觉察不出基类对象和子类对象的区别,即把基类都替换成它的子类,程序的行为没有变化。
在运用里氏替换原则时,尽量将一些需要扩展的类或者存在变化的类设计为抽象类或者接口,并将其作为基类,在程序中尽量使用基类对象进行编程。
3.依赖倒置原则
依赖倒置原则是指抽象不应该依赖于细节,细节应当依赖于抽象。为了确保该原则的应用,一个具体类应当只实现接口和抽象类中声明过的方法,而不要给出多余的方法,否则,将无法调用到在子类中增加的新方法。
4.组合/聚合复用原则
组合/聚合复用原则又称为合成复用原则,是在一个新的对象中通过组合关系或聚合关系来使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简单地说,就是要尽量使用组合/聚合关系,少用继承。
通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是透明的,所以这种复用是透明的复用,又称为白盒复用。
5.接口隔离原则
接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。
6.最少知识原则
最少知识原则也称为迪米特法则(Law of Demeter),是指一个软件实体应当尽可能少地与其他实体发生相互作用。这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。
最少知识原则的主要用途在于控制信息的过载。在将最少知识原则运用到系统设计中时,要注意以下几点:
(1)在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用。一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波动。
(2)在类的结构设计上,每个类都应当尽量降低其属性和方法的访问权限。
(3)在类的设计上,只要有可能,一个类型应当设计成不变类。
(4)在对其他类的引用上,一个对象对其他对象的引用应当降到最低。
1.软件模式与设计模式
软件模式是将模式的一般概念用于软件开发领域,即软件开发的总体指导思路或参照样板。软件模式包括设计模式、架构模式、分析模式和过程模式等,软件生存期的各个阶段都存在着被认同的模式。
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式的目的是为了提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性。
2.设计模式的关键元素
设计模式包含模式名称、问题、目的、解决方案、效果、实例代码和相关设计模式等基本要素,其中的关键元素包括以下四个方面:
(1)模式名称。给模式取一个助记名,用一两个词语来描述模式待解决的问题、解决方案和使用效果,以便更好地理解模式并方便开发人员之间的交流。
(2)问题。描述应该在何时使用模式,即在解决何种问题时可使用该模式。有时,在问题部分会包括使用模式必须满足的一系列先决条件。
(3)解决方案。描述设计的组成成分、它们之间的相互关系及各自的职责和协作方式。模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供一个问题的抽象描述和具有一般意义的元素组合(类或对象组合)。
(4)效果。描述模式应用的效果以及使用模式时应权衡的问题,即模式的优缺点。没有一种解决方案是完美的,每种设计模式都具有自己的优点,但也存在一些缺陷,它们对于评价设计选择和理解使用模式的代价和好处具有重要意义。模式效果有助于选择合适的模式,它不仅包括时间和空间的权衡,还包括对系统的灵活性、可扩展性或可移植性的影响。
根据目的和用途不同,设计模式可分为创建型(creational)模式、结构型(structural)模式和行为型(behavioral)模式三种。创建型模式主要用于创建对象,结构型模式主要用于处理类或对象的组合,行为型模式主要用于描述类或对象的交互以及职责的分配。
根据处理范围不同,设计模式可分为类模式和对象模式。类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,属于静态关系;对象模式处理对象之间的关系,这些关系在运行时刻变化,更具动态性。
1.创建型模式
创建型模式隐藏了对象是如何被创建的和组合在一起的,以达到使整个系统独立的目的。创建型模式包括工厂方法模式、抽象工厂模式、原型模式、单例模式和建造者模式等。
(1)工厂方法(factory method)模式。工厂方法模式又称为虚拟构造器(virtualconstructor)模式或多态模式,属于类的创建型模式。在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实例化(创建)哪一个类。
(2)抽象工厂(abstract factory)模式。抽象工厂模式又称为Kit模式,属于对象创建型模式。抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态,它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。在抽象工厂模式中,引入了产品等级结构和产品族的概念,产品等级结构是指抽象产品与具体产品所构成的继承层次关系,产品族是同一个工厂所生产的一系列产品,即位于不同产品等级结构且功能相关联的产品组成的家族。当抽象工厂模式退化到只有一个产品等级结构时,即变成了工厂方法模式。
(3)原型(prototype)模式。在系统开发过程中,有时候有些对象需要被频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后通过复制这个原型对象的办法,创建出更多同类型的对象。原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式又可分为两种,分别是浅克隆和深克隆。浅克隆仅仅复制所考虑的对象,而不复制它所引用的对象,也就是其中的成员对象并不复制;深克隆除了对象本身被复制外,对象包含的引用也被复制,即成员对象也被复制。
(4)单例(singleton)模式。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
(5)建造者(builder)模式。建造者模式强调将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步地创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。
2.结构型模式
结构型模式描述如何将类或对象结合在一起形成更大的结构。结构型模式描述两种不同的事物,即类与类的实例(对象),根据这一点,可以分为类结构型模式和对象结构型模式。结构型模式包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式等。
(1)适配器(adapter)模式。适配器模式将一个接口转换成客户希望的另一个接口,从而使接口不兼容的那些类可以一起工作。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。在类适配器模式中,通过使用一个具体类将适配者适配到目标接口中;在对象适配器模式中,一个适配器可以将多个不同的适配者适配到同一个目标。
(2)桥接(bridge)模式。桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(handle and body)模式或接口(interface)模式。桥接模式类似于多重继承方案,但是多重继承方案往往违背了类的单一职责原则,其复用性比较差,桥接模式是比多重继承方案更好的解决方法。
(3)组合(composite)模式。组合模式又称为整体-部分(part-whole)模式,属于对象的结构模式。在组合模式中,通过组合多个对象形成树形结构以表示整体-部分的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。
(4)装饰(decorator)模式。装饰模式是一种对象结构型模式,可动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。通过装饰模式,可以在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;当需要动态地给一个对象增加功能,这些功能可以再动态地被撤销时可使用装饰模式;当不能采用生成子类的方法进行扩充时也可使用装饰模式。
(5)外观(facade)模式。外观模式是对象的结构模式,要求外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
(6)享元(flyweight)模式。享元模式是一种对象结构型模式,通过运用共享技术,有效地支持大量细粒度的对象。系统只使用少量的对象,而这些对象都很相似,状态变化很小,对象使用次数增多。享元对象能做到共享的关键是区分内部状态和外部状态。内部状态是存储在享元对象内部并且不会随环境改变而改变,因此内部状态可以共享;外部状态是随环境改变而改变的、不可以共享的状态,享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部,外部状态之间是相互独立的。
(7)代理(proxy)模式。代理模式是一种对象结构型模式,可为某个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式能够协调调用者和被调用者,能够在一定程度上降低系统的耦合度,其缺点是请求的处理速度会变慢,并且实现代理模式需要额外的工作。
3.行为型模式
行为型模式是对在不同的对象之间划分责任和算法的抽象化,它不仅仅是关于类和对象的,而且是关于它们之间的相互作用的。行为型模式分为类行为模式和对象行为模式两种,其中类行为模式使用继承关系在几个类之间分配行为,而对象行为模式则使用对象的聚合来分配行为。行为型模式包括职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、
状态模式、策略模式、模板方法模式、访问者模式等。
(1)职责链(chain of responsibility)模式。职责链模式是一种对象的行为型模式,避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式不保证每个请求都被接受,由于一个请求没有明确的接收者,那么就不能保证它一定会被处理。
(2)命令(command)模式。命令模式是一种对象的行为型模式,类似于传统程序设计方法中的回调机制,它将一个请求封装为一个对象,从而使得可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是对命令的封装,将发出命令的责任和执行命令的责任分割开,委派给不同的对象,以实现发送者和接收者完全解耦,提供更大的灵活性和可扩展性。
(3)解释器(interpreter)模式。解释器模式属于类的行为型模式,描述了如何为语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子,这里的“语言”是使用规定格式和语法的代码。解释器模式主要用在编译器中,在应用系统开发中很少用到。
(4)迭代器(iterator)模式。迭代器模式是一种对象的行为型模式,提供了一种方法来访问聚合对象,而不用暴露这个对象的内部表示。迭代器模式支持以不同的方式遍历一个聚合对象,复杂的聚合可用多种方法来进行遍历;允许在同一个聚合上可以有多个遍历,每个迭代器保持它自己的遍历状态,因此,可以同时进行多个遍历操作。
(5)中介者(mediator)模式。中介者模式是一种对象的行为型模式,通过一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者对象的存在保证了对象结构上的稳定,也就是说,系统的结构不会因为新对象的引入带来大量的修改工作。
(6)备忘录(memento)模式。备忘录模式确保在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤。
(7)观察者(observer)模式。观察者模式又称为发布-订阅模式、模型-视图模式、源-监听器模式或从属者(dependents)模式,是一种对象的行为型模式。它定义了对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。观察者模式的优点在于实现了表示层和数据层的分离,并定义了稳定的更新消息传递机制,类别清晰,抽象了更新接口,使得相同的数据层可以有各种不同的表示层。
(8)状态(state)模式。状态模式是一种对象的行为型模式,允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。状态模式封装了状态的转换过程,但是它需要枚举可能的状态,因此,需要事先确定状态种类,这也导致在状态模式中增加新的状态类时将违反开闭原则,新的状态类的引入将需要修改与之能够进行转换的其他状态类的代码。状态模式的使用必然会增加系统类和对象的个数。
(9)策略(strategy)模式。策略模式是一种对象的行为型模式,定义一系列算法,并将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,其目的是将行为和环境分隔,当出现新的行为时,只需要实现新的策略类。
(10)模板方法(template method)模式。模板方法模式是一种类的行为型模式,用于定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,其缺点是对于不同的实现,都需要定义一个子类,这会导致类的个数增加,但是更加符合类职责的分配原则,使得类的内聚性得以提高。
(11)访问者(visitor)模式。访问者模式是一种对象的行为型模式,用于表示一个作用于某对象结构中的各元素的操作,它使得用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式使得增加新的操作变得很容易,但在一定程度上破坏了封装性。