软件体系结构:面向思维的解析方法(txt+pdf+epub+mobi电子书下载)


发布时间:2020-05-27 00:25:24

点击下载

作者:沈军

出版社:东南大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

软件体系结构:面向思维的解析方法

软件体系结构:面向思维的解析方法试读:

内容提要

本书采用系统化思维策略,解析软件体系结构相关知识。第1章概述,给出软件体系结构的定义及其内涵、涉及的相关内容及其逻辑关系以及本书的组织结构及应有的学习策略。第2章主要解析软件体系结构赖以建立的基础—软件模型。第3章主要解析用以建立软件体系结构的基本构件—设计模式。第4章主要解析面向同族系统和异族系统的两类软件体系结构基本风格及其关系,同时解析由它们衍生的各种典型风格及其具体应用。第5章主要解析面向Web应用的新3-Tirer/n-Tirer体系结构的基本工作原理和面向服务的体系结构(SOA)的基本工作原理,并简单解析面向领域的体系结构的基本思想。第6章主要解析软件体系结构的若干基本描述方法,包括非形式化描述方法和形式化描述方法,并通过具体设计工具和应用案例,解析软件体系结构的基本设计方法。第7章主要解析软件体系结构的发展,基于归纳和演绎两种思维策略重点解析可恢复程序语句组件模型与SOA深入以及云计算。元模型与MDA。

本书主要面向普通高等院校计算机学院、软件学院的高年级本科生、硕士生相关课程的教学,也可以满足对计算机软件技术感兴趣的普通读者的自学需求。

第1章 概述

本章主要给出软件体系结构的定义,解析软件体系结构的重要性以及涉及的主要内容。在此基础上,给出本书的组织结构以及应有的学习策略。

1.1 什么是软件体系结构

所谓体系结构(Architecture),一般是指一种思想的抽象,它通过一些原则和方法等具体体现。这种解释本质上是强调了体系结构的“体系”的涵义,主要侧重于抽象级别较高的概念层面。体系结构的另一种解释,是指系统的基本组成元素及其相互关系的抽象。显然,这种解释本质上是强调了体系结构的“结构”的涵义,主要侧重于抽象级别较低的技术层面。

软件体系结构,是体系结构概念在软件上的投影或具体应用。软件体系结构通常取体系结构定义中的第二种解释,即软件体系结构是指一系列关于软件系统组织的重大决策,是软件系统结构的结构,由软件元素、元素的外部可见属性及元素间的关系组成。

对于体系结构,理解的核心在于抽象程度。首先,体系结构并不等同于系统结构的具体设计说明,而是该说明的一种抽象表示。其次,根据上下文或特定应用背景,要区分究竟侧重于“体系”还是“结构”,由此决定其具体内涵。

1.2 为什么要研究软件体系结构

得益于计算机工具的特殊结构,计算机应用都是通过构造软件系统实现。随着人类文明信息化程度的深入,企业IT应用越来越复杂,导致软件系统越来越庞大。众所周知,如果要建造一座摩天大厦,首先必须给出其设计蓝图,然后才能在设计蓝图的指导下,一步步按照工程要求进行建造,绝不是仅仅凭想象随意施工。与之相似,如果要构造一个庞大、复杂的软件系统,显然也必须在具体实施之前进行其蓝图的设计,即进行软件体系结构设计。这是复杂大系统构造方法与简单小系统构造方法之间的本质区别。

另外,相对于其他系统而言,软件系统有其特殊性。一方面,软件构造的基本方法和技术,随着人们对软件认识程度的深入不断发展,目前已诞生了各种各样异质的方法和技术,并且应用系统赖以执行的基础环境,包括硬件系统和系统软件平台,也存在异质性。另一方面,应用具有恒变性,业务规则多次被重新定义,新业务模型也屡屡出现。随着应用的不断发展,多年的反复改造使很多系统之间具有复杂的交叉依赖,异质度和冗余度相当高。不同时期采用不同技术和平台构建的软件系统逐渐形成“信息孤岛”,企业应用程序环境变成一个大杂烩,其维护和集成成本居高不下。特别是面对新世纪全球化、虚拟化的商业环境,许多企业由于不能及时提供所需功能而错失良机。因此,如何匹配技术的动态性和应用的动态性,显然必须从一个战略高度进行分析。也就是说,为了有效地“重用”现有系统并与之“协同”工作,及时开发新的业务功能,减轻成本压力,必须为企业计算建立十分“灵活”和“敏捷”的“完美”结构,使企业软件环境内部保持恒久的“有序度”。企业软件体系结构正是实现这一需求的有力武器。

更进一步的认识是,软件体系结构也是软件自身发展的使然。按照事物发展的普遍规律,从软件发展的脉络来看,其目前正处于由初级阶段到高级阶段的过渡时期。软件体系结构的建立以及以软件体系结构为核心的新一代软件开发方法学的研究与发展标志着该学科的逐步成熟。

1.3 软件体系结构涉及的内容

软件体系结构研究源自于软件工程研究,目前已基本上相对独立。事实上,软件工程主要关注软件开发的整个过程,涉及软件开发整个过程的各个方面,包括人员、资源和费用等等非技术的因素。而软件体系结构主要关注软件系统本身的抽象结构定义和设计。从认识论层面可以将软件工程和软件体系结构看做针对软件开发的宏观视图和(某个)微观视图及它们的辩证关系。

按照给出的软件体系结构的定义,软件体系结构应该涉及基本软件构造模型、设计模式、基本风格、典型案例、描述与设计等内容。其中,基本软件构造模型是软件体系结构建立的基础和最小粒度的元素。不同的软件模型蕴涵了其直接支持的软件体系结构,同时,软件模型发展的轨迹也反映了软件体系结构的发展历程和进化理念,两者在思想本质上具有通约性。设计模式是面向对象软件设计中关于对象关系和结构的一种设计经验的抽象,它能有效支持软件结构对于应用的恒变特性的适应性,提高软件系统的维护能力。尽管设计模式来源于面向对象软件设计,但其思维本质对软件体系结构如何适应应用的恒变特性也有同样的指导意义。软件体系结构中往往在某个局部大量采用设计模式,以重用久经考验的设计经验。软件体系结构基本风格抽象了一些经过实验验证的有效的软件体系结构基本设计方法,这些方法广泛应用于现实软件系统的设计之中。典型案例是指软件体系结构的各种具体实现,它集成了软件模型、设计模式和软件体系结构基本风格。软件体系结构描述是指通过一定的语言或符号,从形式上定义软件体系结构,实现软件体系结构蓝图的书面具现,从而对软件体系结构进行交流、审核和分析验证。软件体系结构设计是指针对某个具体系统或应用,利用软件模型、设计模式和软件体系结构基本风格等知识以及相应描述手段,进行艺术创造,创建出能够处理该具体系统或应用的一种完美的蓝图。

软件体系结构所涉及的各部分内容从逻辑上构成一个整体,如图1-1所示。图1-1 软件体系结构涉及的内容及其关系

1.4 本书的组织结构及学习策略

本书后面的章节,根据图1-1所示的逻辑关系展开。第2章到第4章构成一个思维单元,它们从细粒度到粗粒度,建立了软件体系结构三个层次的视图。第5章单独构成一个思维单元,解析第一个思维单元的具体应用。第6章单独构成第三个思维单元,从理论上和形式上对软件体系结构进行描述,并且在第一个思维单元的知识和描述方法与手段的基础上,解析如何针对一个具体应用问题进行其软件体系结构设计。第7章单独构成一个思维单元,解析软件体系结构的最新发展。

针对本书的知识体系,宏观上可以采用面向应用和面向设计两种基本学习策略。采用面向应用策略,主要学习第1章到第5章的内容,以理解目前软件开发领域中常用软件体系结构的相关概念和知识为核心。采用面向设计策略,应该学习所有内容,一方面理解目前软件开发领域中常用软件体系结构的相关概念和知识;另一方面,在此基础上能够设计具体的软件体系结构。通俗地讲,面向应用策略主要培养工程师,而面向设计策略主要培养设计师。微观上,应该采用基于模式的学习策略,即重点认识具体模型、方法和技术背后蕴涵的各种模式(隐性知识)及其在这些具体模型、方法和技术中的应用(模式建构),在模式层次进行学习。具体而言,映射到每一章内容的学习,首先学习各种具体模型、方法和技术,包括它(们)的概念、术语和基本原理等。然后再深入一步,思考并发掘这些概念、术语和基本原理中共同采用的一些问题处理思想,例如分层处理思想、抽象粒度等。最后还要在此基础上,对各种概念、术语和基本原理等进行比较和分析,认识它们之间的发展和演化的脉络及其原因,例如软件模型发展和演化的脉络及其原因等。针对本书整个内容的学习,应该在普通知识(显性知识)和模式知识(隐性知识)两个层面展开学习。首先,在每个层面注意从各种具体知识点中形成知识链,例如普通知识层面中的知识关系、模式知识层面中的模式关系。然后,将两个知识面综合,形成知识球,从而达到知识的触类旁通和融会贯通。

1.5 本章小结

本章首先给出软件体系结构的定义及其重要性并解析其内涵,然后给出软件体系结构涉及的相关内容并解析其逻辑关系。在此基础上,给出本书主体部分的组织结构并解析应有的学习策略,从而为本书的展开建立思维基础。

习题

1.什么是软件体系结构?如何理解“系统结构”和“系统结构的结构”两者之间的区别?

2.建立一个精美的小狗窝与建立一座庞大的摩天大厦,在策略上究竟有何本质不同?

3.如何理解软件体系结构和软件工程之间的区别和联系?

4.如何理解软件模型、设计模式和基本风格之间的关系?

5.什么是应用的恒变特性?你认为对应用的恒变特性的灵活适应是不是软件体系结构设计的核心,为什么?

6.事物初级阶段发展和高级阶段发展分别基于什么样的思维特征?

第2章 软件体系结构基础:软件模型

本章主要解析软件体系结构赖以建立的基础——软件模型。首先,给出软件模型的定义,解析软件模型对软件体系结构的作用。然后,梳理软件模型发展脉络,解析各种软件模型的基本原理。最后,对软件模型进行深入认识和思考。

2.1 什么是软件模型

所谓模型(Model),一般是指客观世界中存在事物的一种抽象。事物可以是具体的,例如房子、人等;也可以是抽象的,例如思想、算法等。

模型一般都需要通过某种形式表达出来,以便交流。从形式上看,模型的表达有文字(包括自然语言和数学语言)、图形(图形语言)或图文混合。用数学语言表达的模型称为数学模型,这种模型的形式化级别最高,一般用来描述事物的抽象本质,刻画事物内在的稳定规律。例如,描述了匀加速直线运动的本质,刻画了时间、加速度和路程之间的稳定变化规律。相对于数学模型,用自然语言或图形语言表达的模型的形式化级别较低,一般用来描述对事物的某种认识和理解,表示一种观点。例如,一座房子的结构、一个系统的结构或某种思想体系等。数学模型是精确的定量描述,其他模型则是一种定性描述。

所谓软件模型(Software Model),是指软件的一种抽象,目前一般通过非数学模型来描述。相对于其他事物,软件具有特殊性。这主要体现在软件描述的基本元素的一致性,也就是说,无论如何描述软件,同构模型中描述的最基本单元的抽象都是统一的。本书中,将这种统一的基本单元的抽象称为软件模型,而将软件系统的抽象称为软件体系结构。因此,软件模型可以看做一种元模型(Meta Model)。

2.2 软件模型对软件体系结构的作用

软件模型作为软件组成的最基本单元的抽象,它既反映了软件体系结构构建的核心思想,也奠定了软件体系结构构建的基础。一方面,它定义了软件体系结构构建的基本单元元素的形态;另一方面,它定义了基本单元元素之间关系的基本形态。不同的软件模型隐式地定义了软件体系结构构建的不同方法。

2.3 软件模型的发展脉络

审视软件模型从诞生到发展的历程,尽管各种软件模型的发展存在一定的时间交叉,但从其是否作为软件构造技术的主体支撑技术来说,软件模型的发展基本上符合图2-1所示的轨迹。图2-1 软件模型的发展轨迹

软件模型的发展脉络也清晰地体现了计算机应用发展的历程以及计算机技术发展的历程。计算机应用的发展和计算机技术的发展相辅相成,一方面,计算机应用的发展对计算机技术提出了新的要求,促进计算机技术的发展;另一方面,计算机技术的发展又为新型计算机应用的发展提供了基础,促进计算机应用的发展。作为计算机技术之一的软件技术在计算机应用和其他计算机技术之间建立起桥梁。因此,软件模型的发展事实上也就是不断动态地黏合应用与技术。图2-2给出了计算机应用、计算机技术和软件模型的关系。图2-2 技术、应用和软件模型的关系

2.4 软件模型解析

本节按照软件模型的发展轨迹,主要解析各种软件模型的基本原理及其思维本质,并阐述其对软件体系结构建立的影响。

2.4.1 功能模型

功能模型(Function Model)也可以称为过程模型或函数模型,它是模型化软件构建方法的第一个基本模型。功能模型的基本原理是将一个系统分解为若干个基本功能模块,基本功能模块之间可以按需进行调用。基本功能模块集合及其调用关系集合构成一个系统的模型。

功能模型诞生于20世纪60年代,它强调了对程序中数据处理(功能)的抽象,通过功能分解和综合的方法,降低系统构造的复杂度。从而实现一体式程序体系结构向结构化程序体系结构的转变,并建立了结构化软件设计方法。

功能模型的核心之一是基本功能模块的抽象及耦合。事实上,基本功能模块是一种处理方法的抽象,这种方法独立于其处理的具体数据集,建立在抽象数据集上。通过将抽象数据集具体化,就可以实现处理方法在某个具体数据集上的作用,从而实现处理方法的重用。因此,基本功能模块的抽象一般需要定义其处理的抽象数据集。具体实现中,基本功能模块一般有函数(Function)和过程(Procedure)两种形式,前者返回处理结果,后者不返回处理结果。抽象数据集称为形式参数,具体数据集称为实际参数。图2-3分别给出了基本功能模块在PASCAL语言和C语言中的实现。图2-3 基本功能模块的实现

基本功能模块的耦合,是指一个模块调用另一个模块时如何进行被调模块的抽象数据集的具体化以及被调模块如何返回其处理结果给主调模块。前者一般称为参数传递,后者称为函数返回。目前,参数传递和函数返回的实现方式基本上都是通过堆栈进行,图2-4给出了参数传递和函数返回实现的基本思想。按照传递的方式,参数传递基本上有值传递和地址传递两种。值传递将实际参数值复制到堆栈,地址传递则是将实际参数值存放的内存地址复制到堆栈。因此,当被调模块的抽象数据集具体化后,值传递方式不会因为被调模块的处理而改变原始的实际参数值,而地址传递方式由于被调模块的处理会通过实际参数值存放的内存地址而间接地作用于原实际参数,从而改变原始的实际参数值。图2-5是两种参数传递方式的C语言实现。图2-4 参数传递和函数返回实现的基本思想图2-5 两种参数传递方式的C语言实现

功能模型的核心之二是递归思想的具体实现。所谓递归(Recursion),是指用同一种处理方法(该方法通过缩小待处理数据集规模)来处理不断缩小规模的数据集,并通过不断综合小规模数据集的处理结果来得到大规模数据集的处理结果的一种问题处理方法。图2-6给出了递归方法的基本思想。图2-6 递归方法的基本思想

功能模型中递归思想体现在两个方面,一个是基本功能模块的递归应用,另一个是处理逻辑或数据组织方式的递归应用。基本功能模块的递归应用是将图2-6中的一种处理方法通过一个基本功能模块实现,将数据集规模作为基本功能模块的形式参数之一,这样在基本功能模块处理逻辑的定义中,显然需要在缩小后的数据规模上再调用其自身(使用同一种处理方法)。可见,递归是一种特殊的模块耦合关系,其主调模块和被调模块是同一个处理模块。图2-7给出了基本功能模块递归应用的一个具体案例。根据模块调用关系的不同,递归可以呈现多种具体应用形式,如图2-8所示。图2-7 基本功能模块递归应用具体案例——汉诺塔问题求解图2-8 递归的多种具体应用形式

