免费智能真题库 > 历年试卷 > 系统架构设计师 > 2016年下半年 系统架构设计师 下午试卷 论文
  第2题      
  知识点:   开发过程   实际应用   编码   代码设计   开发人员   可靠性   面向对象软件开发   软件设计   设计模式

 
软件设计模式及其应用。
软件设计模式(Software Design Pattern)是一套被反复使用的、多数人知晓的、经过分类编目的代码设计经验的总结。使用设计模式是为了重用代码以提高编码效率、增加代码的可理解性、保证代码的可靠性软件设计模式是软件开发中的最佳实践之一,它经常被软件开发人员在面向对象软件开发过程中所采用。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在实际应用中都有相应的原型与之相对,每种模式都描述了一个在软件开发中不断重复发生的问题,以及对应该原型问题的核心解决方案。
 
问题:2.1   请围绕“论软件设计模式及其应用”论题,依次从以下三个方面进行论述。
1.概要叙述你参与分析和开发的软件系统,以及你在项目中所担任的主要工作。
2.说明常用的软件设计模式有哪几类?阐述每种类型特点及其所包含的设计模式。
3.详细说明你所参与的软件系统开发项目中,采用了哪些软件设计模式,具体实施效果如何。
 
 
 

   知识点讲解    
   · 开发过程    · 实际应用    · 编码    · 代码设计    · 开发人员    · 可靠性    · 面向对象软件开发    · 软件设计    · 设计模式
 
       开发过程
        嵌入式系统软件的开发过程可以分为项目计划、可行性分析、需求分析、概要设计、详细设计、程序建立、下载、调试、固化、测试及运行等几个阶段。
        项目计划、可行性分析、需求分析、概要设计及详细设计等几个阶段,与通用软件的开发过程基本一致,都可按照软件工程方法进行,如采用原型化方法、结构化方法等。
        :由于嵌入式软件的运行和开发环境不同,开发工作是交叉进行的,所以每一步都要考虑到这一点。
        程序建立阶段的工作是根据详细设计阶段产生的文档进行的,主要是源代码编写、编译链接等子过程,这些工作都在宿主机上进行,不需要用到目标机。产生应用程序的可执行文件后,就要用到交叉开发环境进行调试,根据实际情况可以选用3.6.3节中提到的调试方法或其有效组合来进行。由于嵌入式系统对安全性和可靠性的要求比通用计算机系统要高,所以,在对嵌入式系统进行白盒测试时,要求有更高的代码覆盖率。
        最后,要将经调试后正确无误的可执行程序固化到目标机上。根据嵌入式系统硬件配置的不同,可以固化在EPROM(Erasable Programmable ROM,可擦除可编程ROM)和Flash等存储器中,也可固化在DOC(DiskOnChip)等电子盘中,通常还要借助一些专用编程器进行。
 
       实际应用
        在考试时,可能会出现一些需要综合应用的问题,需要考生根据基本的概念,结合实际问题进行解答。
        例如,在某并发系统中,有一个发送进程A、一个接收进程B、一个环形缓冲区BUFFER、信号量S1S2。发送进程不断地产生消息并写入缓冲区BUFFER,接收进程不断地从缓冲区BUFFER取消息。假设发送进程和接收进程可以并发地执行,那么,当缓冲区的容量为N时,如何使用PV操作才能保证系统能够正常工作。发送进程A和接收进程B的工作流程如下图所示。请在下图中的①~④处填写正确的操作。
        
        PV操作实例一
        根据题意,很显然,这是一个“生产者-消费者”问题,根据该问题的特性,通常需要3个信号量来实现:两个用来管理缓冲区同步,信号量empty表示空闲缓冲区数量,初值为缓冲区最大数N,信号量full表示已填充缓冲区数量,初值为0;一个用于管理互斥,由信号量mutex保证只有一个进程在写缓冲区,初值为1。但在本题中,进程A和进程B允许并发地访问缓冲区,因此无须管理互斥,就不需要使用信号量mutex了。因此只需定义两个信号量:S1S2,初值为NS1在此承担的是信号量empty的功能,初值为0的S2在此则承担的是信号量full的功能。
        通过这样的分析,不难得出结论:①处应该是P(S1),将空闲缓冲区数量减1;②处应该是V(S2),将已填充的缓冲区数量加1;③处则是P(S2);④处为V(S1)。
        在这个例子的基础上,如果系统中有多个发送进程和接收进程,进程间的工作流程如下图所示,其中空①~④的内容与上图相同。发送进程产生消息并顺序地写入环形缓冲区BUFFER,接收者进程顺序地从BUFFER中取消息,且每条消息只能读取一次。为了保证进程间的正确通信,增加了信息量SASB。请说明信息量SA和SB的物理意义,在下图中的⑤和⑥处填入正确的内容,并从下图的~①中选择4个位置正确地插入P(SA)、V(SA)、P(SB)和V(SB)。
        
        PV操作实例二
        上图所涉及的问题在普通的“生产者-消费者”问题上增加了一些复杂度:“系统中有多个发送进程和接收进程”,根据题意,我们可以得知它要完成的控制是:发送进程顺序写入,接收进程顺序读取,而且每条消息都只能够读取一次。这显然是两个互斥的问题,即多个发送进程在写缓冲区时是互斥关系,多个接收进程读缓冲区也是互斥关系。因此,信号量SASB分别实现这两个用来完成两个进程的互斥控制。
        (1)SA:初值为1,表示允许同时对缓冲区进行写操作的进程数量。
        (2)SB:初值为1,表示允许同时对缓冲区进行读操作的进程数量。
        当然,两个对调也是可以的。在发送进程和接收进程中分别有一组信号量SASB的PV操作。因此,接下来的问题就是找插入点。互斥控制的要点在于判断出临界区的范围,也就是哪部分程序必须互斥进入,否则将出现问题。根据这一点,可以进行如下分析。
        (1)发送进程:在进程产生消息之后准备写入缓冲区时,这时就需要进行互斥判断,因此在位置应插入P(SA);而直到完成“i=(i+1)mod N”操作后,才完成缓冲区操作,因此必须在位置插入V(SA)。
        (2)接收进程:由于接收进程是负责读数据的,如果数据区是空的则应该等待,因此必须先完成P(S2)操作,来决定其是否需要阻塞。如果没有阻塞时,再进入临界区,因此应该在位置处操作P(SB);而“对读取的消息进行处理”已显然在临界区之外,因此应该在位置插入V(SB)。
 
       编码
               编码过程
               在给定了软件设计规格说明书后,下一步的工作就是编写代码。一般来说,编码工作可以分为四个步骤:
               (1)确定源程序的标准格式,制订编程规范。
               (2)准备编程环境,包括软硬件平台的选择,包括操作系统、编程语言、集成开发环境等。
               (3)编写代码。
               (4)进行代码审查,以提高编码质量。为提高审查的效率,在代码审查前需要准备一份检查清单,并设定此次审查须找到的bug数量。在审查时,要检查软件规格说明书与编码内容是否一致;代码对硬件和操作系统资源的访问是否正确;中断控制模块是否正确等。
               编码准则
               在嵌入式系统中,由于资源有限,且实时性和可靠性要求较高,因此,在开发嵌入式软件时,要注意对执行时间、存储空间和开发/维护时间这三种资源的使用进行优化。也就是说,代码的执行速度要越快越好,系统占用的存储空间要越小越好,软件开发和维护的时间要越少越好。
               具体来说,在编写代码时,需要做到以下几点:
               .保持函数短小精悍。一个函数应该只实现一个功能,如果函数的代码过于复杂,将多个功能混杂在一起,就很难具备可靠性和可维护性。另外,要限制函数的长度,一般来说,一个函数的长度最好不要超过100行。
               .封装代码。将数据以及对其进行操作的代码封装在一个实体中,其他代码不能直接访问这些数据。例如,全局变量必须在使用该变量的函数或模块内定义。对代码进行封装的结果就是消除了代码之间的依赖性,提高了对象的内聚性,使封装后的代码对其他行为的依赖性较小。
               .消除冗余代码。例如,将一个变量赋给它自己,初始化或设置一个变量后却从不使用它,等等。研究表明,即使是无害的冗余也往往和程序的缺陷高度关联。
               .减少实时代码。实时代码不但容易出错、编写成本较高,而且调试成本可能更高。如果可能,最好将对执行时间要求严格的代码转移到一个单独的任务或者程序段中。
               .编写优雅流畅的代码。
               .遵守代码编写标准并借助检查工具。用自动检验工具寻找缺陷比人工调试便宜,而且能捕捉到通过传统测试检查不到的各种问题。
               编码技术
                      编程规范
                      在嵌入式软件开发过程中,遵守编程规范,养成良好的编程习惯,这是非常重要的,将直接影响到所编写代码的质量。
                      编程规范主要涉及的三方面内容:
                      .命名规则。从编译器的角度,一个合法的变量名由字母、数字和下画线三种字符组成,且第一个字符必须为字母或下画线。但是从程序员的角度,一个好的名字不仅要合法,还要载有足够的信息,做到“见名知意”,并且在语意清晰、不含歧义的前提下,尽可能地简短。
                      .编码格式。在程序布局时,要使用缩进规则,例如变量的定义和可执行语句要缩进一级,当函数的参数过长时,也要缩进。另外,括弧的使用要整齐配对,要善于使用空格和空行来美化代码。例如,在二元运算符与其运算对象之间,要留有空格;在变量定义和代码之间要留有空行;在不同功能的代码段之间也要用空行隔开。
                      .注释的书写。注释的典型内容包括:函数的功能描述;设计过程中的决策,如数据结构和算法的选择;错误的处理方式;复杂代码的设计思想等。在书写注释时要注意,注释的内容应该与相应的代码保持一致,同时要避免不必要的注释,过犹不及。
                      性能优化
                      由于嵌入式系统对实时性的要求较高,因此一般要求对代码的性能进行优化,使代码的执行速度越快越好。以算术运算为例,在编写代码时,需要仔细地选择和使用算术运算符。一般来说,整数的算术运算最快,其次是带有硬件支持的浮点运算,而用软件来实现的浮点运算是非常慢的。因此,在编码时要遵守以下准则:
                      .尽量使用整数(char、short、int和long)的加法和减法。
                      .如果没有硬件支持,尽量避免使用乘法。
                      .尽量避免使用除法。
                      .如果没有硬件支持,尽量避免使用浮点数。
                      下图是一个例子,其中两段代码的功能完全一样,都是对一个结构体数组的各个元素进行初始化,但采用两种不同的方法来实现。下图(a)采用数组下标的方法,在定位第i个数组元素时,需要将i乘以结构体元素的大小,再加上数组的起始地址。下图(b)采用的是指针访问的方法,先把指针fp初始化为数组的起始地址,然后每访问完一个数组元素,就把fp加1,指向下一个元素。在一个奔腾4的PC上,将这两段代码分别重复10 700次,右边这段代码需要1ms,而左边这段代码需要2.13ms。
                      
                      算术运算性能优化的例子
 
       代码设计
        代码是代表系统中客观存在的事物名称、属性或状态的一个或一组有序符号,它应易于计算机和人进行识别和处理。组成代码的符号可以包括数字、字母或者混合组成。代码设计是一个科学管理的问题,设计出一个好的代码方案对于系统的开发工作是一件极为有利的事情。
               代码的功能
               (1)唯一标识功能。
               唯一标识是代码最基本的特征。在一个信息分类代码标准中,一个代码只能唯一地表示一个对象,而一个分类对象只能有一个唯一的代码。对于相同名称的人和物,也可以用不同的代码加以区分,这样便于信息的存储和检索。
               (2)分类功能。
               代码可以作为分类对象类别的标识。这是利用计算机进行分类统计的基础。
               (3)排序功能。
               当按分类对象产生的时间、所占空间或其他方面的顺序关系分类,并赋予不同的代码时,代码可以作为排序的标识。
               代码种类
               实际应用中,常常根据需要采用两种或两种以上基本代码的组合。根据代码的组织特点和编排方式来分类,一般有以下几种。
               (1)顺序码。
               顺序码又被称为系列码,它用一串连续的数字来代表系统的实体或实体属性。顺序码是一种无实义的代码,这种代码只作为分类对象的唯一标识,只代替对象名称,而不提供对象的任何其他信息。
               顺序码的优点是短小精悍,易于管理。缺点是不能反映代码对象的特征,代码本身无任何含义。另外,由于代码按顺序排列,新增加的数据只能排在最后,删除数据则要造成空码,缺乏灵活性,所以通常作为其他代码的一个组成部分。
               (2)区间码。
               区间码单代码对象的特点把代码分成若干个区段,每一个区段表示代码对象的一个类别。它的优点是信息处理比较可靠,排序、分类、检索等操作易于进行,但这种代码的长度与它分类属性的数量有关,有时可能造成很长的码。
               (3)助忆码。
               助忆码用文字、数字或文字数字相结合来描述对象。它用可以帮助记忆的字母和数字来表示代码对象,所以它的优点是直观、便于记忆和使用,甚至可以通过联系帮助记忆,缺点是不利于计算机处理。当代码对象较多时也容易引起联想出错,所以这种代码主要用于数量较少的人工处理系统。
               助忆码适用于数据项数目较少的情况(一般少于50个),否则可能引起联想出错。此外,太长的助忆码占用存储容量过多,也不宜使用。
               (4)缩写码
               缩写码把人们习惯使用的缩写直接用于代码,简单、直观,便于记忆和使用。
               此外,根据代码所选用的符号类型,代码又分为字符码、数字码和混合码。
               代码设计的原则
               (1)唯一性。
               一个对象可能有多个名称,也可按不同的方式对它进行描述,但每个对象只能赋予它一个唯一的代码,每一个代码只能唯一地代表系统中的一个实体或实体属性。例如在人事档案管理中,人的姓名很可能出现重名,为了便于计算机识别,解决的方法就是编制职工号。
               (2)标准化。
               代码的设计要尽量采用国际或国内的标准,某些行业的代码还应该遵循行业内部的代码标准。采用标准的代码方案,不仅能够减少代码的工作量,还能一定程度上减少系统更新和维护的工作量,而且能够为今后的信息共享创造条件。
               (3)规范化。
               代码的结构、类型和代码格式必须严格统一,同时要有规律性,以便于计算机进行处理。在一个代码体系中,代码结构、类型、编写格式必须统一。规范化和标准化是息息相关的,在一个代码体系中,有关代码标准是代码设计的重要依据,已有的标准必须遵循。
               (4)合理性。
               代码设计必须与代码对象的分类体系相适应,以保证代码对代码对象的分类具有表示作用。
               (5)可扩展性。
               代码所对应的对象总是在不断的变化之中,因此代码体系本身应留有充分的余地,以备将来不断扩充的需要。当然,备用代码也不能留得过多,那样会增加处理的难度。
               (6)简单性。
               代码结构要尽可能简单,尽量缩短代码的长度,以方便利用、提高处理效率,并减少各种差错。
               (7)实用性。
               代码应尽可能反映对象的特点,以利于记忆、便于填写。例如,数据库代码名应尽量采用其相应汉字的汉语拼音头一个字母表示。
               代码设计的步骤
               代码设计需要科学的编码思路,进行严谨、全面的调查研究。一般来讲,代码设计可以遵循步骤如下。
               (1)确定编码对象和范围。
               罗列要进行编码的对象,划清编码范围。
               (2)调查是否已有标准代码。
               遵循标准化原则,在进行代码具体设计之前调查是否已有相关标准。
               (3)确定编排方式和符号类型。
               根据代码的使用范围、使用时间等实际情况选择代码的编排方式和代码符号类型。
               (4)考虑检错功能。
               代码出错将引发非常难以处理的问题,因此代码必须进行校验。代码校验利用在源代码的基础上增设一位或几位校验位的方式来实现。校验位通过事先规定好的数学方法计算出来。
               (5)编写代码表。
               编制代码表并作详细的说明,通知有关部门组织学习,以便正确使用。
 
       开发人员
        ①多媒体软件:项目负责人、学科教学专家、教学设计专家、软件工程师、多媒体素材制作专家和多媒体课件制作专家。
        ②多媒体电子出版物:策划编导、文字编辑、美术编辑、音乐编辑和多媒体编辑。
 
       可靠性
        (1)完备性。完备性评价指标及测量,如下表所示。
        
        完备性评价指标及测量
        (2)连续性。连续性评价指标及测量,如下表所示。
        
        连续性评价指标及测量
        
        (3)稳定性。稳定性评价指标及测量,如下表所示。
        
        稳定性评价指标及测量
        (4)有效性。有效性评价指标及测量,如下表所示。
        
        有效性评价指标及测量
        (5)可追溯性。可追溯性评价指标及测量,如下表所示。
        
        可追溯性评价指标及测量
        
 
       面向对象软件开发
        面向对象的方法从问题模型开始,然后进行识别对象、不断细化的过程。它从本质上就是迭代和渐增的。开发过程是一次次的迭代反复过程,随着迭代的进行,系统功能不断完善。典型的面向对象开发方法有RUP(Rational Unified Process)和XP(eXtreme Programming),两者各有侧重,适用于不同的场景。
        1.RUP
        RUP是Rational公司开发和维护的过程产品。RUP以适合于大范围项目和机构的方式捕捉了许多现代软件开发过程的最佳实践。RUP的三个关键特征为迭代(Iterative)、以架构为中心(Architecture-Centric)和用例驱动(Use-Case Driven)。
        RUP的6个基本最佳实践如下:
        .迭代式开发。RUP支持专注于处理生命周期中每个阶段中最高风险的迭代开发方法,极大地减少了项目的风险性。
        .需求管理。RUP描述了如何提取、组织和文档化需要的功能和限制。
        .使用基于构件的体系结构。RUP提供了使用新的及现有构件定义体系结构的系统化方法。
        .可视化软件建模。RUP开发过程显示了对软件如何可视化建模,捕获体系结构及构件的构架和行为。
        .验证软件质量。RUP帮助计划、设计、实现、执行和评估软件质量,并且不是事后型的。
        .控制软件变更。RUP开发过程描述了如何控制、跟踪和监控修改以确保成功的迭代开发。
        RUP的二维开发模型
        RUP可以用二维坐标来描述。横轴表示时间组织,是过程展开的生命周期特征,体现开发过程的动态结构,用来描述它的术语主要包括周期(Cycle)、阶段(Phase)、迭代(Iteration)和里程碑(Milestone);纵轴按内容组织为自然的逻辑活动,体现开发过程的静态结构,用来描述它的术语主要包括活动(Activity)、产物(Artifact)、工作者(Worker)和工作流(Workflow),如下图所示。
        RUP开发过程的各个阶段和里程碑
        RUP中的软件生命周期在时间上被分解为4个顺序的阶段,分别是初始阶段(Inception)、细化阶段(Elaboration)、构造阶段(Construction)和交付阶段(Transition)。每个阶段结束于一个主要的里程碑(Major Milestones);每个阶段本质上是两个里程碑之间的时间跨度。在每个阶段的结尾执行一次评估以确定这个阶段的目标是否已经满足。
        
        RUP的二维开发模型
        (1)初始阶段。
        初始阶段有时也称为先启阶段,该阶段的主要目标是为系统建立商业模型并确定项目的边界。任务包括识别和规避项目的主要风险,建立用例模型框架,并制订里程碑日期的阶段计划。
        初始阶段结束时是第一个重要的里程碑:生命周期目标(Lifecycle Objective)里程碑。该里程碑评估项目的基本可行性。
        (2)细化阶段。
        细化阶段的目标是分析问题领域,建立健全的体系结构基础,编制项目计划,淘汰项目中最高风险的元素。为了达到该目标,必须在理解整个系统的基础上,对体系结构做出决策,包括其范围、主要功能和诸如性能等非功能需求。同时为项目建立支持环境,包括创建开发案例,创建模板、准则并准备工具。
        细化阶段结束时是第二个重要的里程碑:生命周期结构(Lifecycle Architecture)里程碑。该里程碑为系统的架构建立了管理基准,并使项目小组能够在构建阶段中进行衡量。
        (3)构造阶段。
        在构造阶段,所有剩余的构件和应用程序功能被开发并集成为产品,所有的功能被详细测试。从某种意义上说,构造阶段是一个制造过程,其重点放在管理资源及控制运作以优化成本、进度和质量。
        构造阶段结束时是第三个重要的里程碑:初始操作(Initial Operational)里程碑。该里程碑确定产品是否已经可以部署到Beta测试环境。
        (4)交付阶段。
        交付阶段的重点是确保软件对最终用户是可用的。交付阶段可以跨越几次迭代,包括为发布做准备的产品测试,基于用户反馈的少量调整。在生命周期的这一点上,用户反馈应主要集中在产品调整、设置、安装和可用性问题上,所有主要的结构问题应该已经在项目生命周期的早期阶段解决了。
        在交付阶段的终点是第四个里程碑:产品发布(Product Release)里程碑。此时,要确定目标是否实现,是否应该开始另一个开发周期。在一些情况下,这个里程碑可能与下一个周期的初始阶段的结束重合。
        RUP的核心工作流
        RUP中有9个核心工作流,分为6个核心过程工作流(Core Process Workflows)和3个核心支持工作流(Core Supporting Workflows)。尽管6个核心过程工作流可能使人想起传统瀑布模型中的几个阶段,但应注意迭代过程中的阶段是完全不同的,这些工作流在整个生命周期中一次又一次被使用。9个核心工作流在项目中轮流被使用,在每一次迭代中以不同的重点和强度重复。
        (1)商业建模(Business Modeling)。
        商业建模工作流描述了如何为新的目标组织开发一个构想,并基于这个构想在商业用例模型和商业对象模型中定义组织的过程,角色和责任。
        (2)需求(Requirements)。
        需求工作流的目标是描述系统应该做什么,并使开发人员和用户就这一描述达成共识。为了达到该目标,要对需要的功能和约束进行提取、组织、文档化,最重要的是理解系统所解决问题的定义和范围。
        (3)分析和设计(Analysis&Design)。
        分析和设计工作流将需求转化成未来系统的设计,为系统开发一个健壮的结构并调整设计使其与实现环境相匹配,优化其性能。分析设计的结果是一个设计模型和一个可选的分析模型。设计模型是源代码的抽象,由设计类和一些描述组成。设计类被组织成具有良好接口的设计包(Package)和设计子系统(Subsystem),而描述则体现了类的对象如何协同工作实现用例的功能。
        (4)实现(Implementation)。
        实现工作流的目的包括以层次化的子系统形式定义代码的组织结构;以组件的形式(源文件、二进制文件、可执行文件)实现类和对象;将开发出的组件作为单元进行测试以及集成由单个开发者(或小组)所产生的结果,使其成为可执行的系统。
        (5)测试(Test)。
        测试工作流要验证对象间的交互作用,验证软件中所有组件的正确集成,检验所有的需求已被正确地实现,识别并确认缺陷在软件部署之前被提出并处理。RUP提出了迭代的方法,意味着在整个项目中进行测试,从而尽可能早地发现缺陷,从根本上降低了修改缺陷的成本。测试类似于三维模型,分别从可靠性、功能性和系统性能来进行。
        (6)部署(Deployment)。
        部署工作流的目的是成功地生成版本并将软件分发给最终用户。部署工作流描述了那些与确保软件产品对最终用户具有可用性相关的活动,包括软件打包、生成软件本身以外的产品、安装软件、为用户提供帮助。在有些情况下,还可能包括计划和进行beta测试版、移植现有的软件和数据以及正式验收。
        (7)配置和变更管理(Configuration&Change Management)。
        配置和变更管理工作流描绘了如何在多个成员组成的项目中控制大量的产物。配置和变更管理工作流提供了准则来管理演化系统中的多个变体,跟踪软件创建过程中的版本。工作流描述了如何管理并行开发、分布式开发、如何自动化创建工程。同时也阐述了对产品修改原因、时间、人员保持审计记录。
        (8)项目管理(Project Management)。
        软件项目管理平衡各种可能产生冲突的目标,管理风险,克服各种约束并成功交付使用户满意的产品。其目标包括为项目的管理提供框架,为计划、人员配备、执行和监控项目提供实用的准则,为管理风险提供框架等。
        (9)环境(Environment)。
        环境工作流的目的是向软件开发组织提供软件开发环境,包括过程和工具。环境工作流集中于配置项目过程中所需要的活动,同样也支持开发项目规范的活动,提供了逐步的指导手册,并介绍了如何在组织中实现过程。
        2.XP
        极限编程(eXtreme Propgramming,XP)由Kent Beck在1996年开创,是一种演进式的原型化方法,以最大化发挥人的能量为核心目标,具有沟通高效、设计简单、反馈迅速等特点,是一种轻量级、敏捷的过程方法。
        XP是一种高度动态的过程,它通过非常短的迭代周期来应对需求的变化。XP一般适用于需求不确定、变化快、项目历时不超过半年、人数不超过10个、在同一地点工作的中小型团队。
        XP的生命周期包括4个基本活动:编码(coding)、测试(testing)、聆听(listening)、设计(designing)。
        XP的4个价值目标:
        .沟通:让开发人员集体负责所有代码并结队工作,鼓励与客户及团队内部保持沟通。
        .简化:鼓励只开发当前的功能,避免过多的文档,专注于最小化解决方案,做好为新特性改变设计,在系统隐喻和代码规范下不断重构的准备。
        .反馈:通过单元测试和功能测试获得快速反馈。
        .勇气:提倡积极面对现实和处理问题的勇气,拥抱变化。
        XP的12个最佳实践:
        .有计划的开发。通过结合使用标有优先级的“故事”卡和技术估算,确定下一版本的功能。
        .小型发布。以小的增量版本经常向客户发布软件。
        .系统隐喻。隐喻是一个高层次的系统构想,需要不断地细化架构来指导全部开发。
        .简单设计。通过保持代码简单从而保证设计简单。不断地在代码中寻找复杂点并且立刻进行移除。
        .测试驱动。“先测试,后编码”。用户编写测试内容以对“故事”进行测试。程序员编写测试内容来发现代码中的任何问题。在编写代码前先编写测试内容。
        .重构。这是一项简化技术,用来移除代码中的重复内容和复杂之处。
        .结对编程。团队中的两个成员使用同一台计算机开发所有的代码。一个人编写代码或者驱动,另一个人同时审查代码的正确性和可理解性。
        .集体代码所有权。任何人都拥有所有的代码。提高代码透明度,增强团队合作精神。
        .持续集成。每天按任务多次创建和集成系统,随着需求变化,进行不断的回归测试。
        .每周40小时工作制。程序员在疲劳时无法保证最高效率。连续两周加班是绝对不允许的,否则会影响工作效率。
        .现场客户。至少有一名真实的客户全天候工作于开发环境中,帮助定义系统、编写测试内容并回答问题。
        .编码规范。程序员采用统一的编码规范。
 
       软件设计
               软件设计的任务
               在给定系统的需求规格说明书后,需要对软件的结构进行设计,并对设计的过程进行管理。在嵌入式系统的软件设计过程中,需要完成以下一些任务。
                      准备工作计划
                      在软件设计之前,首先要制订详细的工作计划,其内容包括:
                      .过程管理方案:包括软件开发的进度管理、软件规模和所需人年的估算、开发人员的技能培训等;
                      .开发环境的准备方案:包括开发工具的准备、开发设备的准备、测试装备的准备、分布式开发环境下的开发准则等;
                      .软硬件联机调试的方案:联调的起始时间、地点、人员和具体的准备工作;
                      .质量保证方案:包括质量目标计划、质量控制计划等;
                      .配置控制方案:包括配置控制文档的编写、配置控制规则的制订等。
                      确定软件的结构
                      设计软件的各个组成部分,包括:
                      .任务结构的设计:使用操作系统提供的函数,设计出一个最佳的任务结构;
                      .线程的设计;
                      .公共数据结构的设计:在确保系统一致性的基础上,设计出所需的公共数据;
                      .操作系统资源的定义;
                      .类的设计;
                      .模块结构设计:在设计时要充分考虑模块的划分、标准化、可重用和灵活性等;
                      .内存的分配与布局。
                      设计评审
                      对于软件设计的结果,进行一次设计评审,并在必要时对设计进行修正。具体内容包括:
                      .确认每件工作的执行方法是否恰当,其内容是否完善;
                      .确认该设计完成了系统需求规格说明书所要求的功能和服务;
                      .评估任务结构设计、评估类的设计、评估模块结构设计;
                      .对软件设计的结果进行总结,编写出相应的文档。
                      维护工作计划
                      执行软件设计工作控制,在每日、每周和每月的时间粒度上对进度进行控制,确保软件设计能够如期完成。
                      与硬件部门密切合作、相互协调
                      根据工作计划中的安排,定期与硬件部门召开会议,协调各自的进展。如果软件规格说明书发生了变化,立即进行调整,重新进行软件设计。
                      控制工作的结果,把工作记录存档
                      掌握当前的工作进展情况,尽早地发现和分析问题,并采取相应的措施。对各种事件进行跟踪记录,包括:
                      .执行过程控制,跟踪进展情况并定期记录、存档。
                      .执行质量控制,保留质量记录。
                      .记录产品的配置、版本变化、bug的发现和处理等信息。
               软件架构设计
               软件架构也称为软件体系结构,需要考虑如何对系统进行分解,对分解后的组件及其之间的关系进行设计,满足系统的功能和非功能需求。软件架构形成过程如下图所示。
               
               架构的形成过程概要
               软件架构设计需要从用户业务需求、未来应用环境、需求分析、硬件基础、接口输入、数据处理、运算或控制规律、用户使用等方面进行综合、权衡和分析基础上产生。面向某种问题的架构一旦确定就很难改变,随后的架构设计需要通过一系列的迭代开发完善,使得软件架构日趋成熟、稳定。
               软件架构的重要作用也在于控制一个软件系统的使用、成本和风险。好的架构要求是和谐的软件架构,包括与上一级系统架构相互和谐、与系统中同一级的其他组件架构互相和谐,确保系统满足性能、可靠性、安全性、信息安全性和互操作性等方面的关键要求,也具有可扩展、可移植性,从而为一个软件带来长久的生命力。
               在大量开发实践中,有很多广泛使用并被普遍接受的软件架构设计原则,这些原则独立于具体的软件开发方法,主要包括抽象、信息隐藏、强内聚和松耦合、关注点分离等。
               (1)抽象:这是软件架构的核心原则,也是人们认识复杂客观世界的基本方法。抽象的实质是提取主要特征和属性,从具体的事务中通过封装来忽略细节,并且运用这些特征和属性,描述一个具有普遍意义的客观世界。软件架构设计中需要对流程、数据、行为等进行抽象。复杂系统含有多层抽象,从而有多个不同层次架构。
               (2)信息隐藏:包括局部化设计和封装设计。局部化设计就是将一个处理所涉及到的信息和操作尽可能地限制在局部的一个组件中,减少与其他组件的接口。而封装设计是将组件的外部访问形式尽可能简单、统一。
               (3)强内聚和松耦合:强内聚是指软件组件内的特性,即组件内所有处理都高度相关,所有处理组合在一起才能组成一个相对完整的功能。而松耦合是指软件组件之间的特性,软件组件之间应尽量做到没有或极少的直接关系,使其保持相对独立,这样使得未来的修改、复用简单,修改之后带来的影响最小。
               (4)关注点分离:所谓关注点是软件系统中可能会遇到的多变的部分。如为适应不同运行接口条件,需要进行适应性的参数调整和驱动配置。关注点分离设计是将这部分组件设计成为相对独立的部分,使未来的系统容易配置和修改。而核心的部分可以保持一个相对独立的稳定状态。如果功能分配使得单独的关注点组件足够简单,那么就更容易理解和实现。但“展示某些关注点得到满足时,可能会影响到其他方面的关注点,但架构师必须能够说明所有关注点都已得到满足”。
               以上的原则中,删除需求细节或对细节进行抽象是最重要的工作,为用户的需求创建抽象模型,通过抽象将特殊问题映射为更普遍的问题类别,并识别各种模式。
               软件架构设计使用纵向分解和横向分解两种方式。纵向分解就是分层,横向分解就是将每一个层面分成相对独立的部分。经过分解之后,可以将一个完整的问题分解成多个模块来解决。模块是其中可分解、可组装,功能独立、功能高度内聚、之间低耦合的一个组件。
               类似于建筑架构,软件架构也决定了软件产品的好用、易用、可靠、信息安全、可扩展、可重用等特性,好的软件架构也给人完整、明确、清晰等赏心悦目的感觉,具有较长的生命力。
               架构设计是围绕业务需求带来的问题空间到系统解决空间第一个顶层设计方案。按照抽象原则,在这个阶段进行的架构设计关注软件设计环节抽象出来的重要元素,而不是所有的设计元素。在架构设计时将软件这些要素看作是黑盒,架构设计需要满足黑盒的外部功能和非功能需求的目标。一个软件的架构设计首先为软件产品的后续开发过程提供基础,在此基础上可将一个大规模的软件分解为若干子问题和公共子问题。而一般意义的软件设计是软件的底层设计,开发人员需要关注各子问题或要素的进一步分解和实现,是根据架构设计所定义的每个要素的功能、接口,进一步实现要素组件内部的配置、处理和结构。在遵守组件外部属性前提下,考虑实现组件内部的细节及其实现方法。对于其中的公共子问题,形成公共类和工具类,从而可以达到重用的目的。
               一般的软件构架是根据需求自上而下方式来设计,即首先掌握和研究利益相关方的关键需求,基本思路是首先进行系统级的软件架构设计,需要将软件组件与其外部环境属性绑定在一起,关注软件系统与外部环境的交联设计;其次将一个大的系统划分成各组成部分,这些部分可以按照架构设计的不同方法,分为层次或成为模块;之后再开始研究所涉及到的要素,再实现这些要素以及定义这些要素之间的关系。
               在实际工作中,软件构架也可采用自底向上的方法,前提是已经建立了一个成熟稳定的软件架构,也可以称之为“模式”。模式是组织一级设计某一类具体问题的顶层思路,是为了解决共有问题解的方案模板,但并不是一个问题的设计或设计算法。
               模式常常整合在一起使用,提供解决更大、更复杂问题的解决方案,而组成一个解决问题的通用框架。框架往往提供统一平台和开发工具,而且已经高效地利用了已经经过验证的模式、技术和组件。在新软件系统的设计中指定沿用或重用这种架构框架,这时其他重要元素可以在这个架构基础上针对新的需求进行扩展,有时是针对性地进行参数化设计。所以在架构设计中可以借用模式的概念进行设计,采用成熟的先进的设计框架和工具提高开发的效率,保证设计正确性。
               下图所示是针对架构设计中非功能需求的多维度分析,从中可知任何一个因素的变化都会带来对其他因素的影响。实际上软件架构设计属于软件设计过程的一部分,但超越了系统内部的算法和数据结构的详细设计。
               
               架构的多维度分析
               在架构设计阶段,需要定义边界条件、描述系统组织结构、对系统的定量属性进行约束、帮助对模型进行描述并基本构造早期的原型、更准确地描述费用和时间的评估。
               软件设计方法
               在将系统分解为各个组件的过程中,需要采取不同的策略,而每个策略则关注不同的设计概念。根据分解过程中所采用的不同策略,设计方法有基于功能分解的设计方法、基于信息隐藏的设计方法和基于模型驱动开发的设计方法等分类。
               (1)基于功能分解的设计方法。实时结构化分析与设计采用了功能分解,系统被分解为多个函数,并且以数据流或控制流的形式定义函数之间的接口;基于并发任务结构化的设计(Design Approach for Real-Time Systems,DARTS)提供了任务结构化标准,辅助人员确定系统中的并发任务,并指导定义任务接口。
               (2)基于信息隐藏的设计方法。面向对象(Object Oriented,OO)设计方法将数据和数据上操作封装在对象实体中,对象外界不能够直接对对象内部进行访问和操作,只能通过消息间接访问对象,符合人类思维方式,提高软件的扩展性、维护性和重用性。
               (3)基于模型驱动开发的设计方法。通过借助有效的(Model Driven Development,MDD)工具,构建和维护复杂系统的设计模型,直接产生高质量的代码,将开发的重心从编码转移到设计。当前使用较为广泛的MDD工具有IBM公司的Rhapsody。
 
       设计模式
        “每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。设计模式的核心在于提供了相关问题的解决方案。
        设计模式一般有如下4个要素。
        (1)模式名称(pattern name)。模式名称应具有实际的含义,能反映模式的适用性和意图。
        (2)问题(problem)。描述了应该在何时使用模式,解释了设计问题和问题存在的前因后果。可能描述了特定的设计问题,如怎样用对象表示算法等;也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。
        (3)解决方案(solution)。描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。解决方案并不描述一个特定的具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。
        (4)效果(consequences)。描述了模式应用的效果及使用模式应权衡的问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。
        设计模式确定了所包含的类和实例,它们的角色、协作方式以及职责分配。每一个设计模式都集中于一个特定的面向对象设计问题或设计要点,描述了什么时候使用它,在另一些设计约束条件下是否还能使用,以及使用的效果和如何取舍。按照设计模式的目的可以分为创建型、结构型和行为型三大类,如下表所示。
        
        设计模式分类
               创建型设计模式
               创建型模式与对象的创建有关,抽象了实例化过程,它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。
               创建型模式包括面向类和面向对象两种。Factory Method(工厂方法)定义一个用于创建对象的接口,让子类决定实例化哪一个类。Abstract Factory(抽象工厂)提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。Builder(生成器)将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Factory Method使一个类的实例化延迟到其子类。Prototype(原型)用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。Singleton(单例)模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
               下面以抽象工厂模式和单例模式为例进行说明。
                      Abstract Factory(抽象工厂)
                      (1)意图。提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
                      (2)结构。抽象工厂模式的结构如下图所示。
                      
                      抽象工厂模式结构图
                      其中:
                      .AbstractFactory声明一个创建抽象产品对象的操作接口。
                      .ConcreteFactory实现创建具体产品对象的操作。
                      .AbstractProduct为一类产品对象声明一个接口。
                      .ConcreteProduct定义一个将被相应的具体工厂创建的产品对象,实现AbstractProduct接口。
                      .Client仅使用由AbstractFactory和AbstractProduct类声明的接口。
                      (3)适用性。Abstract Factory模式适用于:
                      .一个系统要独立于它的产品的创建、组合和表示时。
                      .一个系统要由多个产品系列中的一个来配置时。
                      .当要强调一系列相关的产品对象的设计以便进行联合使用时。
                      .当提供一个产品类库,只想显示它们的接口而不是实现时。
                      Singleton(单例)
                      (1)意图。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
                      (2)结构。单例模式的结构如下图所示。
                      
                      单例模式结构图
                      其中:Singleton指定一个Instance操作,允许客户访问它的唯一实例,Instance是一个类操作;可能负责创建它自己的唯一实例。
                      (3)适用性。Singleton模式适用于:
                      .当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
                      .当这个唯一实例应该是通过子类化可扩展的,并且客户无须更改代码就能使用一个扩展的实例时。
               结构型设计模式
               结构型模式处理类或对象的组合,涉及如何组合类和对象以获得更大的结构。结构型类模式采用继承机制来组合接口或实现。一个简单的例子是采用多重继承方法将两个以上的类组合成一个类,结果这个类包含了所有父类的性质。这一模式尤其有助于多个独立开发的类库协同工作。其中一个例子是类形式的Adapter(适配器)模式。一般来说,适配器使得一个接口与其他接口兼容,从而给出了多个不同接口的统一抽象。为此,类Adapter对一个adaptee类进行私有继承。这样,适配器就可以用adaptee的接口表示它的接口。对象Adapter依赖于对象组合。
               下面以适配器模式和代理模式为例进行说明。
                      Adapter(适配器)模式
                      (1)意图。将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
                      
                      类适配器结构图
                      (2)结构。类适配器使用多重继承对一个接口与另一个接口进行匹配,其结构如上图所示。对象适配器依赖于对象组合,其结构如下图所示。
                      
                      对象适配器结构图
                      其中:
                      .Target定义Client使用的与特定领域相关的接口。
                      .Client与符合Target接口的对象协同。
                      .Adaptee定义一个已经存在的接口,这个接口需要适配。
                      .Adapter对Adaptee的接口与Target接口进行适配。
                      (3)适用性。Adapter模式适用于:
                      .想使用一个已经存在的类,而它的接口不符合要求。
                      .想创建一个可以服用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
                      .(仅适用于对象Adapter)想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
                      Proxy(代理)模式
                      (1)意图。为其他对象提供一种代理以控制对这个对象的访问。
                      (2)结构。代理模式的结构如下图所示。
                      
                      代理模式结构图
                      其中:
                      .Proxy保存一个引用使得代理可以访问实体;提供一个与Subject的接口相同的接口,使代理可以用来代替实体;控制对实体的存取,并可能负责创建和删除它;其他功能依赖于代理的类型:Remote Proxy负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求;Virtual Proxy可以缓存实体的附加信息,以便延迟对它的访问;Protection Proxy检查调用者是否具有实现一个请求所必需的访问权限。
                      .Subject定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
                      .RealSubject定义Proxy所代表的实体。
                      (3)适用性。Proxy模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候,常见情况有:
                      .远程代理(Remote Proxy)为一个对象在不同地址空间提供局部代表。
                      .虚代理(Virtual Proxy)根据需要创建开销很大的对象。
                      .保护代理(Protection Proxy)控制对原始对象的访问,用于对象应该有不同的访问权限的时候。
                      .智能引用(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以被自动释放;当第一次引用一个持久对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
                      结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些方法。因为可以在运行时刻改变对象组合关系,所以对象组合方式具有更大的灵活性,而这种机制用静态类组合是不可能实现的。
                      Composite(组合)模式将对象组合成树型结构以表示“部分—整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。它描述了如何构造一个类层次式结构,这一结构由两种类型的对象所对应的类构成。其中的组合对象使得用户可以组合基元对象以及其他的组合对象,从而形成任意复杂的结构。proxy(代理)模式为其他对象提供一种代理以控制对这个对象的访问,其中,proxy对象作为其他对象的一个方便的替代或占位符。它的使用可以有多种形式,例如可以在局部空间中代表一个远程地址空间中的对象,也可以表示一个要求被加载的较大的对象,还可以用来保护对敏感对象的访问。proxy模式还提供了对对象的一些特有性质的一定程度上的间接访问,从而可以限制、增强或修改这些性质。Flyweight(享元)模式运用共享技术有效地支持大量细粒度的对象,为了共享对象定义了一个结构。至少有两个原因要求对象共享:效率和一致性。Flyweight的对象共享机制主要强调对象的空间效率。使用很多对象的应用必须考虑每一个对象的开销。使用对象共享而不是进行对象复制,可以节省大量的空间资源。但是,仅当这些对象没有定义与上下文相关的状态时,它们才可以被共享。Flyweight的对象没有这样的状态。任何执行任务时需要的其他一些信息仅当需要时才传递过去。由于不存在与上下文相关的状态,因此Flyweight对象可以被自由地共享。
                      Facade(外观)模式为子系统中的一组接口提供一个一致的界面,定义了一个高层接口,这个接口使得这一子系统更加容易使用。该模式描述了如何用单个对象表示整个子系统。模式中的facade用来表示一组对象,facade的职责是将消息转发给它所表示的对象。Bridge(桥接)模式将对象的抽象和其实现分离,从而可以独立地改变它们。
                      Decorator(装饰)模式描述了如何动态地为对象添加一些额外的职责。该模式采用递归方式组合对象,从而允许添加任意多的对象职责。例如,一个包含用户界面组件的Decorator对象可以将边框或阴影这样的装饰添加到该组件中,或者它可以将窗口滚动和缩放这样的功能添加到组件中。可以将一个Decorator对象嵌套在另外一个对象中,就可以很简单地增加两个装饰,添加其他的装饰也是如此。因此,每个Decorator对象必须与其组件的接口兼容并且保证将消息传递给它。Decorator模式在转发一条信息之前或之后都可以完成它的工作(例如绘制组件的边框)。许多结构型模式在某种程度上具有相关性。
               行为型设计模式
               行为模式对类或对象怎样交互和怎样分配职责进行描述,涉及算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。这些模式刻画了在运行时难以跟踪的复杂的控制流。它们将用户的注意力从控制流转移到对象间的联系方式上来。
               行为类模式使用继承机制在类间分派行为。本章包括两个这样的模式,其中Template Method(模板方法)较为简单和常用。Template Method是一个算法的抽象定义,它逐步地定义该算法,每一步调用一个抽象操作或一个原语操作,子类定义抽象操作以具体实现该算法。另一种行为类模式是Interpreter(解释器)模式,它将一个文法表示为一个类层次,并实现一个解释器作为这些类的实例上的一个操作。
               行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任一个对象都无法单独完成的任务。这里一个重要的问题是对等的对象。
               如何互相了解对方。对等对象可以保持显式的对对方的引用,但那会增加它们的耦合度。在极端情况下,每一个对象都要了解所有其他的对象。Mediator(中介者)模式用一个中介对象来封装一系列的对象交互,在对等对象间引入一个mediator对象以避免这种情况的出现。mediator提供了松耦合所需的间接性。
               Chain of Responsibility(责任链)使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。Chain of Responsibility模式提供更松的耦合,让用户通过一条候选对象链隐式地向一个对象发送请求。根据运行时刻情况任一候选者都可以响应相应的请求。候选者的数目是任意的,可以在运行时刻决定哪些候选者参与到链中。
               Observer(观察者)模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。典型的Observer的例子是Smalltalk中的模型/视图/控制器,其中一旦模型的状态发生变化,模型的所有视图都会得到通知。
               其他的行为对象模式常将行为封装在一个对象中并将请求指派给它。Strategy(策略)模式将算法封装在对象中,这样可以方便地指定和改变一个对象所使用的算法。Command(命令)模式将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。Memento(备忘录)模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便在以后可将该对象恢复到原先保存的状态。State(状态)模式封装一个对象的状态,使得对象在其内部状态改变时可改变它的行为,对象看起来似乎修改了它的类。Visitor(访问者)模式表示一个作用于某对象结构中的各元素的操作,使得在不改变各元素的类的前提下定义作用于这些元素的新操作。Visitor模式封装分布于多个类之间的行为。Iterator(迭代器)模式提供一种方法顺序访问一个聚合对象中的各个元素,且不需要暴露该对象的内部表示。Iterator模式抽象了访问和遍历一个集合中的对象的方式。
               下面以中介者模式和观察者模式为例进行说明。
                      Mediator(中介者)
                      (1)意图。用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
                      (2)结构。中介者模式的结构图如下图所示。
                      
                      中介者模式结构图
                      其中:
                      .Mediator(中介者)定义一个接口用于各同事(Colleague)对象通信。
                      .ConcreteMediator(具体中介者)通过协调各同事对象实现协作行为;了解并维护它的各个同事。
                      .Colleague class(同事类)知道它的中介者对象;每一个同事类对象在需要与其他同事通信的时候与它的中介者通信。
                      (3)适用性。Mediator模式适用于:
                      .一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
                      .一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
                      .想定制一个分布在多个类中的行为,而又不想生成太多的子类。
                      Observer(观察者)
                      (1)意图。定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
                      (2)结构。观察者模式的结构图如下图所示。
                      
                      观察者模式结构图
                      其中:
                      .Subject(目标)知道它的观察者,可以有任意多个观察者观察同一个目标;提供注册和删除观察者对象的接口。
                      .Observer(观察者)为那些在目标发生改变时需获得通知的对象定义一个更新接口。
                      .ConcreteSubject(具体目标)将有关状态存入各ConcreteObserver对象;当它的状态发生改变时,向它的各个观察者发出通知。
                      .ConcreteObserver(具体观察者)维护一个指向ConcreteSubject对象的引用;存储有关状态,这些状态应与目标的状态保持一致;实现Observer的更新接口,以使自身状态与目标的状态保持一致。
                      (3)适用性。Observer模式适用于:
                      .当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。
                      .当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时。
                      .当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的。
   题号导航      2016年下半年 系统架构设计师 下午试卷 论文   本试卷我的完整做题情况  
1 /
2 /
3 /
4 /
 
第2题    在手机中做本题