资源描述
第一章 交互式三维图形软件开发系统
为什么要研制基于OpenGL的交互式三维图形软件开发工具Intra3D 2.0 ? 本章开头两节将解答这个问题。
软件工程的主要目标是提高质量与生产率,如果没有软件工程思想方法的指导,Intra3D 2.0的设计、编程、扩展等工作可能会陷入混乱的困境。1.3节论述了软件的质量因素,以及提高质量与生产率的三种基本策略:“复用”、“分而治之”和“优化—折衷”。
本章重点是Intra3D 2.0的系统设计,主要从“体系结构设计”、“模块化设计”、“数据结构与算法设计”、“用户界面设计美学”和“程序设计风格”五个方面论述。
1.1 图形标准OpenGL及其高级开发工具
图形标准在图形领域有着重要的地位,是很多大公司与机构争夺的法宝。历史上曾出现的图形标准有Core、GKS、PHIGS、PEX、GL、Dore、RenderMan、Hoops、OpenGL等等。经过了竞争与淘汰,目前OpenGL占据了领导地位,成为国际上公认的3D图形工业标准。SGI公司是赢家,它于1990年着手研制OpenGL。1992年OpenGL体系结构委员会制定了1.0规范,1995年推出1.1规范。1998年OpenGL 1.2在SGI平台实现。OpenGL及其应用程序已在Unix与PC平台得到广泛的应用。
图形标准存在显而易见的优点:它不仅加速了3D应用程序的开发,而且使应用程序的可移植性更好。但是标准亦存在着缺点,Andries Van Dam 指出:“标准是很多人经过多年的努力共同创建的,它的规范与实现总是落后于最新技术。经验表明,绝大多数领域最先进的设计都是极小数精英小组创作的,但是标准却是一个庞大委员会的技术、政治折衷的产物”[Dam 1998]。
OpenGL的发展已近10年,从1.0版本到1.2版本在功能上并无显著的变化。在开发交互式3D图形应用程序方面,OpenGL存在明显的不足:
(1)OpenGL与窗口系统无关,不提供任何交互手段,必须由程序员自己编写所有的交互功能。
(2)OpenGL应用程序性能的优化是程序员面临的一大困难。OpenGL的API(编程接口)非常灵活,不仅针对硬件与软件的优化方法多而相异,即便对于同一数据结构或算法,如果代码的组织不同亦会产生显著的性能差异。
(3)OpenGL 的API是低级的C函数,不提供可复用的对象库或者应用程序框架,开发效率不高。
以上几点表明,除非开发人员既精通图形系统又精通窗口系统的编程,否则难以开发出较好的基于OpenGL的交互式3D应用程序。由于标准不能被轻易创建与修改,它往往跟不上技术的发展与市场的变化。而3D图形是迅猛发展的领域,为了克服这个困难,人们往往在图形标准之上再建立更高级的开发工具(3D Toolkit或3D Engine)。
基于OpenGL的著名的开发工具有:
一、Open Inventor
Open Inventor 是从SGI的Iris Inventor发展而来,适合于开发面向对象的交互式3D图形应用程序。其核心是围绕 Scene Graph的庞大的C++ 类库,它提供场景编辑、交互式绘制、文件输入输出等功能以及相应的图形用户界面。Open Inventor是通用的商业化软件,由Open Inventor体系结构委员会制定标准,可以在多个平台运行。PC平台的Open Inventor由TGS公司开发与销售。[Wernecke 1994] [Wang 1997]
Open Inventor的基本特征是“大而全”,如果你只要开发很小的3D应用程序,使用Open Inventor就象让相扑运动员去干针线活那样笨。
二、Iris Performer
Iris Performer 是高性能实时3D图形开发系统,由SGI公司研制,专用于SGI的高档图形工作站。Iris Performer 提供高层的场景结构,支持多CPU、场景多层次细节(LOD)管理。不提供用于3D交互的图形用户界面。[Rohlf 1994]
Iris Performer我只听说过但没有亲手用过。总统套房好是好,可老百姓住不起。
三、OpenGL Optimeizer/Cosmo3D
OpenGL Optimeizer/Cosmo3D 是面向大型CAD/CAE与可视化应用的开发系统,由SGI公司研制。它可以将大型复杂的造型简化,并快速地绘制。但由于该产品推出的时间不长,短期内难以被广泛采纳。[Paul 1997]
所有的新产品都说自己好并且对用户“必不可少”,但用户总免不了疑神疑鬼,还是等它成熟了再用。
四、GLUT
GLUT 的核心是个小型的C函数库,这些函数可以创建OpenGL窗口、弹出菜单、响应鼠标事件,并可以绘制简单的常见几何形体。GLUT 可在X-Window, Windows NT, OS/2等系统下运行,特别适合于开发不需要复杂界面的OpenGL示例程序。GLUT的源程序是公开的,并且提供了百余个出色的示例程序,可帮助用户进一步了解OpenGL。[Paul 1997] [Kilgard 1996]
GLUT可爱得象南方的小葱,但是派不上大用场。
另外,SGI与Microsoft合作的“Fahrenheit计划”,将综合OpenGL、Direct3D、Open Inventor、Optimeizer/Cosmo3D的优点,创建更优秀的图形与可视化开发系统。可是雷打了两年多,就是不下雨。我在博士入学的第一年就看到了这计划,一直到毕业都没见到产品,幸好我没有干等着。[Fahrenheit ]
1.2 Intra3D 2.0的研制背景
由于Open Inventor在Unix与PC平台得到了广泛的应用,被誉为是交互式3D开发工具的“事实标准”。但显然没有一个3D开发工具能满足所有的应用需求,3D的广泛应用需要更多的开发工具支持。目前在Internet上可以搜索到的各种用途的3D Engine有500多个[Engines],Open Inventor可以更精确地归类于“object oriented drawing and document editing system” [Kochhar 1996]。
随着计算机图形技术与网络技术的迅猛发展,两者的结合势在必行。在商业、科研、教育、娱乐等领域,用于分布式虚拟环境(Distributed Virtual Environments, DVEs)和支持协同工作(Computer Supported Cooperative Work, CSCW)的图形系统已成为研究与应用的热点[Barton 1999] [Pendergast 19999]。著名的DVEs系统有DIVE、dVS、MR、Repo-3D等[MacIntyre 1998]。但是这些DVEs系统缺乏3D开发工具的交互式图形功能以及通用性,而通用的3D开发工具如Open Inventor则又不支持分布式计算和协同工作。由于在窗口系统、图形支撑库、编程语言等方面存在差异,上述DVEs系统和3D开发工具难以方便地结合使用。
为了紧跟发展潮流,我们从国情出发,多方面分析了3D的需求及软硬件条件研制完成了自主版权、运行于PC平台的“交互式协同式三维图形软件开发系统与数据流可视化平台”[林锐 2000b],如图1.1所示,其中:
(1)Intra3D 2.0是基于OpenGL的交互式三维图形软件开发工具,可用于快速开发Window 9X/NT下的交互式三维图形应用软件。(功能示例见彩图1—6)
(2)DVL 2.0是基于OpenGL的数据可视化工具库(Data Visualization Lib),主要用于绘制科学计算数据和商业统计图形。结合Intra3D 2.0和DVL 2.0可以快速开发交互式数据可视化应用软件。
(3)CNC 2.0是用于协同工作的网络通讯开发系统(Cooperative Network Communicator),其核心是支持“发布—订阅模式”和“数据流模式”的类库。结合Intra3D 2.0、DVL 2.0和CNC 2.0,可以快速开发支持协同工作的交互式三维图形与数据可视化应用软件。
(4)GIVE+ 是“分布式数据流可视化平台”。用户从GIVE+ 中选取模块,以“搭积木”的方式将各个模块用数据流连接起来,之后用户就可以直观地控制这些模块的运行。如果用户在GIVE+ 中找不到需要的模块,可以使用Intra3D 2.0、DVL 2.0和CNC 2.0来开发该模块,把开发完成的模块添加到GIVE+的模块库中即可。(功能示例见彩图7、8、9)
通用可视化平台 GIVE与GIVE +
应用
环境
用于协同工作的网络通讯开发系统
CNC 2.0
数据可视化工具库 DVL 2.0
交互式三维图形软件开发工具 Intra3D 2.0
开发
工具
国际图形标准 OpenGL
支撑
PC + Windows 9X/NT
平台
图1.1 交互式协同式的三维图形软件开发系统与数据流可视化平台
图1.1的方案充分体现了“大规模复用”和“分而治之”的软件工程思想(见1.3.节)。如果按工作量划分比例,那么Intra3D 2.0 约占40%,DVL 2.0约占10%,CNC 2.0约占25%,GIVE+ 约占25%。Intra3D 2.0 是上述方案中最重要的软件开发工具。
在设计Intra3D 2.0时,我们分析过百余个OpenGL应用程序,借鉴了Open Inventor与Optimeizer/Cosmo3D的设计优点。以下是Intra3D 2.0与Open Inventor的主要不同点:
(1)体系结构不同。Open Inventor是围绕Scene Graph开发的,可称大而全,但其很多功能在图1.1方案中不实用也不必需。Intra3D 2.0分四层设计,每层少而精,扩展灵活,切合图1.1的方案。
(2) Intra3D 2.0提供C++类库与COM(Component Object Model)库,支持Visual C++、Visual Basic、Delphi编程。这种多语言编程是现代PC应用开发的一个特征。而Open Inventor是从SGI的IRIS Inventor发展而来的,由于需求背景不同,Open Inventor不具备Intra3D 2.0这种特征。
(3)Intra3D 2.0是个免费软件,所有源代码向用户开放。Intra3D 2.0的内核用Visual C++ 编写,其C++类库与COM库各有60余个模块,共有7万多行代码。由于Intra3D 2.0是分层设计的,用户可以方便地修改内核以适应不同的需求。Open Inventor是商业软件,不免费、不公开源代码。
1.3 软件工程思想
本节讲述指导Intra3D 2.0系统设计的软件工程思想[林锐 2000a]。
软件工程的目标是提高软件的质量与生产率,最终实现软件的工业化生产。质量是软件需求方最关心的问题,用户即使不图物美价廉,也要求个货真价实。生产率是软件供应方最关心的问题,老板和员工都想用更少的时间挣更多的钱。质量与生产率之间有着内在的联系。高生产率必须以质量合格为前提。如果质量不合格,对供需双方都是坏事情。从短期效益看,追求高质量会延长软件开发时间并且增大费用,似乎降低了生产率。从长期效益看,高质量将保证软件开发的全过程更加规范流畅,大大降低了软件的维护代价,实质上是提高了生产率,同时可获得很好的信誉。质量与生产率之间不存在根本的对立,好的软件工程方法可以同时提高质量与生产率。
对开发人员而言,如果非得在质量与生产率之间分个主次,那么应该是质量第一,生产率第二。这是因为:
(1)质量直接体现在软件的每段程序中,高质量自然是开发人员的技术追求,也是职业道德的要求。
(2)高质量对所有的用户都有价值,而高生产率只对开发方有意义。
(3)如果一开始就追求高生产率,容易导致急功近利,留下隐患。宁可进度慢些,也要保证每个环节的质量,以图长远利益。
1.3.1 软件的质量因素
由于软件开发是一种智力创作活动,很难象传统工业那样通过执行严格的操作规范来保证软件产品的质量。世上最小心翼翼、最老实巴脚的程序员未必就能开发出高质量的软件来。程序员必须了解软件质量的方方面面(称为质量因素),才能在进行系统设计、程序设计时将高质量内建其中。软件的高质量并不是“管理”出来的,实质上是设计出来的。
那么什么是高质量的程序?
“运行正确”的程序就是高质量的程序吗?
“运行正确”的程序不见得就是高质量的程序。这个程序也许运行速度很低并且浪费内存;也许代码写得一塌糊涂,除了开发者本人谁也看不懂也不会使用。正确性只是反映软件质量的一个因素而已。
软件的质量因素很多,如正确性、精确性、可靠性、容错性、性能、效率、易用性、可理解性、简洁性、可复用性、可扩充性、兼容性等等。
这里关心的主要质量因素为“正确性与精确性”、“性能与效率”、“易用性”、“可理解性与简洁性”和“可复用性与可扩展性”。其中“正确性与精确性”排在第一位,其余的并列,如图1.2所示。
正确性与精确性
易用性
性能与效率
可复用性
与
可扩展性
可理解性与简洁性
图1.2 软件质量因素的分类
一、正确性与精确性
正确性与精确性之所以排在质量因素的第一位,是因为如果软件运行不正确或者不精确,就会给用户造成不便甚至造成损失。机器不会欺骗人,软件运行不正确或者不精确一般都是人造成的。即使一个软件能100% 地按需求规格执行,但是如果需求分析错了,那么对客户而言这个软件也存在错误。即使需求分析完全符合客户的要求,但是如果软件没有100% 地按需求规格执行,那么这个软件也存在错误。开发一个大的软件项目,程序员要为"正确"、"精确"四个字竭尽精力。
与正确性、精确性密切相关的质量因素是容错性和可靠性。
容错性首先承认软件系统存在不正确与不精确的因素,为了防止潜在的不正确与不精确因素引发灾难,系统为此设计了安全措施。在一些高风险的软件系统,如航空航天、武器、金融等系统中,容错性设计非常重要。
可靠性是指在一定的环境下,在给定的时间内,系统不发生故障的概率。可靠性本来是硬件领域的术语。比如某个电子设备,一开始工作很正常,但由于工作中器件的物理性质会发生变化(如发热),慢慢地系统就会失常。所以一个设计完全正确的硬件系统,在工作中未必就是可靠的。软件在运行时不会发生物理性质的变化,人们常以为如果软件的某个功能是正确的,那么它永远都是正确的。可是我们无法对软件进行彻底地测试,无法根除软件中潜在的错误。平时软件运行得好好的,说不准哪一天就不正常了,如“2000年”问题。因此把可靠性引入软件领域是非常有意义的。
二、性能与效率
用户都希望软件的运行速度高些(高性能),并且占用资源少些(高效率)。程序员可以通过优化算法、数据结构和代码组织来提高软件系统的性能与效率。优化的关键工作是找出限制性能与效率的"瓶颈"。附录A论述了OpenGL三维图形程序性能优化的方法。
三、易用性
易用性是指用户感觉使用软件的难易程度。用户可能是操作软件的最终用户,也可能是那些要使用源代码的程序员。易用性没有一个量化的指标,主观性较强。调查表明,当用户不理解软件中的某个特性时,大多数人首先会向同事、朋友请教。要是再不起作用,就向产品支持部门打电话。只有30%的用户会查阅用户手册。一般认为,如果用户不翻阅手册就能使用软件,那么表明这个软件具有较好的易用性。[Cusumano 1995]
四、可理解性与简洁性
开发人员只有在自己思路清晰时才可能写出让别人能理解的程序。编程时要注意不可滥用技巧,应该用自然的方式编程。简洁是一种美,不管是开发人员自己还是用户都会有同感。一个原始的应用问题可能很复杂,但高水平的人就能够把软件系统设计得很简洁。如果软件系统臃肿不堪,它迟早会出问题。简洁是人们对工作"精益求精"的结果。
五、可复用性与可扩展性
可扩展性反映软件适应“变化”的能力。在软件开发过程中,“变化”是司空见惯的事情,如需求的变化、对需求理解的变化、算法的改进、数据及其描述的变化、程序实现技术的变化等等。
复用能同时提高软件的质量与生产率。复用的一种方式是原封不动地使用现成的软构件,另一种方式是对现成的软构件进行必要的扩展后再使用。可复用性好的程序一般也具有良好的可扩展性。下一节将深入论述“复用”。
1.3.2 提高质量与生产率的三种基本策略
人们都有自己的世界观和方法论,能自然而然地运用于生活和工作中。同样,程序员脑子里的软件工程观念会无形地支配他怎么去做事情。软件工程三十年的发展,已经积累了相当多的方法,但这些方法不是严密的理论。实践人员不应该教条地套用方法,更重要的是学会“选择合适的方法”和“产生新方法”。本节讲述提高质量与生产率的三种基本策略:“复用”、“分而治之”、“优化—折衷”。
一、复用
复用是指“利用现成的东西”,被复用的对象可以是有形的物体,也可以是无形的成果。复用不是人类懒惰的表现而是智慧的表现。因为人类总是在继承了前人的成果,不断加以利用、改进或创新后才会进步。
复用的内涵包括了提高质量与生产率。由经验可知,在一个新系统中,大部分的内容是成熟的,只有小部分内容是创新的。一般地可以相信成熟的东西总是比较可靠的(即具有高质量),而大量成熟的工作可以通过复用来快速实现(即具有高生产率)。人们应该把大部分的时间用在小比例的创新工作上,而把小部分的时间用在大比例的成熟工作中,这样才能把工作做得又快又好。
把复用的思想用于软件开发,称为软件复用。据统计,世上已有1000亿多行程序,可见无数功能被重写了成千上万次。面向对象(Object Oriented)学者的一个“呼吁”就是“请不要再发明相同的车轮子了”。
将具有一定集成度并可以重复使用的软件组成单元称为软构件(Software Component)。软件复用可以表述为:构造新的软件系统可以不必每次从零做起,直接使用已有的软构件,即可组装(或加以合理修改)成新的系统。复用方法合理化并简化了软件开发过程,减少了总的开发工作量与维护代价,既降低了软件的成本又提高了生产率。另一方面,由于软构件是经过反复使用验证的,自身具有较高的质量。因此由软构件组成的新系统也具有较高的质量。利用软构件生产应用软件的过程如图1.3所示。
查询软构件库
用构
件建
造新
软件
定义
所需
构件
集合
应用
软件
系统
分解
提取构件
存在
创建新构件
构件不存在
图1.3 利用软构件生产应用软件的过程
软件复用不仅要使自己拿来方便,还要让别人拿去方便,是“拿来拿去主义”。面向对象技术,Microsoft公司的COM规范 [Rogerson 1999],都能很好地用于实现大规模的软件复用。在图1.1的方案中,Intra3D 2.0、DVL 2.0、CNC2.0软件系统的核心都是可复用的对象库,可帮助用户快速地开发交互式协同式的三维图形与数据可视化应用软件。
软构件经典的程序形式是C++的类,例如Open Inventor的构件库是C++类库。目前在PC平台非常流行的一种构件程序形式是COM,COM的一个突出优点是与编程语言无关。Intra3D 2.0同时提供C++类库与COM库,在应用编程时可以支持Visual C++、Visual Basic、Delphi等语言,使得Intra3D 2.0的构件适应面更广。
二、分而治之
分而治之是指把一个复杂的问题分解成若干个简单的问题,然后逐个解决。这种朴素的思想来源于人们生活与工作的经验,完全适合于技术领域。软件人员在执行分而治之的时候,应该着重考虑:复杂问题分解后,每个问题能否用程序实现?所有程序最终能否集成为一个软件系统并有效解决原始的复杂问题?
图1.4表示了软件领域的分而治之策略。诸如软件的体系结构设计、模块化设计都是分而治之的具体表现。软件的分而治之不可以“硬分硬治”。不像为了吃一个西瓜或是一只鸡,挥刀斩成n块,再把每块塞进嘴里粉碎搅拌,然后交由胃肠来消化吸收,象征复杂问题的西瓜或是鸡也就此消失了。
图1.1的设计方案充分体现了“分而治之”方法的有效性。Intra3D 2.0、DVL 2.0、CNC 2.0和GIVE+ 在设计和实现是都是独立的,降低了开发难度。而在应用时,它们可以方便的结合。例如使用Intra3D 2.0和DVL 2.0可以快速地开发交互式数据可视化应用软件。结合Intra3D 2.0、DVL 2.0和CNC 2.0,可以快速开发支持协同工作的交互式三维图形与数据可视化应用软件,并且可以方便地将“模块”加入到GIVE+的可视化集成环境中。
软件系统
复杂问题
解决原始问题
程序1
子问题1
分解 集成
程序2
子问题2
程序n
子问题n
图1.4 软件领域的分而治之策略
三、优化——折衷
软件的优化是指优化软件的各个质量因素,如提高运行速度,提高对内存资源的利用率,使用户界面更加友好,使三维图形的真实感更强等等。想做好优化工作,首先要让开发人员都有正确的认识:优化工作不是可有可无的事情,而是必须要做的事情。当优化工作成为一种责任时,程序员才会不断改进软件中的算法,数据结构和程序组织,从而提高软件质量。
著名的3D游戏软件Quake,能够在PC机上实时地绘制高度真实感的复杂场景。Quake的开发者能把很多成熟的图形技术发挥到极致,例如把Bresenham画线、多边形裁剪、树遍历等算法的速度提高近一个数量级。我第一次看到Quake时不仅感到震动,而且深受打击。这个PC游戏软件的技术水平已经远胜于我所见识到的国内领先的图形学相关科研成果。这对我们日益盛行的点到完止的研发工作真是莫大的讽刺。所以当我们开发的软件表现出很多不可救药的病症时,不要怨机器差。真的是我们自己没有把工作做好。
优化工作的复杂之处是很多目标存在千丝万缕的关系,可谓数不清理还乱。当不能够使所有的目标都得到优化时,就需要“折衷”的策略。软件中的折衷策略是指通过协调各个质量因素,实现整体质量的最优。
软件折衷的重要原则是不能使某一方损失关键的职能,更不可以象“舍鱼而取熊掌”那样抛弃一方。例如3D动画软件的瓶颈通常是速度,但如果为了提高速度而在程序中取消光照明计算,那么场景就会丧失真实感,3D动画也就不再有意义了(如果人类全是色盲,计算机图形学将变得异常简单)。
人都有惰性,如果允许滥用折衷的话,那么一当碰到困难,人们就会用拆东墙补西墙的方式去折衷,不再下苦功去做有意义的优化。所以我们有必要为折衷制定严正的立场:在保证其它因素不差的前提下,使某些因素变得更好。
Intra3D 2.0和DVL 2.0的核心都是基于OpenGL的对象库,这些对象库对应用软件的图形绘制性能有很大的影响。附录A论述了OpenGL三维图形程序性能优化的方法,可以指导Intra3D 2.0、DVL 2.0及其应用软件的优化工作。
1.4 Intra3D 2.0系统设计
系统设计是把需求转化为软件系统的最重要的环节。系统设计的优劣在根本上决定了软件系统的质量。
本章在宏观上讲述Intra3D 2.0系统设计的五方面内容:体系结构设计、模块设计、数据结构与算法设计、“用户界面设计美学”和“程序设计风格”。[林锐 2000a]
在进行系统设计时,我们要关注软件的质量因素,如正确性与精确性、性能与效率、易用性、可理解性与简法性、可复用性与可扩充性等等。即使把系统设计做好了,也并不意味着就能产生好的软件系统。在程序设计、测试、维护等环节还要做大量的工作,无论哪个环节出了差错,都会把好事搞砸了。
1.4.1 体系结构设计原理
体系结构有两个基本特征:
(1)体系结构是对复杂事物的抽象。良好的体系结构是普遍适用的,它可以高效地处理多种多样的个体需求。一提起“房子”,我们的脑中马上就会出现房子的印象。“房子”是人们对住宿或办公环境的一种抽象。不论是办公楼还是民房,同一类建筑物(甚至不同类的建筑物)之间都具有非常相似的体系结构和构造方式。
(2)体系结构在一定的时间内保持稳定。软件开发中最不希望发生的是需求变化,但“需求会发生变化”是个无法逃避的现实。人们希望在需求发生变化时,最好只对软件做些轻微的修改,如果此时不得不去修改软件的体系结构,那么这个软件的体系结构设计是失败的。
良好的体系结构意味着普适、高效和稳定。图1.1的方案中,Intra3D 2.0和DVL 2.0采用的是层次结构,而CNC 2.0和GIVE+ 采用的是Client—Server(客户机—服务器)结构。
一、层次结构
(1)上下级关系的层次结构
上下级关系的层次结构表达了这么一种常识:有些事情比较复杂,我们没法一下子做完,就把事情分为好几层,一层一层地去做。上层的工作总是建立在下层的工作之上。上层子系统可以使用下层子系统的功能,而下层子系统不能够使用上层子系统的功能。如同在军队中,上级可以命令任何下级,而下级不能命令上级。
(2)顺序相邻关系的层次结构
顺序相邻关系的层次结构表明通讯只能在相邻两层之间发生,信息只能被一层一层地顺序传递。这种层次结构的经典之作是计算机网络的OSI参考模型。每一层的目的都是向它的上一层提供一定的服务,而把如何实现这一服务的细节对上一层加以屏蔽。一台机器上的第n层与另一台机器上的第n层进行对话。通话的规则就是第n层的协议。数据不是从一台机器的第n层直接传送到另一台机器的第n层。发送方把数据和控制信息逐层向下传递。最低层是物理介质,它进行实际的通讯。接收方则将数据和控制信息逐层向上传递。当网络设计者在决定一个网络应包含多少层,每一层应当做什么的时候,其中很重要的工作是在相邻层之间定义清晰的接口。接口可以使得同一层能轻易地用某一种实现(Implementation)来替换另一种完全不同的实现(如用卫星信道来代替所有的电话线),只要新的实现能向上层提供同一组服务就可以了。[Tanenbaum 1998]
(3)其它的层次结构
目前在大型商业应用软件系统中还流行一种包含中间件(Middleware)的层次结构,如图1.5所示。中间件支持与平台无关的分布式计算,可以用DCOM和CORBA对象来实现。
Application Systems
Business-specific Components
Middleware
System Software
图1.5 包含中间件的层次结构
二、客户机—服务器结构
让我们先回顾一下早期的电话系统。贝尔(Alexander Graham Bell)于1876年申请了电话专利。那时期的电话必须一对一对地卖,用户自己在两个电话之间拉一根线。如果一个电话用户想和其它几个电话用户通话,他必须拉n根单独的线到每个人的房子里。于是在很短的时间内,城市里到处都是穿过房屋和树木的混乱的电话线。很明显,企图把所有的电话完全互联(如图1.6(a)所示)是行不通的。
1.6(a)完全互联的电话系统 1.6(b)集中交换式的电话系统
贝尔电话公司在1878年开办了第一个交换局。公司为每个客户架设一条线。打电话时,客户摇动电话的曲柄使电话公司办公室的铃响起来,操作员听到铃声以后根据要求将呼叫方和被呼叫方用跳线手工连接起来。这种集中交换式的模型如图1.6(b)所示。很快地,贝尔系统的交换局就出现在各地。人们又要求能打城市间的长途电话,就出现了二级交换局,以后进一步发展为多个二级交换局。[Tanenbaum 1998]
如果将图1.6(b)中的电话看成是客户程序,将中心的交换局看成是服务程序,那么图1.6(b)就是典型的客户机—服务器结构。注意这里客户机和服务器都是指软件而不是指硬件(一台计算机可以放多个客户机和服务器软件)。
客户机—服务器结构存在两个显然的优点:
(1)以集中的方式高效率地管理通讯。前面讲电话系统的故事就是要说明这一点。
(2)可以共享资源。比如在信息管理系统中,服务器将信息集中起来,任何客户机都可以通过访问服务器而获得所需的信息。
客户机和服务器之间的通讯以“请求—响应”的方式进行。客户机先向服务器发起“请求”,服务器再响应这个请求,如图1.7所示。
采用“请求—响应”这种通讯方式的基本动机是为了解决“聚集”(Rendezvous)问题。为了理解这一个问题,设想一个人试图在分离的机器上启动两个程序并让它们进行通讯。还需记住,计算机的运行速度要比人的操作速度高出许多数量级。在他启动第一个程序后,该程序开始执行并向对等程序发送消息。在几个微秒内,它便发现对等程序还不存在,于是就发出一条错误消息,然后退出。此后,他启动了第二个程序。不幸的是,当第二个程序开始执行时,它也找不到第一个程序(早已退出)。即使这两个程序连续地重新试着通讯,但由于它们的执行速度那么高,以致于它们在同一瞬间联系上的概率非常低。在客户机—服务器结构中,服务器在启动后必须(无限期地)等待客户机的“请求”,因此就形成了“请求—响应”这种通讯方式[Comer 1996]。
在Internet/Intranet领域,目前“浏览器—Web 服务器—数据库服务器” 结构是一种非常流行的客户机—服务器结构,如图1.8所示。这种结构最大的优点是:客户机统一采用浏览器软件(如Microsoft公司或Netscape公司的浏览器),这不仅让用户使用方便,而且使得客户机端不存在维护的问题。当然,软件开发布和维护的工作不是自动消失了,而是转移到了Web 服务器端。在Web 服务器端,程序员要用脚本语言编写响应页面。例如用Microsoft的ASP语言查询数据库服务器,将结果保存在Web 页面中,再由浏览器显示出来。
请求
服务器
客户机
响应
图1.7 客户机和服务器之间的通讯以“请求—响应”的方式进行
数据库
服务器
Web 服务器
客户机
ASP Engine
浏览器
HTTP 请求 查询
HTTP 响应
图1.8 “浏览器—Web 服务器—数据库服务器”结构
1.4.2 Intra3D 2.0的层次结构
Intra3D 2.0采用上下级关系的层次结构,它将构件库分成四层,高层构件可以引用低层构件,但低层构件不能引用高层构件,如图1.9所示。
第一层为“基础对象与函数”(Basic Objects and Functions),该层构件集合记为B。第二层为“图形对象”(Graphical Objects),该层构件集合记为G。
第三层为“场景图与节点”(Scene Graph and Nodes),该层的构件集合记为S。
第四层为“绘制与交互”(Rendering and Interaction),该层的构件集合记为R。
Intra3D
COM
库
Intra3D
C++
类库
4、 绘制与交互层
3、 场景图与节点层
2、 图形对象层
1、 基础对象与函数层
Window 9X/NT + OpenGL + Visual C++
图1.9 Intra3D 2.0 体系结构
Intra3D构件库的层次结构具有良好的可扩展性,表现在横向扩展与纵向扩展两方面。横向扩展有两种:一是每层各自增加新构件,如图1.10(a)所示;二是两个已存在的构件库合并成一个更大的构件库,如图1.10(b)所示。纵向扩展是指在Intra3D之上再扩展新的开发工具,纵向扩展模型如图1.11所示。
R1∪R 2
S1∪S2
G1∪G2
B1∪B2
R2
S2
G2
B2
R1
S1
G1
B1
{r1,r 2 }
{s1,s2 }
{g1,g2}
{b1,b2}
{r1,r2,r3 …..}
{s1,s2,s3….}
{g1,g2,g3.….}
{b1,b2,b3. ….}
∪
图1.10(a) 每层各自增加新构件 图1.10(b) 两个构件库合并成一个构件库
R2 S2 G2 B2
3D 开发工具 2
=
R1 S1 G1 B1
3D 开发工具 1
=
图1.11 构件库纵向扩展模型
1.4.3 模块设计原理
在设计好软件的体系结构后,就已经在宏观上了解到各个模块应具有什么功能,应放在体系结构的哪个位置。我们习惯地从功能上划分模块,保持“功能独立”是模块化设计的基本原则。因为,“功能独立”的模块可以降低开发、测试、维护等阶段的代价。但是“功能独立”并不意味着模块之间保持绝对的孤立。一个系统要完成某项任务,需要各个模块相互配合才能实现,此时模块之间就要进行信息交流。
比如手和脚是两个“功能独立”的模块。没有脚时,手照样能干活。没有手时,脚仍可以走路。但如果希望跑得快,那么迈左脚时一定要伸右臂甩左臂,迈右脚时则要伸左臂甩右臂。在设计一个模块时不仅要考虑“这个模块就该提供什么样的功能”,还要考虑“这个模块应该怎样与其它模块交流信息”。
有三个特征因素可以评价模块设计的优劣:“信息隐藏”、“内聚与耦合”和“封闭——开放性”。
一、信息隐藏
为了尽量避免某个模块的行为去干扰同一系统中的其它模块,在设计模块时就要注意信息隐藏。应该让模块仅仅公开必须要让外界知道的内容,而隐藏其它一切内容。模块的信息隐藏可以通过接口设计来实现。一个模块仅提供有限个接口(Interface),执行模块的功能或与模块交流信息必须且只须通过调用公有接口来实现。如果模块是一个C++对象,那么该模块的公有接口就对应于对象的公有函数。如果模块是一个COM对象,那么该模块的公有接口就是COM对象的接口。一个COM对象可以有多个接口,而每个接口实质上是一些函数的集合。[Rogerson 1999]
二、内聚与耦合
内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling)是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块通常意味着弱内聚,而强内聚的模块通常
展开阅读全文