处理逻辑的递归应用,是指将问题的整个处理逻辑看做数据集,将基本的处理逻辑看做处理方法,从而实现用基本的处理逻辑及其组合来实现处理不同复杂度问题的整个处理逻辑。功能模型建立了三种基本的处理逻辑:顺序、分支和循环。图2-9给出了它们的语义。图2-10解释了处理逻辑的递归应用内涵。由图2-10可知,程序A(大程序)和子程序B(小程序)在思维上具有显式通约性。图2-9 三种基本的处理逻辑及其语义图2-10 处理逻辑的递归应用内涵

数据组织方式的递归应用,是指将需要组织的全部数据看做数据集,将基本的数据组织类型看做处理方法,从而实现用基本的数据组织类型及其组合来实现不同规模的数据的结构化组织。在计算机中,数据组织是通过数据类型实现的,一般分为基本数据类型和复合数据类型两类。复合数据类型是基本数据类型的组合实现,这种组合实现就是建立在递归思维基础上。图2-11给出了C语言中的两类数据类型,图2-12给出了C语言中递归数据组织的具体应用。图2-11 C语言中的两类数据类型图2-12 C语言中递归数据组织的具体应用

由于功能模型侧重于功能部分,淡化了数据部分以及数据与功能之间的关系,因此对于大规模程序的构造,功能模型具有其固有的波动效应的缺陷。也就是说,如果一个模块或某个数据被修改了或被调整了,而又没有及时通知其他相关的模块,则会引起意想不到的影响。这种现象会在整个系统中产生连锁的反应(即波动效应),最终导致整个系统的不正确性。尽管功能模型存在固有的缺陷,并由此失去其主流技术的地位,但其模型化构造方法的建立以及模块化设计思想、递归构造思想的建立,对软件构造方法产生了深远的影响。

2.4.2 对象模型

对象模型(Object Model)诞生于20世纪80年代,它强调了对程序中数据组织的抽象,并将数据处理和数据组织进行统一考虑。对象模型以对象为核心,通过对象进行数据组织的抽象并实现数据组织和数据处理的统一,在此基础上建立面向对象的软件构造方法。因此,对象模型的基本原理是将一个系统分解为若干个对象,对象之间可以通过发送消息按需进行协作。对象集合及其协作关系集合构成一个系统的模型。

所谓对象(Object),是指客观世界中存在的东西,可以是具体的(例如:人、桌子、书等),也可以是抽象的(例如:缓冲池、堆等)。在计算机中,为了描述一个对象(或以对象进行数据组织),显然必须给出对象的形态(称为对象的型),并按需建立对象的各个具体实例(称为对象的值)。例如,C++语言中,以类(Class)描述一个对象的型,以变量描述对象的值。一个对象一般有静态属性和动态行为(即对象的职责),例如汽车有型号、颜色、价格、牌号、生产厂家等静态属性,有发动、刹车、转弯等动态行为等。因此,对象的型的描述中必须将该类对象的静态属性和动态行为描述清楚。其中静态属性对应于数据组织,动态行为对应于数据处理,从而以对象为核心,实现数据处理和数据组织的统一。图2-13给出了对象描述的基本视图。图2-14给出了C++语言中对象描述的具体案例。图2-13 对象描述的基本视图图2-14 C++语言中对象描述的具体案例

对象模型的核心之一是对数据类型的抽象。所谓数据类型的抽象,是指允许用户按需定义自己的数据类型并通过其进行数据组织,从而拓展某种程序设计语言固有的数据类型,为应用程序的构造带来灵活性。相对于传统固有的数据类型,拓展的数据类型称为抽象数据类型(Abstract Type)。一般来说,一种数据类型既要规定其数据的取值范围,又要定义其数据的基本运算操作。因此,对象模型中的对象机制可以用来定义抽象数据类型。其中,对象的属性描述对应于数据的取值范围,对象的行为描述对应于数据的基本运算操作。可见,对象本质上就是数据,对象的描述就是定义一种抽象数据类型。值得注意的是,抽象数据类型的定义体现了数据组织方式的递归应用特性。相对于功能模型中的复合数据类型,抽象数据类型的实现的灵活性和扩展性要强得多。

对象模型的核心之二是同构(或同族)对象关系的定义,这种关系体现在继承(Inherit-ance)和多态(Polymorphism)两个方面。所谓继承,是指同族对象中后代可以共享前代的某些属性及行为特征。例如,麻雀作为子类可以是其父类——鸟的一种类型,因此麻雀可以继承鸟类的一些属性及行为特征。同时,麻雀可以有自己的属性和行为特征,也可以改变父类的一些行为特征。所谓多态,是指对象的某种行为在各个具体实例中呈现不同的形态。例如,对于一个图形类,可以定义一个draw()行为。但是,对于直线、圆、矩形和椭圆等不同的图形,显然各自的draw()会呈现出不同的具体形态(绘制方法)。根据不同形态表现的时机,多态可以分为静态多态和动态多态。静态多态是指在具体实例建立前对象的行为描述中就已经将各种形态的表现定义清楚。这种多态形式一般用于一个对象的行为描述中。动态多态是指各种形态的表现要在具体实例建立后才能具体确定。这种多态形式一般用于具有继承关系的多个对象的行为描述中。此时,父类通常只给出抽象的行为(即对象可以做什么),而不定义具体行为(即对象究竟如何做),各个子类具体定义其对象应有的行为。这样当通过抽象引用概念性地要求对象做什么时,将会得到不同的行为,具体行为取决于子类对象的具体类型。在C++语言中,静态多态通过函数重载机制实现,动态多态通过虚函数机制实现。

对象模型通过对象机制统一了数据组织和数据处理两个方面,并建立了抽象数据类型构造的方法。然而,其抽象级别仍然较低,认识视野仍然局限于实现层次,对概念层次和规约层次的重视不够,从而使得基于对象模型的面向对象的设计思想和方法失去了巨大的效力。也就是说,抽象数据类型的具体实现仍然需要由具体的程序设计语言来体现,对象不能独立于语言。另外,对象模型通过继承机制强化了同族对象关系,而对异族对象关系并没有显式说明。因此,对于异构集成以及大规模软件的开发,对象模型暴露出它的不足之处。

2.4.3 组件模型

组件模型(Component Model)诞生于20世纪90年代,它在对象模型的基础上,强调了异族对象关系以及独立性问题。异族对象关系主要是指组件内部完成组件功能的对象不但是同族的,也可以是异族的。独立性是指组件建立在二进制基础上并独立封装,可以独立部署。组件模型的基本原理是以接口(Interface)为核心,通过接口抽象组件的行为,在此基础上建立面向接口的软件构造方法。组件集合及其协作关系集合构成一个系统的模型。

所谓接口,是指对象动态行为的集合。接口也支持继承机制。因此,相对于对象模型,组件模型更加重视在概念层次和规约层次上认识面向对象的方法和思想,强调对象是一组责任,具有可以被其他对象或对象自身调用的方法(即行为)。也就是说,将数据组织部分封装在内部,不对外暴露,而将针对数据的处理部分以接口形式对外暴露。

所谓组件,是指能完成特定功能并能独立部署的软件合成单元。一个组件一般具有一个或多个接口,每个接口的功能由一个或多个方法来体现。接口的具体功能(即其每个方法的具体行为)由组件对象实现。组件对象之间可以通过聚合和包容方式进行功能重用。图2-15是组件的基本结构。图2-16是组件功能重用的两种基本方式。图2-17(a)是Microsoft COM组件的封装结构,它支持DLL和EXE两种封装结构。图2-17(b)是COM组件的运行时结构。图2-15 组件基本结构图2-16 组件功能重用方式图2-17 Microsoft COM组件的基本结构

组件模型强调标准,以实现具有独立性的组件之间的集成。组件模型的标准一般称为软件总线(简称软总线,Software Bus),它定义组件的封装结构并提供基本的集成服务功能(例图2-18 组件集成的基本原理如命名服务、查找服务等等)。满足同一标准的组件可以通过软总线进行集成。目前,流行的组件模型标准有Microsoft的COM、SUN的Java Beans和OMG的CORBA。为了实现对组件的管理和集成,软总线除了提供各种基本服务功能外,还提供一些高级服务功能,例如属性服务、持久化服务、安全服务、事务服务等等。图2-18是组件集成的基本原理。其中,组件对象首先必须在软总线中进行注册,然后才能被使用。某个客户应用或一个对象需要使用某个组件对象时,也是通过软总线进行查找,然后再使用。因此,软总线充当组件对象集成的中介。

组件模型一般采用基于框架的程序构造方法。所谓框架(Framework),是指已实现部分功能的某类程序结构的实现。框架抽象了某类程序的结构,定义其中各个功能组件及其相互关系并实现部分功能。框架类似于生产线,通过框架构造程序,相当于按照生产线进行各种组件的装配。框架可以分为水平型、垂直型和复合文档型三种。水平型框架一般面向通用类程序的构造,与特定应用领域不相关,例如Visual C++支持的各种工程类型就是各种水平型框架。垂直型框架一般面向特定应用领域,它抽象和封装了该应用领域应用程序的基本结构和共性基本组件,例如San Francisco就是一种垂直型框架。复合文档型框架是一种比较通用的框架,它将一个程序抽象为一个文档,将构成程序的各个组件看做文档中的一个个独立元素,各个独立元素通过事件消息相互联系。通过复合文档框架构造程序相当于用各种各样的元素(带有界面或不带界面)在文档上创作一幅动态的美丽图画。因此,随着图形用户界面的流行以及计算机应用的广泛普及,复合文档型框架已经成为程序构造的主流。目前,除了Visual C++外,基本上所有的开发工具都是基于复合文档型框架的。图2-19是利用复合文档型框架进行程序构造的基本原理及样例。图2-19 利用复合文档型框架进行程序构造的基本原理及样例

相对于对象模型,组件模型基于二进制黑盒重用机制,为软件的维护提供了技术上的保障。基于框架的程序构造模式为软件工业的大规模生产奠定了基础。然而,尽管组件模型的独立特性拓展了对象模型的重用机制,但是各种标准之间的异构集成和重用仍然是一个问题。

由于组件模型解决了异构集成问题,因此分布式对象计算模型得到迅速发展。分布式对象计算的基本模型如图2-20(a)所示。它在软总线的基础上,通过在客户端和服务器端分别增加代理机制来实现分布式环境下组件对象之间的集成。客户端代理、服务器端代理以及软总线为应用开发者屏蔽低层网络通信的细节和异构环境的特性,建立一个面向分布式环境应用开发的通用基础结构。图2-20 分布式对象计算的基本模型

CORBA直接支持分布式计算模型,Java Beans通过Java RMI(Remote Method Invoke)将其组件模型拓展为分布式计算模型,COM通过RPC(Remote Procedure Call)将其拓展为DCOM(Distributed Component Object Model)。图2-20(b)所示是DCOM运行时结构。对于DLL封装的组件,DCOM通过自动加载一个Dllhost.exe作为其宿主。

2.4.4 配置型组件模型

配置型组件模型(Configurable Component Model),又称为服务器组件模型,它专门针对应用服务器,定义其基于组件的基础结构模型。在传统的分布式对象计算模型中,软总线提供的附加基础服务需要被业务逻辑代码显式地使用,如图2-21(a)所示。然而,对于响应大量客户端的服务器而言,基础服务的提供涉及系统资源的有效利用,基础服务需要与资源管理技术一起使用。因此,如果这两者都由业务逻辑代码来显式使用,那么应用开发的复杂度就会急剧加大!特别是随着组件交互的行为愈加复杂,协调这些基础服务就成为一项困难的任务,这就需要与编写业务逻辑代码无关的系统级专门技术来处理。同时,这样处理也可使业务逻辑代码的开发者避免陷入各种基础服务和资源管理机制的系统级事务处理的泥潭之中,从而将其思维重心服务于其工作的本质——业务模型建立。因此,配置型组件模型的基本原理,是将应用业务逻辑与系统基础服务两者解耦,由系统基础服务构成一个服务容器,自动隐式地统一为各种应用业务逻辑按需提供相应的基础服务。也就是说,应用业务逻辑可以按需提出不同的基础服务要求,即可配置。这样,基于问题分离(Separation of Con-cerns)原理和功能可变性(Functional Variability)原理,将业务逻辑的功能部分和系统资源有效管理的技术部分两者分开,可以允许两者独立演化并促进基础服务和资源的重用。图2-21(b)是隐式使用软总线基础服务的原理。图2-21(c)是配置型组件模型的基本实现思想。图2-22是配置型组件模型的基本体系。其中,容器提供的基础服务主要包括基本服务和高级服务两类,基本服务主要用于组件实例的运行管理,例如实例池管理、JITA(Just-in-Time Activation)、并发管理等;高级服务主要用于为组件实例提供附加的高级处理功能,例如事务处理、安全管理、事件服务、消息服务等。容器内置并扩展了软总线的基础服务集。图2-21 配置型组件模型的基本原理图2-22 配置型组件模型的基本体系图2-23 配置型组件模型

配置型组件模型中,配置型组件是一种分布式、可部署的服务器端组件。图2-23(a)是配置型组件的基本模型。其中,“组件实现”具体实现组件接口的功能,“配置说明”用来配置组件运行时所需要的基础服务功能。组件实现体一般实现三个方面的功能:业务逻辑功能操作实现具体的业务逻辑规则;生命周期回调操作面向服务容器,服务容器通过这些操作管理组件实例的运行(例如钝化、激活等);主操作主要用来创建、查找和删除组件实例。一个应用往往涉及多个组件,因此在配置型组件基本模型基础上建立应用部署包模型,如图2-23(b)所示。执行应用业务逻辑的配置型组件的可配置性体现在两个层面。首先,在应用部署包进行部署时,相应的部署工具(例如Windows中的服务管理器)根据部署包中的公共配置以及各个业务逻辑组件的配置说明进行相应配置要求的登记。同时,自动生成客户端和服务容器端的相应代码黏合层(Glue Code Layer)(含面向分布式计算的两端代理)。例如,COM+中,通过Windows服务管理器,可以在组件安装时为每一个应用生成一个特殊的安装文件(∗.msi),该文件记录所有组件及其配置参数与相应的黏合层代码。然后,每当客户端调用组件的相应业务操作功能时,客户端代理附加调用上下文,经过总线将请求发送给服务容器,服务容器中的代码黏合层负责创建组件实例,并且通过拦截机制在将客户端请求发送到目标业务逻辑操作之前,根据其预先登记的配置要求启用相应的基础服务,将附加的基础服务功能加入到应用业务逻辑中。同时,为了实现服务器资源的有效利用和提高各个组件实例的执行性能,服务容器通过组件的生命周期回调接口对运行中的组件实例进行管理。图2-24是配置型组件模型的基本运行原理。图2-24 配置型组件模型的基本运行原理

目前,流行的配置型组件模型标准有EJB、COM+和CCM(CORBA Component Model)。CCM定义了一种较为完善的可配置组件模型,但目前还没有具体的产品。EJB中,主对象称为Home对象,远程对象称为EJB对象,配置型组件称为Enterprise Bean,配置型组件的配置说明用配置描述器给出。另外,还可以通过一个特定属性描述器描述Enterprise Bean的一些附加特性,以便Enterprise Bean在运行时使用。目前,一个EJB对象只能支持一个接口。EJB基础服务包括生命周期管理、持久性服务、事务处理服务、安全管理服务等。对于这些基础服务,Enterprise Bean都可以按需进行配置。图2-25(a)是EJB配置型组件包以及可部署包(应用程序)的基本结构。图2-25(b)是EJB运行时的基本结构。COM+中,主对象称为工厂对象(ClassFactory实例),配置型组件称为组件对象(CoClass实例),远程对象是组件对象的一种包装,在COM+中通过调和器对象(Mediator)给出。配置型组件的封装一般只能以一个DLL文件给出,配置型组件的配置说明可以通过在程序中加入属性描述给出(这种方式称为说明性程序设计,即Declarative Programming,或称为基于属性的编程)或在组件安装时进行交互式配置。COM+中的一个配置型组件可以实现一个或多个接口的功能。COM+组件的基本结构参见图2-26。图2-26(a)是COM+运行时的基本结构。事实上,COM+并没有特别地定义一种可配置组件模型,而是在COM/DCOM组件基础上融入MTS(Microsoft Transaction Server,微软事务服务器)及其他基础服务功能并借助于Windows操作系统构成一个运行时环境,由Windows操作系统及各种基础服务套件充当应用服务器和容器的角色。任何DLL封装格式的COM组件,只要在Windows服务管理器中被导入到某个应用程序(即安装组件)并进行相应配置即可成为COM+组件。因此,COM+应用程序可以看成一种逻辑可部署包,它可以含有多个COM+组件,这些组件具有相同的已配置特性。图2-26(b)是COM+应用程序与COM+组件之间的关系。COM+基础服务增强了有关线程同步方面的控制服务(基于套件、活动的同步控制机制),提供对组件实例并发运行的细粒度控制。图2-26(c)是COM+并发控制的基本模型。COM+服务容器除了提供实例池管理、分布式事务处理外,还提供基于角色的安全管理、松散耦合的事件服务等,并且集成了MSMQ(Microsoft Message Queue)、负载平衡、内存数据库等服务功能。图2-25 EJB的基本结构图2-26 COM+的基本结构

随着Microsoft.NET平台的推出,COM+组件标准得到了修正和改善。.NET采用装配件(Assembly)作为基本的组件打包及部署单元,它是一种逻辑组件(Logical Component),可以包含打包清单(Manifest,描述该配件以及所需的其他配件,包括版本号,编译链接号以及编译器在编译时捕获的修正号、文化特征等版本信息)以及一个(或多个)物理DLL或EXE模块,每个模块内部包含IL代码(Intermediate Language Codes,中间语言代码)、元数据(Meta-data,描述配件中声明的所有类型及其关系)及资源(包括图标、图像等),模块以文件形式存储。装配件可以是静态的(由开发工具生成,存储在磁盘中),也可以是动态的(在内存中动态生成并立即运行,可以保存在磁盘中再次使用)。模块及装配件的基本结构如图2-27所示。在装配件基础上,.NET环境进一步通过应用程序域(AppDomain)概念定义装配件的运行模型(即逻辑组件的运行结构及作用域),建立.NET环境中的可配置组件模型。图2-28(a)给出了应用程序域与装配件的关系。并且,.NET平台通过CLR(Common Language Runtime)环境封装并提供类似于COM+容器所提供的基础服务(参见.NET命名空间System.EnterpriseServices),拓展了COM+的容器机制,从而使得.NET中的每个应用程序域成为一个在CLR中可以独立配置并部署的逻辑可部署包(参见图2-28(b)所示)。因此,.NET平台通过元数据机制及CLR改善了COM+的各种缺陷,简化了组件的开发和部署。例如:.NET没有派生出所有组件的正式基本接口(例如:IUnknown),而是将所有组件都从Sys-tem.Object类派生,即所有.NET组件对象都是System.Object类的多态表现;.NET没有类工厂,而是由运行时环境将类型声明解析成包含该类型的装配件以及该装配件内确切的类或结构;通过完善的无用单元回收机制改善由引用计数的缺陷而导致的内存和资源泄漏;采用元数据方法代替类型库和IDL(Interface Description Language,接口描述语言)文件;采用命名空间和装配件名称来确定类型范围的方法以提供类型(类或接口)的唯一性来改善基于GUID(Globally Unique IDentifier)的注册的脆弱性;.NET没有套件,默认情况下,所有.NET组件都在自由线程环境中运行,由开发者负责同步组件的访问,开发者可以依赖.NET的同步锁或使用COM+的活动来实现同步;.NET中通过在类定义中使用关键字internal告知运行时环境拒绝一个装配件之外的任何调用者访问该装配件内的组件,从而可以防止COM+中通过搜索注册表找到私有组件的CLSID(CoClass IDentifier,组件类标识符)以使用它的漏洞;.NET通过为指定代码段配置许可并提供证据,将COM+基于角色的安全性拓展为角色安全和调用身份验证双向的安全控制,为高度分散、面向组件的环境提供新的安全模型;.NET中有关组件的一切操作都不依赖于注册表,并严格维护版本控制,从而简化组件的部署;与COM+只支持运行时不同语言所实现的组件的集成不同,.NET同时支持运行时和开发时的组件的无缝集成(例如:允许使用一种语言开发的组件从使用另一种语言开发的组件派生);采用基于属性的编程方法增加组件配置的灵活性等等。另外,.NET支持两种组件:使用基础服务的组件(Serviced Component)和标准的被管理组件(Managed Component,也称为托管代码或受控代码)。其中,前者就是COM+标准组件的演化和发展,而后者(总是含有元数据)则是新的组件封装模型。.NET实现了两者的统一。为了支持说明性程序设计,.NET提供的新型程序设计语言C#直接支持将配置要求以属性方式写入程序中,建立基于属性的程序设计方法(Attribute-Based Programming)。图2-27 .NET模块及装配件的基本结构图2-28 .NET应用程序结构

2.4.5 服务模型

进入21世纪,随着互联网应用的不断普及,配置型组件模型及其衍生的分布式对象计算模型,由于紧耦合、多标准和低层传输协议的依赖性等弊端,导致其不能适应面向互联网的应用动态集成的需求。因此,服务模型应运而生,并在此基础上建立服务计算模型。

所谓服务(service),是指一个封装着高级业务概念、实现公共需求功能、可远程访问的独立应用程序模块。服务一般由数据、业务逻辑、接口和服务描述组成,如图2-29所示。服务独立于具体的技术细节,一般提供业务功能,而不是技术功能。

服务模型的基本原理是明确服务提供者和服务使用者,并通过服务中介实现两者的耦合,如图2-30所示。服务模型通过定义独立于具体技术、可以扩展的通用描述手段来描述服务和实现服务交互,而将服务实现的具体技术细节隐藏在内部,从而实现服务的无缝集成。图2-31给出了服务模型的抽象作用。图2-29 服务的一般结构图2-30 服务模型的基本原理图2-31 服务模型的抽象作用

目前,服务模型的标准主要是Web Services。Web Services以XML(Extensible Markup Language,可扩展标记语言,也称为元标记语言,它给出一套定义语义标记的规则,即定义元句法)作为最基本的通用描述规范,并定义的各种规范,如服务定义、描述、访问、发布、发现与集成等。

信息是人类社会必要和重要的资源,信息处理技术是现代社会的基本要求。因此,如何组织和描述信息并对信息进行访问和各种处理成为一个核心问题。特别是随着互联网的蓬勃发展,信息的可交换性变得越来越重要。XML技术体系就是针对这一问题的一套标准。一个信息实体一般涉及结构、种类、属性和内容几个方面,为了描述各种不同的信息,XML首先通过Infoset定义一个信息实体(称为信息文档)的抽象概念模型,然后基于该模型,通过XPath(识别Infoset信息模型的子集)、XPointer(基于XPath,引用外部文档的子集并扩展XPath的数据模型,可以寻址文档中的点和范围)、XLink(两个文档之间的各种链接关系)、XBase(为相对URI引用指定基本参照点URI,URI是Uniform Resource Identifier的简称,一般包括两个子集:Uniform Resource Locator,即URL和Uniform Resource Name,即URN)和XInclude(引用外部已析通用实体)定义如何创建信息文档内和信息文档间的关系,通过DOM(Document Object Model,在内存中创建整个信息文档的树型结构,对文档内容随机访问,适用于XML文档的结构化处理)、SAX(Simple API for XML,不需将整个文档读入内存也不创建信息文档的树型结构,不能对文档内容随机访问,适用于对大型文件的简单快速、高效率的处理)定义如何访问信息文档的编程接口。同时,通过Schema(一种基于XML的语言)为信息文档定义结构和类型,通过Namespace(命名空间)将一个信息元素与其关联的Schema关联起来,避免XML文档中的元素和属性等名称在使用中发生冲突,通过XSLT(XSL Transformation,一种基于XML的语言)定义一个词汇表(比如Schema类型)到另一个词汇表的转换规则,从而建立XML技术体系中的核心规范集。最后,基于这些规范,面向各种应用,定义各种具体的应用规范。例如Web Services中的SOAP(Simple Object Access Pro-tocol)和WSDL(Web Services Description Language)等。图2-32给出了XML技术体系。图2-33到图2-35分别给出SOAP规范、WSDL规范和UDDI规范的直观视图。图2-32 XML技术体系图2-33 SOAP规范图2-34 WSDL规范图2-35 UDDI规范

事实上,XML是一种通用的结构化信息的编码标准。也就是说,作为一种可以创建其他专用标记语言的通用元标记语言,XML可以对任意结构化信息进行定义(即编码)。XML Infoset所定义的信息模型是层次型模型,层次型结构的递归特性决定了其广泛的适用性和描述能力。尽管目前的XML标准(1.0或1.1)主要面向传输和存储(即串行化)定义了语法细节,但是XML中的许多技术标准都是建立在Infoset抽象模型基础上。因此,对于不使用XML 1.0串行化格式的应用(即不是用于传输或存储,而是用于数据交换、互操作、类型化值、Web发布和分布式计算、组件集成等等),这些技术也具有通用性。也就是说,XML可以是软件集成问题的统一解决方案,如图2-36所示。2-36 XML作为一种集成技术

Microsoft. NET平台面向新一代Web应用的开发,通过在开发工具Visual Studio.NET中提供Visual C#Projects中的ASP.NET Web Service模板类型以及建立支持属性编程的新型程序设计语言C#,直接支持面向Web Services的应用开发。通过Web Services,将COM+对象包装为面向互联网的一种服务对象,如图2-37所示。另外,.NET平台还提供了一系列公共Web服务,称为.NET My Services。这些服务类似于传统程序设计中的系统函数、类库中的类或者组件库中的组件,供应用开发时按需调用。2-37 面向互联网的Web服务对象

相对于组件模型而言,服务模型也采用基于框架的程序构造方法。但是,与组件模型的框架不同,服务模型的框架是一种动态框架,它根据业务流程的需要,动态集成各个Web服务。因此,这种方法也称为基于流程的程序设计方法,即流程是一种动态框架。组件模型采用与机器相关的二进制并且标准不统一,从而导致对服务接口与执行环境的分离不够彻底。而服务模型采用与机器无关、基于XML的文本描述并且标准统一,从而使得服务接口与执行环境彻底分离。因此,服务模型的思维重心在于业务服务和业务流程的抽象,专注于业务描述,将业务逻辑与代码实现(特定执行环境)相互分离(参见图2-31)。因此,服务模型比组件模型更加抽象。另外,从支持软件大工业生产的角度来看,组件模型侧重于细粒度的技术基础,而服务模型则是面向粗粒度的应用基础。

服务模型也演绎了递归思想,主要体现在动态框架本身又可以作为一个服务被其他动态框架集成。图2-38是服务模型递归思想的解析。

2.4.6 抽象模型

尽管服务模型已经强调业务逻辑的抽象并使之独立于具体的平台和环境,但其系列标准中对于动态框架的建立仍然局限于传统控制流的设计思维,具有封闭2-38 服务模型的递归思想性,不能适应应用不断变化所带来的复杂交互场景的应用业务流的描述。因此,服务模型的抽象层次不够高。于是,抽象模型自然演化并诞生。

目前,抽象模型主要包括基于归纳思维策略的可恢复程序语句组件(Resumable Program Statements Component)模型和基于演绎思维策略的元模型(Meta Model)。两者异曲同工,都是面向抽象层的应用业务逻辑的描述,而不关注描述的具体实现平台和环境,实现完整的技术独立性和应用发展适应性。

有关抽象模型的具体解析,请读者参见第7章。

2.5 深入认识软件模型

从软件模型演化和发展的轨迹来看,人们对软件的认识不断深入,针对软件构造方法和技术的思维核心也不断变迁。从面向机器的功能模型,到面向问题的对象模型以及组件模型,再到面向应用或业务的服务模型。这种发展显式地区分了软件需要解决的应用问题和软件本身构造的技术问题,并通过软件模型进行黏合。从而回归了软件的本质,即软件用来描述客观世界。

从软件工程角度来看,功能模型的思维核心强调实现阶段,对象模型的思维核心强调分析和设计阶段,组件模型的思维核心强调技术相关的维护阶段,服务模型的思维核心强调技术无关的维护阶段。而且,软件重用的粒度越来越大。

从软件体系结构角度来看,随着软件模型的演化和发展,软件体系结构也不断演化和发展。从无模型的一体式钢板结构到基于功能模型的结构化体系结构,比如Client/Server结构、老三层结构;从结构化体系结构到基于对象模型、组件模型的框架结构和新三层结构;再到基于服务模型的SOA(Server-Oriented Architecture,面向服务的体系结构,关于SOA的相关解析请参见第5章的5.2小节和第7章的7.1小节)结构。体系结构的发展由无到有,由技术和平台的相关性到技术和平台的无关性,由基于归纳思维的策略到基于演绎思维的策略,由相对固定和封闭的框架到灵活和开放的框架,从而使体系结构思维核心由其外延转向其内涵。

从认识论角度来看,软件模型演化和发展的抽象性越来越高,由以机器为中心到以应用为中心,再到以企业为中心,从而真正诠释了软件设计的内涵。另外,尽管软件模型经历了多代的发展,但从本质上看,自对象模型以后的各种模型基本上都是采用面向对象的思维策略,只是其抽象级别越来越高。特别是服务模型的诞生使得我们可以将整个互联网看做一台计算机,将Web Service看做部署在该计算机中的各个软件功能组件,在此基础上建立新型的Web应用程序类型及其构造方法,从而诠释网络就是计算机的深刻内涵。

2.6 本章小结

本章主要解析了软件模型及其演化以及对软件体系结构的作用,并从多个角度深入解析了软件模型的本质,以此为本书后面章节的展开建立基础。

习题

1.对于数学模型和非数学模型,请各举出一个例子。

2.什么是元模型?为什么说软件模型是一种元模型?

3.软件模型对软件体系结构的作用主要体现在哪些方面?

4.请给出软件模型的基本发展脉络及其演化的规律。

5.如何理解软件模型是应用与技术的黏合剂?

6.什么是内存泄漏?什么是无效引用?请以C/C++为例,举例说明之。Java和C#中是通过什么技术解决内存泄漏问题的?

7.针对功能模型,C语言中的函数调用有PASCAL、CEDEL、STDCALL三种调用机制,请详细说明这三种调用机制的含义。

8.请举出两个递归应用的例子。

9.分治法也是用同样的处理方法处理不断缩小的数据集,以最终求解。它与递归方法的区别是什么?

10.所谓递归,即是分解(递)和综合(归)。以求P 35和C35为例,解析其递归求解的过程,给出分解阶段和综合阶段。

11.处理逻辑递归应用的哲学含义是什么?

12.对象可以理解为“具有责任的东西”,对象应该清楚地定义责任,并且自己负责自己。请问:对象的责任是通过什么描述的?它与功能模型有什么联系?

13.请举例说明对象、类和实例三个概念之间的区别和联系。

14.什么是抽象数据类型?为什么说抽象数据类型可以实现数据类型的扩展?如何理解递归思想在抽象数据类型中的应用?

15.什么是多态?什么是静态多态?什么是动态多态?请用C++举例说明。

16.什么是逻辑组件(组件对象及其关系)?什么是物理组件(封装结构)?对于内存芯片和内存条,哪个属于逻辑组件,哪个属于物理组件?

17.什么是垂直框架、水平框架和复合文档框架?请给出利用复合文档框架构造程序的基本步骤。

18.普通组件和可配置组件在基本逻辑结构上有何区别?

19.对于5个客户端、每个客户端每分钟1000个请求和1000个客户端、每个客户端每分钟5个请求这两种情况,服务器在处理上有何区别?哪种会发生拥塞和崩溃?为什么?

20.配置型组件模型主要解决如何在客户端之间共享(服务器)资源的问题。如何理解这一点?它是如何实现的?

21.对于如下两种分布式对象计算应用场景,哪一种使用池机制比较好?为什么?(1)为了向用户提供丰富的体验,胖客户端应用程序长时间占用对象,而只在其中一小段时间内使用对象;(2)面向众多的客户访问,瘦客户端需要不断地为每个客户端建立对象,分配资源,执行计算机之间或进程之间的相关调用,然后清除对象。

22.分层处理方法的本质是什么?如何理解分层处理方法在配置型组件模型中的作用?(提示:分布式计算模型中,两端代理之间的关系是紧耦合的,一般不能通过改变代理来实现服务器端基础服务的应用。配置型组件模型中,通过在代理外面增加容器机制实现扩展的灵活性。)

23.多线程并发模型具有如下优点,请解释各种优点的含义并各取一个实际应用案例:(1)反应迅速的用户界面;(2)提高性能;(提示:在多CPU系统上运行计算密集型应用。)(3)增加吞吐量;

24.配置型组件模型中,组件实例通过上下文使用系统资源,而上下文由服务容器进行控制,这种运行结构对系统资源的有效利用有什么好处?

25.请指出下列COM+相关机制在.NET中的对应物:(1)接口IUnknow;(4)异步方法调用。(2)类工厂;(3)引用计数;(4)IDL文件或类型库;(5)GUID.

26.. NET模块文件中必需的要素是什么?.NET装配件中必需的要素是什么?

27.相对COM、COM+的组件封装机制,.NET装配件采用两层封装机制(逻辑组件和物理文件)的优点是什么?

28.. NET中,装配件是逻辑封装机制,DLL、EXE或模块是物理封装机制,请问真正的组件是什么?它位于何处?请举例说明。

29.. NET中,AppDomain和装配件的关系是什么?它们分别与COM+中的什么概念相对应?(提示:可部署包,应用程序,可配置组件模型与组件包、组件的封装件。)

30.XML Infoset所定义的抽象信息模型是怎样的?

31.Namespace规范的作用是什么?为什么要建立这种规范?

32.XML Schema是干什么的?与DTD相比它有哪些主要优点?

33.给出XPath的基本结构,并说明每个部分的作用。举例说明XPath的应用。

34.给出XPointer的基本结构,并说明它与XPath的关系。举例说明XPointer的应用。

35.XML 1.0是如何给Infoset的抽象模型定义其面向串行化的语法结构的?它规定的一个文档的结构是怎样的?

36.请给出XML1.0+Namespace、SOAP的Schema、SOAP、一个具体的SOAP报文四者之间的关系,并以编译原理中的文法和语言为例,分别找出两者之间四层抽象关系的对应视图。

37.Web Services中是如何描述一个服务的?一个Web Service和一个组件在基本逻辑结构上有何区别?

38.如果将服务看成一种组件,则XML也可以看做一种组件技术。如何理解这一点?(提示:从XML的描述和集成能力着手。)

39.传统的Web应用以网站上部署的整个应用为基础,而Web Services使新型的Web应用以网站上部署的Web Service(服务组件)为基础,这两者有什么本质区别?

40.通过相应开发工具,分别开发一个简单的COM应用、COM+应用、EJB应用和Web Services应用。

41.如何理解软件开发与软件设计的区别与联系?

第3章 软件体系结构基本构件:设计模式

本章主要解析用以建立软件体系结构的基本构件——设计模式。首先,给出设计模式的概念,解析设计模式对软件体系结构的作用。然后,解析几种常用的设计模式。最后,对设计模式进行深入认识和思考。

3.1 什么是设计模式

所谓模式(Pattern),是指对一个在我们周围不断重复发生的问题及其解决方案的核心的一种描述。所谓设计模式(Design Pattern),是软件设计模式的简称,是指对被用来在特定场景下解决面向对象软件中一般设计问题的类和相互通信的对象的描述。也就是说,一个设计模式命名、抽象和确定了一个通用设计结构的主要方面,这些设计结构能被用来构造可重用的面向对象设计。设计模式确定了所包含的类和实例,以及它们的角色、协作方式与职责分配。

一般来说,一个模式必须具备四个基本要素:模式名称(Pattern Name)、问题(Problem)、解决方案(Solution)和效果(Consequence)。模式名称用一个简洁的助记符概括模式涉及的问题及其解决方案和效果,它简洁地描述了模式的本质。问题描述了应该在什么时候使用模式,包括使用模式必须满足的一系列先决条件,即特定的背景。解决方案描述了设计的组成成分,以及它们之间的相互关系及各自的职责和协作方式。效果描述了模式应用的效果以及使用模式应权衡的问题,包括对时间和空间的权衡,有关语言和实现问题,对系统的灵活性、扩展性和可移植性的影响等。

3.2 设计模式的主要作用

设计模式描述了在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。在此,特定问题一般是指一个软件系统中的某个局部方面。因此,设计模式的粒度介于软件模型和体系结构之间。通过设计模式,可以使得软件体系结构的设计不再从最基本的元素(类似于黄沙、水泥和石子等)开始,而是可以从设计模式这个基本构件块(类似于各种预制件)开始。从而使得新的设计建立在以往工作的基础上,重用以往成功的设计经验,有利于提高软件体系结构的质量。特别是,框架(Framework,是指构成一类特定软件可复用设计的一组相互协作的类。它定义该类软件的体系结构)的流行促进了设计模式的广泛应用,以便使框架具有足够的灵活性和可扩展性。

另一方面,设计模式可以建立面向体系结构的一套通用设计词汇,方便设计人员进行交流讨论、书写文档以及探索各种不同设计方案。设计模式也帮助软件设计人员建立以模式为基础的独特的体系结构设计的思考方式,使他们可以在比设计表示或编程语言更高的抽象级别上讨论一个系统,从而降低其复杂度;也可以提高他们的设计以及相互之间讨论这些设计的层次;可以帮助他们更加容易、快速地理解已有系统。

3.3 常用设计模式解析

根据模式用来解决的问题的不同,通常将设计模式分为创建型(Creational)、结构型(Structural)和行为型(Behavioral)三种。创建型模式与对象的创建有关,它抽象了类的实例化过程,使一个系统不必关心其包含的对象是如何创建、组合和表示的,即在实例化时提供接口和其实现之间的透明耦合。结构型模式处理类或对象的组合问题,以便获得更大的结构。行为型模式对类或对象怎样交互(相互之间的通信模式)和怎样分配职责进行描述(类或对象的模式),刻画了在运行时难以跟踪的复杂的控制流。

根据模式的作用范围,通常将设计模式分为类模式和对象模式。类模式处理类和子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻就确定下来了。对象模式处理对象之间的关系,这些关系在运行时刻是可以变化的,更具动态性。创建型类模式通过继承方式改变被实例化的类,创建型对象模式将实例化委托给另外一个对象。结构型类模式采用继承机制来组合接口或实现,例如多继承机制。结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些解决方案。因为可以在运行时改变对象组合关系,所以对象组合方式具有更大的灵活性,可以实现静态类组合不可能实现的功能。行为类模式使用继承机制在类之间分配行为,行为对象模式使用对象复合而不是继承机制在类之间分配行为。

3.3.1 创建型设计模式

1.工厂方法(Factory Method)模式

工厂方法模式就是要针对如何创建对象以及管理对象这一问题,给出一个良好的解决方案。一般情况下,对象的创建和管理由对象的使用者负责。然而,如此一来,对象的使用者就必须预先知道需要创建和管理的对象类型。事实上,在实际应用中,对象的使用者往往需要创建和管理各种对象。因此,预先知道需要创建和管理的所有对象类型显然是不合适的、不方便的。针对这个问题,工厂方法模式通过提供一个抽象的创建对象的操作Cre-ateProduct(),以便抽象出所有各类对象的创建操作,如图3-1(a)所示。针对某个具体应用,可以通过具体子类来覆盖该抽象操作,定义具体的某类对象的创建工作,即CreateProduct(),如图3-1(b)所示。这样,在处理对象的创建和管理这一问题上,就可以在抽象层面上建立一个通用的结构,客户通过该结构,只要调用抽象工厂类的对象的创建操作,就可以创建出各种具体子类的对象并管理它们。从而使得该通用结构可以相对独立于各种具体对象类的对象的创建过程,如图3-1(c)所示。图3-1 工厂方法模式

正是由于抽象操作CreateProduct()是用来创建对象的,其工作性质类似于一个工厂,因此抽象操作CreateProduct()称为工厂方法,该模式就称为工厂方法模式。工厂方法模式是一种类模式,它将对象的创建工作延迟到子类中。由此,使得客户和抽象类之间的关系保持稳定,并通过继承方式不断地定义各种具体对象类的对象的创建工作,支持该结构的扩展能力,如图3-1(d)所示。图3-1(e)给出了工厂方法模式的基本结构。

工厂方法模式在具体应用时,也可以为FactoryMethod()指定一个Product类的参数——产品标识符,以实现参数化的工厂方法,从而使得FactoryMethod()可以创建多种产品。通过为新产品引入新的标识符或将已有的标识符与不同的产品相关联,这种方法可以简单而有选择性地扩展或改变一个Creator生产的产品。另外,在支持模板的语言中,也可以实现一个以Product类作为参数的Creator的模板子类,从而避免仅为了创建适当的Product对象而必须建立Creator子类的约束。

工厂方法模式广泛应用于框架中,框架使用抽象类定义和维护对象之间的关系,对象的创建和管理通常由框架负责。因此,框架中包含工厂方法,框架中的对象都通过工厂方法创建,框架通过使用工厂方法模式建立起一种抽象稳定的通用系统结构。当通过框架开发软件时,框架的一个实例就是通过继承方式子类化框架中定义的各抽象类,并通过覆盖具体定义创建各种子类对象的操作,从而使得客户不必关心各种对象的具体创建过程。EJB中,Home接口就是工厂方法模式的典型应用,所有EJB对象(用以实现EJB接口)的创建都必须通过Home(用以实现Home接口)对象实现,EJB对象的具体创建过程对客户来说是透明的,EJB容器也可以对EJB对象进行统一管理。

2.抽象工厂(Abstract Factory)模式

工厂方法模式解决了一个工厂(Creator相当于一个工厂)及其所生产产品问题的通用解决方案。然而,有时一种应用需要同时产生一系列相关的产品。针对这个问题,工厂方法模式固然可以解决一个产品系列中各种产品的问题,但是一个产品系列中各种产品之间相互独立,它们的相关关系还是需要显式地另外处理。为此,我们可以在工厂方法模式的基础上抽象出抽象工厂模式。

抽象工厂模式通过定义一个能生产一个产品系列的抽象工厂类,建立用于解决同时生产一系列相关产品的通用解决方案,如图3-2(a)所示。针对某个具体应用,可以通过子类覆盖抽象工厂类中生产各种产品的操作,具体定义产品系列中各种产品的具体生产工作,如图3-2(b)所示。一个具体工厂子类显然对应于一个具体的产品系列,可以同时生产出这一系列的各类产品。然而,抽象工厂类并不能预先知道其所需要生产的某种产品系列的各个具体产品类,因此抽象工厂类中不能直接定义生产某种产品系列中各种具体产品的操作,只能定义一组抽象的生产一个产品系列的操作CreateProduct1()~CreateProductn(),以便抽象出所有具体工厂子类的系列化产品的生产操作。而一个产品系列的产品生产的具体实现则可以由具体的工厂子类去完成(通过继承,具体定义CreateProduct1()~CreateProductn()的行为,建立具体应用的CreateProduct1()~CreateProductn())。这样,在处理一个工厂及其如何生产一个产品系列的产品和管理这些产品这一问题上,就可以在抽象层面上建立一个通用的结构,客户通过该结构,只要调用抽象工厂类中对应于一个产品系列的产品生产的操作,就可以生产出一个产品系列的各种具体产品并管理它们,从而使得该通用结构可以相对独立于各种具体产品系列的产品的生产过程,如图3-2(c)所示。图3-2 抽象工厂模式

正是由于抽象工厂所能生产的一个产品系列是不确定的,需要由具体的工厂对象来确定,因此该模式称为抽象工厂模式,表示其可以面向任何系列化产品的生产并管理这些相关的产品。抽象工厂模式是一种对象模式,它将对象的创建工作委托给具体的工厂对象。由此,使得客户和抽象工厂之间的关系保持稳定,并通过继承方式不断地定义各种具体产品系列及其相应的具体工厂,支持该结构的扩展能力,如图3-2(d)所示。图3-2(e)给出了抽象工厂模式的基本结构。

抽象工厂模式尽管通过更换具体的工厂可以很容易地交换不同的产品系列,但却难以扩展抽象工厂以生产新种类的产品。为了解决该问题,抽象工厂模式在具体应用时,可以将生产一个产品系列的操作CreateProduct1()~CreateProductn()合成一个操作CreateProducts(),然后为该操作指定一个用来标识被生产的产品系列类型的参数,从而以类模式方法实现。另外,对于含有多个产品系列且产品系列之间差别比较小的应用场景,具体工厂也可以使用Prototype模式来实现,使产品系列中的每一种产品可以通过复制其原型来生产。这样,就不必要求每个新的产品系列都需要定义其相应的具体工厂类。

抽象工厂模式可以广泛应用于需要强调一系列相关的产品对象的设计以便进行联合使用或一个系统需要支持多个产品系列的配置的应用场合,例如支持多种平台或风格的图形用户界面工具包的构建等。

3.生成器(Builder)模式

生成器模式专门为含有各种不同部件的复杂产品的生产提供一种良好通用的解决方案。为了解决含有各种不同部件的复杂产品的生产问题,生成器模式分离了产品的构建和表示,使得同样的构建过程可以创建不同的表示。也就是说,生成器模式对复杂产品中所有部件的构建过程进行抽象,建立一个抽象的复杂产品生产类,如图3-3(a)所示。某个具体复杂产品的生产过程可以通过继承抽象类来具体定义,从而定义一个通用的构建过程,如图3-3(b)所示。显然,基于一个相同的部件集合,依据部件使用的数量和部件之间不同的结构组合情况,可以生产出各种不同的复杂产品,即创建不同的表示,如图3-3(c)所示。图3-3 生成器模式

正是将一个复杂产品的生产过程封装在一个抽象类中,并通过其子类定义具体复杂产品的各个部件的生产及装配过程,所以该模式称为生成器模式。生成器模式是一种对象模式,它将对象的创建工作委托给具体的生成器对象。生成器模式中,可以通过继承抽象类来扩展复杂产品的种类,如图3-3(d)所示。图3-3(e)给出了生成器模式的基本结构。

生成器模式中,生成器通过向导向器提供一个复杂产品构造的抽象接口(该接口包含创建复杂产品各种部件的抽象操作),隐藏复杂产品的具体表示和内部组合结构。每个Con-creteBuilder包含了创建和装配复杂产品中各种部件的所有代码。这些代码只需要写一次,然后不同的导向器可以重用它以便在相同部件集合的基础上构建不同的产品。也就是说,客户通常用合适的具体生成器来配置导向器,然后依据所要生产的复杂产品的组合情况,使用该生成器包含的创建和装配每种部件的代码来处理它的各个部件。也就是说,客户可以在导向器的控制下一步一步地构造产品,当产品完成时从生成器取回它,如图3-3(f)所示。因此,生成器模式能够更好地反映产品的构建过程。同时,也可以对产品的构建过程及内部结构进行更精细的控制。另外,考虑到由各个具体生成器生成的部件之间,其表示相差太大,因此在生成器模式中,产品类不需要建立统一的抽象类。

生成器模式可以应用于包含各种对象的复合文档的相关处理场合,例如可以应用于包含各种对象的文档解析器或阅读器的设计和实现。

4.单件(Singleton)模式

单件模式确保一个类只能创建一个实例,并提供一个访问实例的全局访问点。如何才能保证一个类只有一个实例并且该实例易于被访问呢?显然,对于易于被访问这一点,可以通过设置一个全局变量实现。然而,全局变量不能防止客户实例化多个对象实例。为此,单件模式通过设置一个面向类的私有成员变量,让一个类自身保存它的唯一实例。同时,提供一个面向类的成员函数,作为唯一实例的访问点。该成员函数只有在该类没有实例时才实例化该类创建一个实例,并将实例赋值给类的私有成员变量。并且,通过拦截创建对象实例的请求来保证该类没有其他的实例被创建;通过将构造函数设置为保护的或私有的,防止客户绕过静态构造函数机制而直接实例化这个类。图3-4给出了单件模式的基本结构。

单件模式是一种对象模式,它将对象实例的创建工作交给类的一个对象来完成。因为单件模式通过类封装它的唯一实例,而图3-4 单件模式的基本结构且客户只能通过其提供的访问点访问该实例,所以单件模式可以严格地控制客户怎样访问实例以及何时访问实例。另外,单件模式也可以子类化,这样可以在运行时刻用所需要的子类创建实例并动态地配置应用。

单件模式广泛应用于框架或容器的实现中,以便确保其中的工厂对象实例的唯一性。

5.进一步认识创建型模式

创建型模式也称为工厂模式,它们都是针对如何生产产品和管理产品这一问题,给出一个良好的解决方案。它们既将一个系统需要使用哪些具体类的信息封装起来,又隐藏了这些类的实例是如何被创建和放在一起的。一个系统关于其所有对象所知道的只是由抽象类所定义的接口。因此,在什么被创建、谁创建它、它是怎样被创建的以及何时创建这些方面,创建型模式提供了很大的灵活性。创建型模式允许客户用结构和功能差别很大的产品对象,静态或动态地配置一个系统。

创建型模式有效分离了对象的使用者和对象的创建及管理者两个角色的职责,使得对象的使用者不必关心所使用的对象究竟是如何创建的。

Factory Method模式是类自己封装了对象创建的操作(工厂函数),不需要另外定义一个用于负责创建对象的类,只需要通过子类化创建一个需要创建特定对象的子类并重载工厂函数即可。这种方法中,如果需要改变产品类型,就可能需要创建一个新的子类。由此,可能导致级联的多层子类化。Abstract Factory模式和Builder模式都需要另外定义一个用于负责创建对象的类,通过类的协作(对象的复合)来完成产品生产工作。Factory Method模式可以生产各种产品。Abstract Factory模式通过另外的工厂对象生产多个类的对象,这些对象具有相关性并组成一个产品系列。Builder模式通过另外的工厂对象使用一个相对复杂的协议,逐步创建一个复杂的产品。Singleton模式用来控制一个类只能有单个实例。

Abstract Factory模式和Builder模式中的工厂对象,其操作的实现建立在Factory Method模式基础上。

3.3.2 结构型设计模式

1.适配器(Adapter)模式

适配器一般是指计算机系统中用来连接系统总线和外围设备的一种接口设备,它将系统总线的标准控制信号转换成各种外围设备需要的控制信号。借用这一概念,适配器模式就是专门针对接口不兼容问题提供一种解决方案,将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式中,需要转换的类(Target)的接口(Request())称为标准接口,客户希望的另外一个接口称为专用接口(SpecilficRequest())。相应地,实现专用接口的类称为被适配者(Adaptee),实现两种接口转换的类称为适配器(Adapter)。适配器模式可以采用类模式,也可以采用对象模式。以类模式实现时,通过多继承机制实现两种接口的转换,如图3-5(a)所示。以对象模式实现时,通过对象的组合机制实现两种接口的转换,如图3-5(b)所示。也就是说,类模式适配器将需要转换的类和实现专用接口的类作为平等关系看待,而对象模式适配器将需要转换的类和实现专用接口的类作为上下关系看待(依赖于一个适配对象包含另一个被适配对象)。对象模式适配器不需要对每一个需要使用的被适配者都进行适配器的子类化,可以按需动态地进行接口转换,具有较大的灵活性。图3-5 适配器模式的基本结构

适配器模式可以使客户将一个自己的类加入到一些现有的系统中去,而这些系统对这个类的接口可能会有所不同。适配器模式也可以使客户正在设计的系统重用已经存在但接口并不完全一致的功能。通过多继承方式,可以实现双向适配器,使得接口差异较大的两个类能够透明地互操作。

适配器模式广泛应用于需要重用已经存在但接口不符合要求的类,以及需要创建一个可以重用的类,该类可以与其他不相关的类或不可预见的类协同工作的应用场合。

2.外观(Facade)模式

外观模式针对客户如何使用一个含有许多功能的复杂系统这一问题,给出了一种通用方便的解决方案。外观模式通过给原有系统提供一个称为系统外观的对象来实现此目标。外观对象定义一个(或一组)接口,向客户提供一致的界面。图3-6(a)给出了外观模式的基本结构。图3-6 外观模式

外观模式对客户屏蔽了复杂系统中的各个子系统组件,实现客户与子系统之间的松散耦合,降低客户与子系统之间的耦合度。并且,外观模式减少了客户处理的对象数目,使得子系统使用起来更加方便。外观模式隔离了客户与子系统,可以有效支持子系统(系统功能)的扩展、更新和维护(如图3-6(b)所示),也可以支持系统的切换。外观模式简化了接口,即将对多个接口(对应于各个子系统)的访问改造成对一个接口(对应于外观对象)的访问。

外观模式广泛应用于封装或者隐藏原系统、使用一个复杂系统的部分功能、以特殊方式使用一个系统、对原系统进行维护、集成多个框架等应用场合。

3.桥接(Bridge)模式

通常,当一个抽象类可能存在多种实现时,一般使用继承机制来处理。也就是说,由抽象类定义相应的接口,而具体的子类则用不同的方法实现接口,如图3-7(a)所示。然而,这种方法将抽象部分和它的实现部分固定在一起,导致难以对抽象部分和实现部分独立地进行修改、扩展和重用。同时,继承机制也使得客户代码与平台相关,导致系统的移植性差。针对该问题,桥接模式通过分离抽象部分和实现部分,分别将它们放在独立的类层次结构中,从而实现两个部分的独立演化,如图3-7(b)所示。同时,通过抽象部分对实现部分对象的聚合来建立完整的结构,并将抽象部分的基类和实现部分的基类之间的关系称为桥接,如图3-7(c)所示。图3-7 桥接模式

桥接模式对客户隐藏实现细节,灵活支持系统的扩展(如图3-7(d)所示)以及一个系统在不同平台之间的移植。并且,桥接模式可以在运行时刻动态实现实现部分的选择和切换。

桥接模式广泛应用于构建面向不同平台的系统的应用场合,例如一个通用图形处理系统等。

4.组合(Composite)模式

组合模式针对如何由一系列基本对象构成具有树状结构的组合对象并使用一致性方法使用基本对象和组合对象的问题,提供一种解决方案。为了处理该问题,组合模式首先抽象出一个既代表基本对象又代表组合对象的抽象类,该类既定义与基本对象相关的一些抽象操作,也定义一些与组合对象相关的一些抽象操作。组合对象实际上是一种容器对象,用于组合一些基本对象或其他组合对象,如图3-8(a)所示。然后,各种基本对象类通过继承机制具体实现抽象类中与基本对象相关的一些抽象操作,各种组合对象类则通过继承机制具体实现抽象类中与组合对象相关的一些抽象操作,同时,通过遍历方式具体实现与基本对象相关的一些抽象操作,以便对其聚合的各个对象进行操作,如图3-8(b)所示。图3-8 组合模式

组合模式通过递归方式定义了包含基本对象和组合对象的类层次结构,并通过一致性接口使用基本对象和组合对象,从而简化了客户代码(即在组合对象类中不需要通过选择语句处理不同的对象)。组合模式也可以在不影响客户代码的前提下,十分容易地增加新类型的组件,提高其扩展能力,如图3-8(c)所示。然而,组合模式也存在一些缺陷。例如,它与类层次设计原则相冲突,因为抽象类中定义的与组合对象相关的操作对基本对象来说毫无意义。并且,在抽象类中定义的与组合对象相关的操作尽管对客户具有良好的透明性,但也会带来安全隐患。例如,客户可能对基本对象执行增加对象或删除对象的操作。另外,作为容器对象的组合对象,应该对如何存储和管理其聚合的各种子对象进行仔细的考虑。例如,采用什么样的数据结构来存储子对象?子对象是否需要排序?对于频繁遍历和查找而言,是否需要利用Cache改善性能?

组合模式广泛应用于具有整体部分层次结构的对象组合的应用场合,例如用户界面工具箱中视图的实现、复合文档的构建等。

5.装饰(Decorator)模式

一般情况下,通过继承机制,子类可以在父类功能的基础上增加一些新功能。但是这种方法不够灵活,因为所有子类对象的实例都将具有这些新添加的功能。如果要求只对子类对象实例中的部分实例添加新的功能,这种方法显然是不可能做到。也就是说,通过继承机制对新功能的选择是一种静态控制方法,客户不能控制给对象实例添加或删除新功能的方式和时机。装饰模式就是针对如何动态地给一个对象实例添加一些新的功能这一问题,为客户提供一种灵活的解决方案。为了处理该问题,装饰模式首先定义一个称为装饰的对象,该对象与需要添加新功能的对象具有一致的接口,以达到对使用需要添加新功能的对象的客户的透明性,如图3-9(a)所示。然后,将需要添加新功能的对象嵌入到装饰对象中,由装饰对象将客户的请求转发给需要添加新功能的对象(如图3-9(b)所示),并且在转发前或后执行一些额外的操作,以动态地为需要添加新功能的对象添加新的功能,如图3-9(c)所示。图3-9(d)给出了装饰模式的基本结构。图3-9 装饰模式

装饰模式可以动态地为一个对象实例按需添加各种新功能,以体现其扩展能力,如图3-9(e)所示。同时,装饰模式还可以按需动态地对各种新功能进行管理,例如添加、删除和建立不同组合,如图3-9(f)所示。装饰模式中,装饰对象可以看做被装饰对象的外壳,它可以改变被装饰对象的行为。装饰模式提供了一种“即插即用”的方法来给需要添加功能的对象添加功能。也就是说,它并不是在一个复杂的可定制的类中支持所有可预见的功能,而是首先定义一个简单的类,然后用装饰类逐渐给它添加功能,由此可以从简单的部件组合出复杂的功能。这样,可以不必为不需要的特征付出代价。同时,也更容易不依赖于Decorator所扩展(甚至可能是不可预知的扩展)的类而独立地定义新类型的Decorator,如图3-9(g)所示。装饰模式在具体应用时,必须注意如下一些问题。首先,当仅仅需要为某个对象实例添加一个新功能时,抽象的Decorator类可以省略,直接将添加新功能的操作合并到一致性接口的操作中,如图3-9(h)所示。其次,为了确保接口的一致性,装饰对象和被装饰对象必须有一个公共的父类,即DecoratedObject。并且,该类只集中于定义接口,而不应该存储数据。对数据表示的定义应该延迟到子类中,否则该类会变得过于复杂和庞大,导致难以大量使用。同时,该类也不应该有太多的功能,因为有些具体的子类可能不需要某些功能。

装饰模式可以应用于图形用户界面工具箱的构建中,以便给各种窗口组件添加各种图形装饰、控制对特定组件的访问以及跟踪组件交互行为及输出调式信息等。装饰模式也可以用于输入/输出流的控制中,以便按需对特定输入/输出流进行特殊功能的处理,例如加密、捎带信息等等。总之,当需要扩展多种功能,但又不能通过生成子类的方法进行直接扩展时,就可以采用装饰模式。因为每种功能扩展都需要独立扩展,这样每一种功能组合就会产生大量的子类,导致子类数目呈爆炸性增长。

6.进一步认识结构型模式

由于模式实现只是基于继承机制(包括单继承和多继承)或对象组合机制,因此结构型模式之间具有相似性,特别是它们的参与者和协作者之间的相似性。然而,透过这些相似性,应该看到各种模式所要解决的不同问题。适配器模式用以解决两个已有接口之间的不匹配问题,如图3-10(a)所示。它不考虑这些接口是如何实现的,也不考虑它们各自的演化问题。适配器模式不需要对独立设计的类中的任何一个进行重新设计,就能使它们协同工作。与适配器模式重用一个原有的接口不同,外观模式需要定义一个新的接口,并将该接口映射到多个已有接口,如图3-10(b)所示。桥接模式对抽象接口与它的实现(可能有多个)进行桥接,用以将多个抽象接口映射到多个实现接口,如图3-10(c)所示。适配器模式一般在类设计好以后实施,以解决不兼容的两个类同时工作的问题。显然,这种不兼容耦合往往是不可预见的。而桥接模式必须在设计类之前实施。也就是说,使用桥接模式者必须事先知道一个抽象将有多个实现,并且抽象和实现两者是独立演化的。组合模式和装饰模式都是通过递归组合方式来组织可变数目的对象,因此它们具有相似的结构。但是,两者的目的不同。组合模式用以将多个基本对象组合成一个复杂对象,使基本对象和复杂对象能够用统一的方式处理,并且多重对象可以被当做一个对象来处理,如图3-10(d)所示。因此,它的重点在于表示。装饰模式用以将多个功能添加到一个对象,避免静态实现功能组合时带来的子类急剧增加的弊端,如图3-10(e)所示。因此,它的重点在于装饰。适配器模式、外观模式和桥接模式主要通过接口映射机制,实现多个对象或类的集成,形成更大的结构。适配器模式可以看成1:1的映射,外观模式可以看成1:n的映射,桥接模式可以看成m:n的映射。组合模式和装饰模式主要通过对象的递归组合,实现多个对象的集成,形成更大的结构。图3-10 结构型模式的基本作用

除了类适配器模式外,其他模式(包括对象适配器模式)都是对象模式,它们都具有较大的灵活性。

3.3.3 行为型设计模式

1.策略(Strategy)模式

所谓策略,是指一种思想和方法。例如,军事策略是指有关军事方面的一些处理问题的指导思想和方法。策略模式主要用来针对应用中处理某个问题的方法需要不断改变的情况,提供一种灵活的解决方案。例如,订单处理中有关税收计算问题,受政策的影响,具体计算方法经常会发生变化。策略模式的基本思想是,将需要不断改变的方法单独从应用中抽取出来,建立具有统一使用接口的抽象策略类,从而使方法的变化独立于使用它的客户,如图3-11(a)所示。也就是说,客户使用策略时,可以通过统一抽象接口调用不同的具体策略。处理某个问题的方法的各种具体实现,可以通过子类化抽象策略类而具体定义其行为,如图3-11(b)所示。图3-11(c)给出了策略模式的基本结构。图3-11 策略模式

策略模式通过独立抽象和封装策略部分,为策略使用者定义了一系列可供重用的策略和行为,并提供了用条件语句选择所需的行为以外的另一种选择方式。这种方式可以消除通过直接子类化策略使用者类来扩展不同行为所带来的弊端,即将行为硬编码到策略使用者类中,导致行为的实现与策略使用者的实现混合起来,从而使策略使用者类难以理解、难以维护及难以扩展,而且也不能动态地改变行为,如图3-11(d)、图3-11(e)所示;或者,使策略使用者的代码中包含一些暂不需要的行为实现代码,使代码量变得庞大,执行效率受到影响。

策略模式是一种对象模式,它将具体策略封装在一个个对象中,从而可以方便地指定或改变一个对象所使用的具体策略。也就是说,策略模式通过改变受委托对象来改变委托对象的行为。策略模式在具体实现时,要考虑到与策略使用者之间的协作。也就是说,策略使用者可以将待处理的原始数据传送给策略对象,策略对象将处理结果返回给策略使用者。协作的具体实现,可以是让策略使用者定义一个接口以便让策略对象来访问它的数据;或者,策略使用者可以将自身作为一个参数传递给策略对象,以便让策略对象在需要时可以回调策略使用者。一般来说,通常有一系列具体策略类供客户选择,客户创建并传递一个具体策略对象给策略使用者,以便策略使用者将客户的请求转发给它的策略对象。另外,也可以为策略使用者定义缺省的行为,这样就可以使策略对象成为可选的。即策略使用者在使用某种策略前先检查是否存在策略对象,如果存在就使用它;如果没有,策略使用者就使用缺省的行为。

策略模式可以提供相同行为的不同实现,以便客户可以根据不同的时间或空间权衡、取舍需求,从不同的实现策略中进行选择。同时,策略模式也为软件的单元测试带来方便。因为每个具体的业务规则实现都有自己的类,可以通过自己的接口单独测试。这比将业务规则代码混合到策略的使用者代码中的测试要简单得多。

然而,策略模式也存在一些缺陷。首先,客户必须了解各种不同的具体策略,否则就不能选择一个合适的策略,因此可能不得不向客户暴露其具体的实现问题。其次,策略使用者与具体策略之间存在一定的通信开销,因为策略使用者需要将具体参数传递给具体策略。最后,策略模式会增加一个应用中的策略对象的数目。

策略模式可以使用在需要在不同时间应用不同业务规则的应用场合,例如数据输入验证方法、寄存器分配方法、指令集调度策略、价格计算方法等,以便客户在多种不同的具体实现方法中选择。

2.观察者(Observer)模式

观察者模式也称为依赖(Dependent)模式或发布订阅(Publish-Subscribe)模式,主要用来为如下应用场景提供一个通用的解决方案,即有一组对象,它们都对某个事件感兴趣,当这个事件发生时,这些对象需要得到通知,以便它们做一些相应的处理工作。针对这种应用场景,显然可以通过让每个对象侦听事件的方法来实现。但是,每次当侦听事件的对象集合发生改变时,这种解决方案就需要改变发出事件通知的对象,以便增加对新加入的侦听对象的事件通知。可见,让发出事件通知的对象(通知者)和关注事件的对象(被通知者)紧密耦合在一起并不是一个好的解决方案。为了处理这种问题,观察者模式首先将两者解耦,将所有希望获得通知的对象(被通知者)称为观察者(Observer),即它们在观察一个事件的发生。然后,让观察者自己负责自己的行为,了解自己观察的究竟是什么,而不是让事件通知者关心究竟有哪些观察者在依赖自己,毕竟这不是它的行为,它的行为只是在事件发生时负责发出通知。为此,观察者需要有一种方式向其所关心的目标注册。为了实现这一功能,观察者模式中将事件通知者定义为目标,并让其提供两个操作Attach(Observer)和Detach(Observer),分别用来让观察者将其注册到目标的观察者列表中,或从目标的观察者列表中将自己删除,如图3-12(a)所示。由于目标对象已经有了已注册的观察者,因此当事件发生时,目标对象就可以通知所有已注册的观察者。为此,目标对象还应该实现一个通知方法Notify(),如图3-12(b)所示。同时,为了响应事件并对其进行处理,每个观察者对象应该实现一个处理方法Update(),如图3-12(c)所示。另外,考虑到有的观察者可能需要得到更多与事件相关的信息,因此具体的目标对象实现时,也可以增加获取信息和设置信息的方法GetState()和SetState(),以便观察者对象需要时调用,如图3-12(d)所示。可见,观察者模式的这种解决方案不会随着观察者集合的变化而改变,因此具有较大的扩展性和灵活的适应性。图3-12(e)给出了观察者模式的基本结构。图3-12(f)给出了一个目标对象和两个观察者之间的协作关系。图3-12 观察者模式

观察者模式的基本结构中,假设由目标对象保存对其所管理事件感兴趣的所有观察者。但是,这种基本实现方案在目标较多而观察者较少时,可能带来较高的存储代价。为此,可以采用一个关联查询机制(例如使用一个Hash表)来维护目标到观察者的映射,此时一个没有观察者的目标就可以不产生存储开销,从而以增加访问观察者的时间开销来换取高存储代价的空间开销。再者,在某些情况下,一个观察者依赖于多个目标可能是有应用意义的。例如,一个表格对象很可能依赖于多个数据源。针对这种应用场景,可以扩展观察者对象中的Update()接口(例如,增加一个表示目标对象的参数),以便使观察者知道到底是哪一个目标发送来的通知。由于目标与它的观察者是依赖于通知机制来保持状态的一致性,因此究竟如何来触发事件通知操作Notify(),也可以有多种具体的实现方法。一种是由目标对象的状态设定操作在改变目标状态后自动调用Notify()。这种方法对客户透明,但是多个连续的操作会产生多次连续的通知,直接降低执行效率。另外,对于某些不需要更多地获取事件信息(即不会调用目标对象的状态设定操作SetState())而只要一个简单事件通知的应用而言,就不能发出事件通知。另一种方法是让客户自己负责触发,以便在适当的时机主动调用Notify()。尽管这种方法具有灵活性,但也会因为客户的疏忽而导致错误,例如客户忘了调用Notify()。另外,客户删除一个目标对象时,应该确保不在其所有观察者中遗留对该目标的悬挂引用。反之,客户也不能简单地删除一个观察者对象,因为其他的对象可能会引用它们,或者它们可能还在观察其他的目标。

观察者模式中,目标和观察者之间的耦合关系是抽象的,耦合的程度最小。因为一个目标所了解的只是它有一系列都满足抽象Observer类的简单接口的观察者,而不知道每一个观察者究竟属于哪一个具体的类。这种特点可以使得目标和观察者分别位于不同的抽象层次并互相通信,例如目标对象可以位于较低层次,而观察者对象则位于较高层次。从而确保系统层次的完整性,而不是横跨两个层(违反层次性)或都处于两个抽象层的某一个层之中(损坏层次的抽象性)。另外,由于这种耦合关系的实现,目标对象发送的通知不需要指定它的接收者。因此,可以在任何时刻动态地按需增加或删除观察者,具有灵活的动态扩展能力。当然,由于各个观察者之间相互独立,因此某个观察者对目标的改变可能会带来更新的波动效应而导致错误,它对目标的一个看似无害的操作可能会引起一系列观察者对象以及依赖于这些观察者对象的那些对象的更新。

观察者模式可以适用于如下应用场合,即对于一个具有两个方面并且其中一个方面依赖于另一个方面的抽象模型,此时将两者分别封装在独立的对象中,可以使两者各自独立地改变和重用;或者,当一个对象状态的改变需要同时改变其他对象的状态,但不知道具体有多少对象有待改变时,观察者模式可以带来灵活的扩展性;再或者,当一个对象必须通知其他对象,但它又不能假定其他对象究竟是谁时,观察者模式采用的解耦思想可以带来较大的方便性。MVC(Model-View-Controller,参见第4章相关部分的解析)风格中,M和V之间的关系就是典型的观察者模式的使用。

3.模板方法(Template Method)模式

所谓模板,一般是指用来制作某类东西的一种工具,它定义了或规定了这类东西的基本形态。例如绘图模板作为一种工具,它定义了各种基本图形符号的形态,可以用来帮助我们绘制各种图形。所谓模板方法,在此是指抽象类中一个具有模板功能的成员函数。显然,该函数定义或规定了某种东西的基本形态。在此,某种东西主要是指一个包含若干基本处理步骤的计算过程。因此,模板方法模式就是对一些具有相同计算过程的处理方法进行抽象,对基本处理步骤进行标准化,在概念层次建立概括所有这些计算过程的一个通用计算过程模板。例如,尽管不同计算机语言对文件操作的具体步骤和方法不同,但从概念上看,文件操作一般都是遵循打开(或建立)、读/写和关闭三个基本的步骤。也就是说,对于文件操作问题,不同计算机语言的处理方法具有相同的计算过程。为了对这一类问题给出一种良好的解决方案,模板方法模式首先抽象计算过程,定义该计算过程应有的一个个基本处理步骤,并以此建立一个抽象类,封装这个计算过程,建立覆盖所有具体计算过程的通用计算过程,如图3-13(a)所示。同时,在抽象类中再提供一个接口,用来代表这个计算过程,建立通用计算过程模板。该接口就称为模板方法,如图3-13(b)所示。然后,通过抽象类的子类化,由各个子类定义具体的计算过程(即实现计算过程的每个具体步骤),如图3-13(c)所示。客户通过模板方法就可以重用面向不同处理对象的相同计算过程,降低代码的冗余度。图3-13(d)给出了模板方法模式的基本结构。图3-13 模板方法模式

模板方法模式是一种类模式,它定义一个算法的基本结构,而将算法中的一些具体操作步骤描述延迟到子类中,以提供具体的行为。事实上,模板方法模式绑定了一组相关的操作步骤,这些步骤必须按预定顺序逐个执行。因此,模板方法模式可以实现钩子操作(Hook Operation),即通过在一组相关的操作步骤中安排一些钩子操作,实现功能的扩展。从本质上看,模板方法模式给出了父类控制其子类扩展的一种通用解决方案,因为子类只能在允许的扩展点(某些步骤)才能扩展。

模板方法模式广泛应用于应用框架中,因为应用框架需要对其未来的各种子类的扩展进行控制。

4.迭代器(Iterator)模式

所谓迭代,是指在某个计算过程中,每一步的计算都需要其前一步的结果作为计算因子之一。迭代器模式针对一个聚合对象中所有元素的顺序访问问题,通过迭代方式建立一种通用的遍历方法,同时对外隐藏该聚合对象内部的具体表示结构。迭代器模式首先将对聚合对象的访问和遍历行为从聚合对象中分离出来,单独建立一个称为迭代器的类来负责访问和遍历聚合对象的行为,如图3-14(a)所示。迭代器类定义了访问一个聚合对象的应有接口,具体的迭代器对象负责跟踪聚合对象中的当前元素,如图3-14(b)所示。由于聚合对象和迭代器对象是耦合在一起的,因此客户必须知道一个迭代器对象所要操作的聚合对象是谁。为此,可以让聚合对象实例化其相应的迭代器,并自己提供聚合对象,以便迭代器对其进行访问,如图3-14(c)所示。图3-14(d)给出了迭代器模式的基本结构。图3-14 迭代器模式

迭代器模式将对聚合对象的遍历访问机制与聚合对象本身分离,使得客户可以定义不同的迭代器来实现不同的遍历访问策略,而不需要在聚合对象接口定义中一一列出。也就是说,迭代器模式不仅简化了聚合对象本身的接口,还支持以不同的方式遍历一个聚合结构以及对同一个聚合对象同时进行多个遍历(每个遍历由一个迭代器对象实现并维护其自己的遍历状态)。迭代器模式中,迭代过程控制既可以由客户完成(外部迭代器,External Itera-tor),也可以由迭代器自己完成(内部迭代器,Internal Iterator)。一般来说,外部迭代器比较灵活,可以实现内部迭代器不能完成的一些功能;而内部迭代器使用较为方便,因为它已经定义好了迭代逻辑。另外,遍历算法不一定非要在迭代器中定义,也可以在聚合对象中定义(即将各种接口移到聚合对象中,如图3-14(e)所示)。此时,只需要在迭代器中存储并维持当前迭代状态即可(这种迭代器称为游标,Cursor)。显然,迭代器负责遍历算法的处理方案,有利于在相同的聚合对象上使用不同的迭代算法,也有利于在不同的聚合对象上重用相同的遍历算法。但是,如果遍历算法需要访问聚合对象的私有变量,则将遍历算法放在迭代器中会破坏聚合对象的封装性。对于具有递归性质的聚合对象,外部迭代器一般难以实现,因为为了跟踪当前的迭代状态(即某个当前对象标识)必须存储一条纵贯该层次的路径。相反,采用内部迭代器则会更容易实现。因为它仅仅需要递归地调用自己即可,从而隐式地将路径存储在调用栈中,而无需显式地维护当前的迭代状态。

聚合数据组织结构是一种针对批量数据组织的复合组织结构(例如集合、列表、记录式文件、树等),遍历是聚合数据组织结构的一种基本操作,迭代器模式为聚合数据组织的遍历访问提供了一种通用的解决方案。

5.进一步认识行为型模式

针对需要解决的某个问题中可能发生变化的某部分行为,行为型模式基本上都是通过抽象,采用一个单独的对象(对于对象行为模式而言)或方法(对于类行为模式而言)来封装这部分的行为并定义一致性抽象接口。例如策略模式中的Strategy对象(封装一个算法)、观察者模式中的Observer对象(封装对同一个目标所产生的事件的处理方法)、模板方法模式中的TemplateMethod()方法(封装一个算法的基本框架)和迭代器模式中的Iterator对象(封装访问和遍历一个聚合对象中各个元素的方法)。然后,在此基础上,进一步定义这个封装变化行为的独立对象与其他使用它的对象之间的交互协作关系以及建立在协作关系之上的复杂的控制流。例如策略模式中的用一个具体Strategy对象来配置上下文对象,使上下文对象维护一个对Strategy对象的引用;同时,上下文对象也可以定义一个接口供Strategy对象来访问它的数据。观察者模式中,目标对象提供注册和删除接口供Observer对象使用,并且提供通知接口向观察者对象发送事件通知;同时,目标对象也应该提供一些接口供Observer对象来访问它的数据。迭代器模式中,Iterator对象有一个聚合对象的引用并维护当前状态;同时,聚合对象在建立Iterator对象时必须将自身的引用传给Iterator对象。模板方法模式中,采用的是静态类行为模式,因此不涉及多个对象之间的协作,只是通过抽象类来实现各种具体类(及其延伸的对象)所定义的具体算法之间的框架的一致性(即算法的步骤不变。这也可以看做各种不同具体类的对象之间的一种协作,但在此没有交互)。

行为型模式一般都涉及多个承担各自行为的对象之间的协作。但各种模式存在着具体的应用语义区别。例如,观察者模式主要处理动态变化的依赖关系,而不是固定依赖关系。策略模式则是处理固定的依赖关系。模板方法模式主要处理固定的代码框架和代码框架中可变的各步操作具体实现的关系。

3.4 深入认识设计模式

尽管面向对象设计方法可以用来促进良好的设计以及对设计活动进行标准化,但是它仅仅提供一种基本方法,不能描述设计专家的经验。设计模式作为一种可重用的抽象,它捕获了随时间进化与发展的问题的求解方法,这些方法是面向对象设计中的最佳实践方法和有价值的经验。也就是说,设计模式文档化已有经验。因此,相对于基本的面向对象设计而言,设计模式是属于应用层面的知识。这类似于创作经验和语言及语言基本使用方法之间的关系。也就是说,设计模式是在语法之上的一个层面进行,用更大的应用语义概念结构来组织设计和思考设计。从认知角度来看,基本的面向对象设计方法属于显性知识,而设计模式则属于隐性知识,如图3-15(a)所示。图3-15 传统面向对象的设计方法与基于设计模式的设计方法

事实上,设计模式也可以看成另一种面向对象设计观,它拓展了基本的面向对象设计的认识视野,从关注单个对象或单族同构对象关系延伸到关注多个异构对象之间的关系,从关注实现对象到关注对象世界的抽象。基本的面向对象设计方法中,因为其主要关注单个对象或单族同构对象关系,因此一般都是采用继承机制。这种方法导致紧耦合、低内聚的层次结构。尽管将一切作为特例来解决问题是非常容易的,但这会产生高冗余和类爆炸!设计模式因其关注多个异构对象之间的关系以及关注对象世界的抽象,一般都采用聚合机制来定义多个类或对象关系。这种方法强调松耦合、高内聚的平面结构。也就是说,设计模式将基本的面向对象设计方法中的继承机制用来实现抽象类的特化(即接口继承),然后再将整个继承层次结构看做一个高内聚的整体,再在此基础上通过聚合机制关注多个整体之间的松耦合关系(即面向接口编程),如图3-15(b)所示。因此,设计模式可以看成巨型继承层次结构的替代方案。

作为文档化经验的设计模式,可以为开发人员提供一种使用专家设计经验的有效途径。针对某类问题的处理,利用已有的设计模式就能一次又一次地复用有效方案而不必做重复劳动。从而既可以提高开发效率,也可以提高质量。

尽管设计模式可以提高开发效率,但不一定提高执行效率。设计模式的目标主要是增加灵活性和复用性。因为需求总是恒变的,阻止变化是不可能的,知道什么将会变化也是不可能的。因此,设计者应该知道哪里会变化,通过隔离变与不变,使设计(及其最终的代码)能够灵活地适应未来变化。可见,从软件生命周期来看,设计模式本质上也是关注维护。这与软件模型的发展本质具有相同之道。

实现灵活性的基本手段是采用分层思想,将直接关系转变为间接关系。因此,所有设计模式几乎都是如此。图3-16给出若干模式的分层思想解析。图3-16 分层思想及其应用

在模式层次上构建体系结构,需要提高思维层次,建立起模式层次的思维方法。一个设计模式相当于一个词汇,多个设计模式的联合相当于句子,由句子就可以构造段落和文章(即各种设计方案,是模式的具体应用),从而构建各种具有强大灵活性、适应性和扩展性的子系统结构和系统结构(即体系结构)。因此,尽管设计模式领域不可能建立完备的模式设计语言(针对一个具体领域,有可能建立完备的语言),但设计模式确立了部分通用术语(即词汇),帮助我们在应用语义层面理解已有的软件结构和交流设计方案。模式是在语法基础之上的层面应用更大的语义概念结构来组织设计和思考设计。因此,面向模式的思维,要求懂得模式的正确使用、模式组合应用、模式系统、其他方面的模式等。模式的正确使用是指应该正确理解每个模式的使用场景(所针对的问题及其上下文),即知道在什么情况下使用某个模式才是最恰当的。同时,了解一个模式的目的也是重要的,它可以帮助我们选择要使用的模式。模式组合应用是指联合多个模式解决问题。尽管每一种模式都是针对面向对象设计中的一个特定的经常出现的问题给出了解决方案,但并不意味着它们不能联合使用。模式的正确使用和模式组合应用就是所谓的模式建构。模式系统是指在模式思维层次,建立模式库、模式语言以及基于模式的设计工具等等。软件元模型的核心,就是要在模式层次上建立一种开发范型。其他方面的模式是指除了设计模式外的其他模式,例如分析模式、资源管理模式、并发控制模式等等。

最后,更为深入地,从认识论的层面,基于设计模式的方法和传统的基本方法代表着两种思维方法,前者属于演绎式思维,强调整体到局部的认识;后者属于归纳式思维,强调局部到整体的认识(部分到整体不可能得到优美的设计)。从传统的基本方法到基于设计模式的方法,也反映了人们对软件设计问题认识的成熟,标志着软件设计由实践阶段转变为理论阶段研究。也就是说,模式本身并不是重要的,重要的是它教会我们认识和解决关系问题的方法以及对真实的感悟力。

3.5 本章小结

本章主要解析了设计模式的概念和方法,并从应用思维角度深入解析了设计模式的抽象本质。尽管软件设计模式主要针对面向对象的软件设计,然而,由第2章的解析可以清晰地看到,对象模型以后的各种模型都是以面向对象思想为基础。因此,相对于狭义的面向对象软件设计,设计模式强调的接口继承和面向接口设计的思想具有更加广泛和普适的指导意义。因此,高质量软件体系结构的构建必然是建立在设计模式之上,设计模式为软件体系结构提供了基本的构件集合。

习题

1.请解释模式与设计模式的关系。

2.为什么模式必须关注问题及其解决方案两个方面?请给出模式的四个要素。

3.模式是已经存在的,还是新创造的?为什么?

4.请解释模式对体系结构的作用。

5.什么是类设计模式?什么是对象设计模式?它们有什么区别?

6.请解释工厂方法模式、抽象工厂模式、生成器模式和组合模式分别适用于解决什么问题?(即分别用于构建什么类型的对象。)

7.Bridge模式和Decorator模式都是结构型模式,是将已有的功能组合起来。请解释Bridge模式和Decorator模式在功能组合时有何不同?

8.几个人的小公司和几千人的大公司采用不同的管理结构。其中,最重要的一点就是责任转移。通过责任转移分解大公司的复杂管理问题,并建立多个部门之间的协作。设计模式也是给出一种适应变化的稳定结构,它在思维上与公司管理问题有什么共同之处?

9.当一个类需要将其部分职责委托给另一个独立的类的时候,委托类和被委托类成为平行的两个类层次。如何通过工厂方法模式解决该问题?

10.请说明工厂方法模式与抽象工厂模式的区别和联系。

11.假设需要开发多套图形系统,每套系统包括图形的打印和显示,则使用哪种模式比较恰当?请给出设计结构。

12.复合文档通常由各种对象组成,为了解析这种文档,使用哪种模式比较恰当?请给出设计结构。

13.操作系统中的系统调用采用的是什么设计模式?操作系统中的设备管理方法采用的是什么设计模式?网卡是哪一种设计模式的具体体现?

14.Facade模式与Adapter模式有何异同?分别适用哪种场合?

15.如果要显示用于文件管理的树型组织结构,使用哪种模式比较恰当?请给出设计结构。

16.如果要开发一个图形绘制系统,并且支持多种不同的绘制设备,使用哪种模式比较恰当?请给出设计结构。

17.日常生活中,各种服务机构都发展VIP客户,这种服务模式与哪种设计模式思想类似?

18.对于观察者模式,如果观察者只关心目标的某种特定事件,则如何扩展注册操作Attach()?如果目标和观察者之间的依赖关系特别复杂,例如一个操作可能涉及对几个相互依赖的目标进行改动,此时就必须保证仅仅在所有的目标都已经更改完成后,才能一次性地通知它们的观察者,而不是在每个目标改动后就通知观察者。请给出解决该问题的一个可行方案。另外,对于与事件相关的信息(可能大量,也可能少量),既可以以Update()参数的形式传递(Push Model,推模型),也可以由观察者显式地向目标询问(通过getState()操作。Pull Model,拉模型)。请比较这两种实现方式的优缺点并说明它们分别适用于哪种场合。

19.考虑到观察者在更新其状态的过程中需要查询目标的当前状态,因此目标在发出通知前应该确保其自身状态的一致性。如何结合模板方法模式实现这种控制?

20.如果将筛选通知的职责转给目标实现,可以避免额外的不需要的通知,降低广播通知模式的执行代价。如何与策略模式联合起来解决此问题?如果不同的观察者有不同的信息需求,则除了用拉模型外,如何结合策略模式来解决此问题,同时避免回调目标?

21.如果一个要成为观察者的类已经存在,但又不希望对其进行修改,则如何将其集成(提示:适配器模式)?这种方法会不会增加复杂性?

22.工厂方法模式可以应用在模板方法模式中,如何使用?模板方法模式和策略模式都是用来处理算法的变化部分,它们有何不同?如果用多个策略模式互联来实现模板方法模式,是否可行?如果需要访问多个不同的数据库,既可以通过if-then-else语句实现,也可以通过复制和粘贴代码实现。这两种实现方案有何缺点?如果考虑将来还可能支持新的数据库访问,如何给出一种好的实现方案?

23.如果一个具有递归特性的聚合结构(具有层次结构)中,节点有一个接口可以从一个节点移到它的兄弟节点、父节点和子节点,则外部迭代器、内部迭代器和游标三种迭代器中哪种最合适?空迭代器(NullIterator)是一个退化的迭代器,它的IsDone操作总是返回true。这样有助于处理边界条件。如何在具有层次结构的聚合结构中用统一的方法进行遍历(提示:每个节点可以返回遍历其子节点的迭代器,聚合元素和叶节点分别返回一个迭代器实例和一个空迭代器)?

24.在遍历一个聚合结构时,如果增加或删除其中的元素,可能会导致两次访问同一个元素或遗漏某个元素。当然,可以通过复制该聚合并对复制后的聚合进行遍历来解决,但每次发生元素增加或删除时都进行一次复制,代价太大。请问如何解决此问题来确保迭代器的健壮性?

25.分析共性和可变性是应用设计的方法,设计模式建立在此基础上。分析矩阵可以作为一种描述工具用来封装变化。分析矩阵的行用来表示需要处理的具体功能,第一列用来表示与功能对应的概念,其他列用来表示一个概念的多种特殊情况。如何用分析矩阵来确定需要的设计模式?

26.以一种松散的方式把一些模式串接在一起来构造软件是可能的,这样的软件仅仅是一些模式的堆砌,而不紧凑。这不够深刻。然而另有一种组合模式的方式,许多模式重叠在一个物理空间。这样的软件非常紧凑,在一小块空间里集成了许多内涵,而由于这种紧凑,它变得深刻。如何从演绎和归纳两种思维方式来理解这一点?两种思维方式的关系是什么?哪一种更适合高级应用场景?

27.尽管我们可以挖掘各种设计模式,但如何具体使用各种模式却是一个问题。其中,也存在一些更高层次的模式,即模式的模式。你如何理解这一点?

28.以程序设计语言的体系结构作为类比,解释设计模式、多个设计模式联合、模式语言、模式族、模式系统几个概念之间的区别和联系。

29.为什么说模式语言是开放的、不完备的?

30.通过继承建立的类结构关系是固定的静态结构,而运行时快速变化的通信对象的网络则是一种动态结构,两者相互独立。因此,语言机制不可能体现动态,语言的应用可以面向运行时刻的对象及其类型之间的关系的设计,这种设计问题直接影响运行的效率。通过上述这段分析,相对于类设计模式,如何理解对象设计模式的灵活性?

31.适配器模式解决1:1的变化适应性问题,策略模式解决1:n的变化适应性问题,桥接模式解决m:n的变化适应性问题,你是如何看待这个问题的?

32.在面向对象的世界中,封装具有两层含义:OO基础层的数据隐藏(或信息隐藏)和OO应用(模式)层的变化性封装(将变化性封装在一个抽象类中)。请解析这两种封装的关系及作用。

33.如何理解模式语言是开放的?

34.解释下列概念的关系:设计模式、复合设计模式、设计模式语言、设计模式族(或集合,指相同的一般性问题或同一问题的不同方案)和设计模式系统(相同领域或相同问题的若干模式)。

35.如果应用模式也存在其模式,即模式的模式。如何理解模式的这种递归特性?

36.对象复用一般有三种方式:类继承(白箱复用/静态/耦合型对象编程)、对象组合(黑箱复用/动态/分离型接口编程)和参数化类型(模板)。请解析它们之间的区别以及各自的优缺点和适用场合。

37.创建型模式提供实例化时建立接口和实现透明连接。如何理解这一点?请举例解析说明。

38.体系结构关注组件外部可见特性,而设计模式关注组件内容的结构。如何理解这种关系?

39.如何理解“问题分离原则是高效软件开发的关键”?(提示:独立演变、复杂性管理、角色分离、技术基础设施重用等。)

40.请举例解析说明:“策略模式通过改变受委托对象来改变委托对象的行为”。

41.什么是设计复用?什么是实现复用?哪个更重要?

第4章 软件体系结构基本风格

本章主要解析面向同族系统和异族系统的两类软件体系结构基本风格,同时解析由它们衍生的各种典型风格并通过相关产品作为案例解析这些典型风格的具体应用。最后,剖析基本风格的思维本质。

4.1 什么是软件体系结构风格

所谓风格(Style),一般是指某样东西的外观表现形态特征。例如,欧洲建筑物的建筑风格、某种品牌服装的设计风格等等。软件体系结构风格显然是风格的一种特例,或者说是风格概念在软件设计领域的投影或映射。它是指一个软件系统体系结构的基本呈现形态特征。

风格一般由若干基本元素构成,并且采用一种或多种特定的设计手段和方法。也就是说,用特定的方法将一些基本元素组织成一种风格。在此,基本元素相当于素材,体现风格的静态属性;而特定方法和手段实现素材的集成和运用,体现风格的动态属性。对于软件体系结构而言,软件模型和设计模式就是其风格建立的基本元素;特定方法和手段是指分层(Delaminating)。也就是说,通过分层手段,基于软件模型,运用设计模式建立软件体系结构的基本风格。

4.2 软件体系结构基本风格解析

4.2.1 Layer风格概述

Layer风格是面向同族系统的一种软件体系结构风格。所谓同族系统,在此是指一个紧耦合的系统,这种系统内部各个层次之间的关系对外部系统来说是透明的。外部系统只能与该系统的顶层或底层交互,不能与其中间层进行交互。系统内部可以按需与中间层交互。因此,同族系统图4-1 Layer风格基本模型的体系结构风格一般采用垂直型层次划分基本模型进行表现,以体现其紧耦合特性,如图4-1所示。Layer风格是面向单个计算系统的软件系统构造,例如一台计算机、一个网络中继节点等等。

4.2.2 Layer风格案例

1.ISO/OSI RM

20世纪70年代后期,为了统一各个计算机公司提出的网络体系结构,实现由同构计算机系统互连的自封闭网络系统到由不同公司异构计算机系统之间开放互连的统一网络系统,适应计算机网络及其应用的发展需求,国际标准化组织(International Organization for Standardization,ISO)提出了开放系统互连(Open System Interconnect,OSI)参考模型(Reference Model,RM),如图4-2所示。ISO/OSI RM基于Layer风格建立,每一层解决异构计算机系统互连的一个方面的问题,所有层次构成一个完整的整体。ISO/OSI RM的特点在于,各个层次相对独立,通过标准的接口进行交互,并且通过抽象不断由低层向高层提供服务。从而借助于Layer风格将一个完整通信端系统构造的复杂度转化为一个简单层构造的复杂度,实现多维复杂度到一维复杂度的降维。图4-2 ISO/OSI RM

2.TCP/IP协议族

20世纪60年代末,美国ARPA网的成功组建,奠定了以分层协议结构,分组交换技术以及NCP(Network Control Protocol)、TCP/IP(Transfer Control Protocol/Internet Protocol)等重点协议为特征的开放式网络的基础。与ISO/OSI RM不同,TCP/IP协议族采用抽象的统一网络模型,并基于该网络模型研究与实现异构系统的互连。同时,考虑到网络部署成本、网络运行的操作开销等,TCP/IP协议族简化了ISO/OSI RM的上面三个层次,如图4-3所示。图4-3 TCP/IP协议族

20世纪80年代初开始,局域网的诞生和迅速发展,尤其是基于IEEE 802.3标准的以太网(Ethernet)的兴起,迎合并推动了微型机和工作站的发展,同时也促使计算机的应用模式向网络化方向迅速迈进。企业网、校园网等开始出现并逐渐热门起来,它们采用远程网与局域网相结合的方式实现。此时,与ISO/OSI RM相比,TCP/IP协议族的抽象网络模型对于各种网络系统(包括局域网、广域网等)的互连显示出其固有的灵活性和伸缩性,逐渐成为网络互连的事实标准,并最终带来了今天的Internet及其应用的蓬勃发展。

TCP/IP协议族的Layer风格体现在两个方面。首先,从整个体系结构来看,可以以物理网、抽象逻辑网、统一的传输服务和应用服务构成宏观Layer风格。其次,从应用服务Layer本身来看,可以以DNS、Web、领域应用构成微观Layer风格。图4-4是TCP/IP协议族的Layer风格解析。图4-4 TCP/IP协议族的Layer风格解析

3.MVC结构

MVC(Model-View-Controller)结构是面向图形用户界面(Graphics User Interface,GUI)应用开发的一种结构,它将需要展示的数据部分称为模型(Model),将数据的显示部分称为视图(View)。为了支持模型和视图两者的独立演化,通过称为控制器(Controller)的部分将模型和视图耦合在一起。图4-5所示是MVC结构的基本视图。图4-5 MVC结构图4-6 MVC结构的Layer风格及灵活性解析

从本质上看,MVC结构采用的就是Layer风格,它通过增加一个控制器Layer,将模型和视图之间的两者直接耦合关系转变成三层间接耦合关系,从而增加了灵活性。图4-6给出了MVC结构的Layer风格及其灵活性解析。

Microsoft Visual C++开发环境附带的MFC(Microsoft Foundation Class)支持基于MVC结构的应用框架(Application Framework),为用户的桌面窗口应用开发提供帮助。Visual C++将这种结构称为文档(Document)视图(View)结构,将基于这种结构的应用开发称为基于文档-视图结构的应用开发。MFC类库中,通过多个类及其消息传递定义了这种结构的相互依赖关系,用户只要继承各种基类并具体化每个类的既定行为,就可以享用这种结构带来的控制关系。Visual C++的文档视图结构支持文档和视图之间1:1和1:n的关系,并且通过多重文档类型支持两者之间的m:n关系(即一种文档类型对应一种或多种视图,多种文档及其视图的集合体现m:n关系)。图4-7所示是Visual C++文档视图结构的基本视图。其中,Document对应于模型,View对应于视图,Frame及Document Template对应于控制器。图4-8所示是Visual C++文档视图结构的灵活性体现。图4-7 文档-视图结构图4-8 文档-视图结构的灵活性

随着互联网的发展,基于Web的应用不断出现,诞生了面向Web应用的新3-Tier/n-Tier体系结构(有关新3-Tier/n-Tier的详细解析,参见第5章)。针对该体系结构的表示层的应用开发,诞生了各种基于MVC结构的框架。图4-9所示是目前J2EE(Java 2 Enterprise Edition)平台中最为流行的开源框架Struts的基本结构。其中,视图由各种JSP页面组成,模型由各种JavaBean组成(通过JavaBean与数据库系统交互),控制器由ActionServlet及其各种具体的Ac-tion承担(各种Action内部调用JavaBean)。图4-9 Struts框架基本结构

4.2.3 Tier风格概述

Tier风格是面向异族系统的一种软件体系结构风格。所谓异族系统,在此是指一个松耦合的系统,这种系统的各个层次之间的关系对外部系统来说是不透明的。外部系统可以与该系统的任何一个层进行交互。因此,异族系统的体系结构风格一般采用水平型层次划分基本模型进行表现,以体现其松耦合特性,如图4-10所示。Tier风格面向分布式环境的软件系统构造,例如分布式数据库应用系统等等。Tier就是指可以被远程访问的一个层或一个逻辑列。从整个分布式环境来看,Tier就相当于分布式API。图4-10 Tier风格基本模型

4.2.4 Tier风格案例

1.Client/Server结构

微型计算机的诞生,促进了计算机应用模式由主框架体系模式向分布式体系模式转变。由微型计算机、个人工作站和计算机网络构成的分布式环境,奠定了Client/Server(C/S)结构的基础。Client/Server结构中,应用程序从逻辑上分为两个部分,一部分主要用于处理与用户交互的功能,称为Client(客户)端程序;另一部分主要用于处理与业务规则相关的各种计算功能,称为Server(服务器)端程序。两个部分通过网络通信进行请求和处理结果的交互。

数据库技术及其应用系统就是建立在Client/Server结构基础上。Client端程序一般由各种专用或通用的、方便用户界面设计的开发工具承担,例如PowerBuilder、Visual BASIC、Delphi等。Server端程序一般由数据库管理系统(DataBase Management System,DBMS)及其支持的存储过程机制实现。两者之间通过标准的SQL命令进行交互。图4-11所示是数据库应用系统结构的基本视图。图4-11 数据库应用系统结构

2.老3-Tier/n-Tier结构

随着应用的发展,应用规则的处理变得越来越复杂,Client/Server结构中Server端的负荷越来越重,严重影响应用系统的执行效率。同时,Server端代码的维护以及系统的维护也变得十分复杂。为此,对Server端的工作性质从逻辑上进行划分,将用于处理复杂应用规则的代码从数据库管理系统中独立出来,建立一个专门面向应用业务规则处理的逻辑层次,称为业务逻辑层,实现该层功能的系统称为应用服务器。将数据库管理系统及其基本应用称为数据服务层。从而,将Client/Server结构演化为老3-Tier/n-Tier结构,如图4-12所示。图4-12 老3-Tier/n-Tier结构

3.Browser/Server结构

面向大规模应用的实施,Client/Server结构以及老3-Tier/n-Tier结构存在固有的缺陷。这种缺陷主要表现为,客户端由于存在各种异构的平台,导致其应用系统的维护量急剧上升,制约应用的进一步发展。

互联网的诞生,带来了Web应用模式。这种应用模式的Client端是统一的浏览器(Browser),Server端是专门的信息服务器。Web应用主要以信息服务为核心,形成如图4

13所示的Browser/Server结构。事实上,Browser/Server结构是Client/Server结构的一种特殊类型,相对于Client/Server结构的客户端来说,由于浏览器没有太多的处理逻辑,因此其维护比较简单。一般地,将Client/Server结构中的客户端称为胖客户端或富客户端(Rich Client),而将浏览器称为瘦客户端(Thin Client)。

尽管Web应用模式为大规模应用的部署和维护带来了方便性,但是Web应用以信息服务为核心的本质决定其服务器端的逻辑结构与传统的Client/Server结图4-13 Browser/Server结构构或老3-Tier/n-Tier结构的服务器端的逻辑结构存在本质的不同。Web应用的信息组织以HTML语言描述的页面为基本单元,并通过超链接实现信息页面之间的语义链接。这种

应用模式需要预先建立各个具体的信息页面。然而,信息具有动态特性,并且传统的应用模式中,信息一般都是存放在数据库中。因为,为了集成数据库系统并实现信息的动态性,Web应用模式由初期的Browser/Server结构向Browser/Server/Database Server结构演化并发展动态网页技术。动态网页技术通过在静态HTML网页中增加脚本代码来实现数据库的连接和访问以及对信息的处理,通过在信息服务器端执行扩展的脚本代码来动态地生成HTML网页,从而实现数据库系统集成和信息动态呈现的目标。图4-14所示是Browser/Server/Database Server结构的基本视图。图4-15所示是动态网页的执行过程。图4-14 Browser/Server/Database Server结构图4-15 动态网页的执行过程(以Microsoft ASP技术为例)

4.新3-Tier/n-Tier结构

随着应用发展的不断深入,支持动态网页技术的Web应用两层结构(如图4-14所示)存在与Client/Server结构中Server端同样的负载问题,因此该结构自然地演化为面向Web应用的新3-Tier/n-Tier结构,如图4-16所示。图4-16 新3-Tier/n-Tier结构

随着软件模型的发展,软件构造技术日新月异。针对新3-Tier/n-Tier结构的每层,以新型的软件模型及其构造技术、设计模式为基础,学术界和工业界展开了广泛的研究和探索,诞生了一系列技术和开发工具及应用框架(例如上面提到的Struts等)。有关新3-Tier/n-Tier结构及其应用框架的详细解析,参见第5章。

特别是服务模型的诞生,促进了Web应用模式的发展,建立了新型的服务计算范型,并在此基础上诞生面向服务的体系结构(SOA)。有关SOA的详细解析,参见第5章。

4.3 深入认识体系结构基本风格

人类处理复杂问题的基本思路是分而治之。逻辑分层是一种常用的分而治之的基本手段。针对复杂软件系统的体系结构问题,随着人们对软件认识的不断深入以及应用自身发展带来的不同需求,人们先后形成了Layer和Tier两种基本风格。并且,基于这两种风格,随着应用的发展,诞生并演化出多种体系结构形态。从系统化思维的角度来看,Layer风格主要面向一个独立系统,而Tier风格主要面向一个分布式系统。从软件体系结构的整体来看,Layer风格可以看成软件体系结构的微观风格,而Tier风格可以看成软件体系结构的宏观风格,两者的统一形成软件体系结构的完整风格。图4-17体现了这种统一性。图4-17 Layer风格和Tier风格的统一性

更加深入的认识是,分层的本质也体现了逻辑文化的特性。因此,软件体系结构风格蕴涵着以逻辑文化为核心的西方文化要素,从而诠释了风格的文化内涵以及文化、环境、思想和技术之间的相互关系。

4.4 本章小结

本章从基本的分层策略出发,解析了面向同族系统和异族系统的两种基本的软件体系结构风格并论述了两者的关系。同时,针对每种风格,梳理并解析了各种典型的软件体系结构,特别是给出了软件体系结构发展和演化的脉络及其规律。

习题

1.什么是风格?风格与文化有什么关系?

2.什么是Layer风格?什么是Tier风格?请各举一个例子。

3.体系结构风格能否看成体系结构层面的设计模式?为什么?

4.请举例说明一个Client/Server结构的具体应用,并给出相应的实现代码。

5.请解析为什么企业应用伸缩性的需求导致了Client/Server结构向老3-Tier/n-Tier演化。

6.n-Tier中n的含义是什么?

7.Web应用模式与传统应用模式有什么不同?

8.为了集成传统的应用模式,Web应用模式是通过什么方法实现的?

9.老3-Tier/n-Tier结构与新3-Tier/n-Tier结构相比,有什么相同点和不同点?

10.MVC结构的灵活性主要体现在什么方面?

11.Microsoft Visual C++中是如何实现MVC结构的?请通过相应的代码解析MVC结构的灵活性。

12.对于网络互连问题,ISO/OSI RM与TCP/IP协议族分别采用什么样的Layer风格?

13.如何认识Layer风格和Tier风格的统一性?

第5章 软件体系结构案例解析

本章根据目前应用发展的现状,通过具体案例重点解析面向Web应用的新3-Tier/n-Tier体系结构的基本工作原理和面向服务的体系结构(SOA)的基本工作原理。同时,简单解析面向领域的体系结构的基本思想。

5.1 新3-Tier/n-Tier体系结构及其案例

随着互联网的诞生及其不断发展,当前的应用几乎都建立在互联网基础上。各个IT工业巨头都针对互联网应用的发展提出了相应的对策,例如Microsoft公司的DNA(Windows Distributed interNet Architecture)和.NET、SUN公司的ONE(Open Network Environment)等等。为了集成新型的Web应用模式和传统的计算模式,学术界和工业界展开了广泛的研究和探索,在传统的C/S结构和老3-Tier/n-Tier结构的基础上,建立了新3-Tier/n-Tier结构(如图4-16所示),并且针对新3-Tier/n-Tier结构的每一个层次进行深入研究,逐步演化出各种技术、开发工具及应用框架。

5.1.1 表示层基本工作原理及其案例

表示层采用以Browser/Server结构为基础的Web应用模式,任何应用都由一套网页组成,网页可以包含各种多媒体资源,网页之间通过超链接实现应用语义链接。应用部署在一台或多台Web服务器(也称为信息服务器或网站)上。用户通过在浏览器中输入一个网址或点击当前网页中的一个超链接,使用HTTP协议穿越互联网向Web服务器请求所需要的网页,网页由浏览器解释并呈现给用户。图5-1是网页的基本结构,图5-2是Web应用的基本原理。图5-3是Microsoft Windows平台Web应用的案例。图5-1 网页的基本结构图5-2 Web应用的基本原理图5-3 Microsoft Windows平台Web应用的案例

基本的Web应用以信息展示为主要目的,构成应用的网页都是以HTML(HyperText Markup Language)进行描述。这些网页的格式及内容都是固定的。然而,动态性是信息的本质属性。因此,固定的静态网页显然是不能满足应用需求的。为此,动态网页技术应运而生。动态网页技术经历了客户端技术和服务器端技术两个发展阶段,客户端技术包括Mi-crosoft Windows平台的ActiveX控件技术、基于虚拟机的Java Applet技术、客户端脚本(Script)技术和DHTML(Dynamic HTML)技术等;服务器端技术包括通用网关接口技术(Common Gateway Interface,CGI)、专用Web服务器API技术(包括:Internet Server API,ISA-PI;NetScape API,NSAPI)、ASP(Active Server Page)技术、Java Servlet技术、JSP(Java Server Pages)技术和PHP(Personal Home Page或者PHP:Hypertext Preprocessor)技术等。相对于客户端动态网页技术,服务器端动态网页技术具有减少网络流量、避免浏览器的兼容性问题以及提供更高的代码安全性等优点。对于服务器端动态网页技术,根据现实应用的需求,在此主要解析CGI、ASP、JSP和PHP技术。

1.CGI技术

CGI技术是最通用的服务端脚本技术,大部分Web服务器都支持CGI技术。CGI技术建立在操作系统进程概念基础上,通过进程之间的信息交互和多进程运行机制实现动态网页信息的处理及HTML网页的动态生成。用户经由浏览器的请求及相关输入数据通过In-ternet发送给Web服务器,Web服务器利用操作系统进程机制启动相应的CGI程序进程并将相关输入数据通过操作系统共享变量或标准输入(stdin)传送给CGI程序进程,CGI程序进程按要求进行信息的各种处理(包括与数据库管理系统交互、与各种组件的交互以及与其他程序的交互)并将结果合成为HTML网页描述格式,然后再通过操作系统的标准输出(std-out)将最终的HTML网页描述文本传送给Web服务器,由Web服务器经过Internet转发给用户浏览器。在此,Web服务器充当了客户端浏览器和CGI程序进程之间的网关角色。图5-4是CGI技术实现的基本思想。图5-5是CGI技术实现的一个案例。图5-4 CGI技术实现的基本思想图5-5 CGI技术实现的一个案例

由于CGI技术建立在进程概念基础上,涉及Web服务器和CGI程序进程之间的频繁信息交互。因此,对于大量请求的响应,其执行性能受到严重影响。然而,通过独立的CGI程序进程处理动态网页信息具有技术独立性优点,即CGI程序可以通过各种计算机语言实现。

2.ASP技术、JSP技术和PHP技术

ASP、JSP和PHP技术是针对CGI技术的弊端而诞生的新一代动态网页实现技术。这些技术采用抽象和分层策略,将动态网页信息的处理代码和其执行环境分开,通过扩展HTML网页的标记,将处理代码直接嵌入在HTML网页中。同时,定义嵌入代码的标准规范,依据该规范实现一个通用执行环境。每当Web服务器接收到用户端请求时,自动启动执行环境并将用户所请求的含有嵌入代码的动态网页描述文本交给执行环境执行。通过嵌入代码的执行,动态地生成最终的HTML网页传送给客户端浏览器。嵌入HTML网页的代码称为脚本代码(或简称脚本),执行环境称为脚本引擎(Script Engine)。图5-6是基于脚本引擎的动态网页技术实现的基本思想。图5-7是ASP技术实现的基本思想及其案例。图5-8是JSP技术实现的基本思想及其案例。图5-9是PHP技术实现的基本思想及其案例。

ASP技术中,脚本引擎通过动态链接库(Dynamic Link Library,DLL)实现,因此Web服务器IIS与脚本引擎属于同一个进程空间,具有较好的执行性能。ASP可以支持VBScript和JavaScript,并且内置各种常用的基本COM组件(包括用于与客户端交互的对象Request、Re-sponse,与数据库系统交互的ADO等)。ASP技术主要用于Microsoft Windows/IIS(Internet Information Server)或PWS(Personal Web Server)平台。图5-6 基于脚本引擎的动态网页技术实现的基本思想

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载