Apr 2

转自http://www.ibm.com/developerworks/cn/java/j-cb02067.html

Lisp 长久以来一直被视为伟大的编程语言之一。其漫长的发展过程(接近五十年)中引发的追随狂潮表明:这是一门非同凡响的语言。在 MIT,Lisp 在所有程序员的课程中占了举足轻重的地位。像 Paul Graham 那样的企业家们将 Lisp 卓越的生产力用作他们事业成功起步的推动力。但令其追随者懊恼万分的是,Lisp 从未成为主流编程语言。作为一名 Java™ 程序员,如果您花一点时间研究 Lisp 这座被人遗忘的黄金之城,就会发现许多能够改进编码方式的技术。

我最近第一次完成了马拉松赛跑,我发 现跑步比我预想的更有价值。我跑了 26.2 英里,通过该步骤,我开始认为这是对身体非常有益的简单活动。一些语言给了我类似的感觉,如 Smalltalk 和 Lisp。对 Smalltalk 来说,引发类似感觉的是对象;Smalltalk 中的一切内容都是在处理对象和消息传递。对于 Lisp 来说,这个至为重要的步骤更为简单。这门语言完全由列表组成。但不要被这个简单的假相所欺骗。这门有着 48 年历史的语言具有难以置信的强大功能和灵活性,这是 Java 语言所不能企及的。

第一次和 Lisp 打交道时,我还是在校大学生,但这次不是很顺利。因为我拼命地想把 Lisp 编入到熟悉的过程化范例中,而不是在 Lisp 的函数结构下工作。尽管 Lisp 并不是一门严格的函数语言(因为一些特性,它不符合最严格的术语定义),但 Lisp 的许多习语和特性有着很强的函数风格。从那以后,我学会了利用列表和函数式编程。

关于本系列

跨越边界系列 文章中,作者 Bruce Tate 提出这样一个观点:如今的 Java 程序员可通过学习其他方法和语言得到很好的其他思路。自从 Java 技术明显成为所有开发项目的最佳选择以来,编程前景已经变化。其他的框架影响着构建 Java 框架的方式,从其他语言学到的概念可以影响您的 Java 编程。您编写的 Python(或 Ruby、Smalltalk……)代码可以改变您处理 Java 编码的方式。

本系列为您介绍与 Java 开发根本不同,但也可以直接应用于 Java 开发的编程概念和技术。在一些示例中,需要对技术进行集成来利用它。在另外一些示例中,您将能够直接应用这些概念。单独的工具不及其他语言和框架能够影响 Java 社区中的开发人员、框架甚至基本方法的思想那么重要。

本期的跨越边界 将重拾这份遗失的财富。我会带您简单地领略一下 Lisp 的基本构造,然后快速的扩展开来。您将学到 Lambda 表达式、递归和宏。这份简单的向导会让您对 Lisp 的高效性和灵活性有所理解。

入门

本文使用 GNU 的 GCL,它针对许多操作系统都有免费下载。但稍作修改,就能使用任何版本的 Common Lisp。请参见 参考资料 获取可用 Lisp 版本的详细说明。

和学习大多数其他语言一样,学习 Lisp 最好的方法就是实践。打开您的解释程序,和我一起编码。Lisp 基本上是一门编译好的语言,通过直接键入命令,就可以轻松地用它进行编程。

列表语言

基本上,Lisp 是一门关于列表的语言。Lisp 中的一切内容(从数据到组成应用程序的代码)都是列表。每个列表都由一些原子 和列表组成。数字就是原子。键入一个数字仅仅会返回该数字作为结果:


清单 1. 简单原子

				
>1
1
>a
Error: The variable A is unbound.

 

如果键入一个字母,解释程序会报错,如清单 1 所示。字母是变量,所以使用之前必须先为其赋值。如果想要引用一个字母或词语而不是变量,请使用引号将其括起来。在变量前加单引号告诉 Lisp 延迟对后续列表或原子进行求值,如清单 2 所示:


清单 2. 延迟求值和引用

				
>"a"
"a"
>'a
A

 

请注意 Lisp 把 a 大写为 A。lisp 假设您希望使用 A 作为符号,因为它没有加括号。后面会讨论赋值,但先要让列表来完成这一任务。简单地讲,Lisp 列表是加了括号并使用空格隔开的原子序列。尝试如清单 3 所示键入一个列表。这个列表是无效的,除非在列表前面加上 '。


清单 3. 键入一个简单列表

				
>(1 2 3)
Error: 1 is invalid as a function.
>'(1 2 3)
(1 2 3)

 

除非在列表前加上 ',否则 Lisp 会像对函数求值那样对每个列表求值。第一个原子是运算符,列表中其余的原子是参数。Lisp 有数目众多的原语函数,正如您预料的那样,其中包括许多数学函数,例如,+、* 和 sqrt(+ 1 2 3) 返回 6(* 1 2 3 4) 返回 24

操纵列表的有两类函数:构造函数选择函数。构造函数构建列表,选择函数分解列表。firstrest 是核心选择函数。first 选择函数返回列表的第一个原子,rest 选择函数返回除第一个原子外的整个列表。清单 4 显示了这两个选择函数:


清单 4. 基本 Lisp 函数

				
> (first '(lions tigers bears))
LIONS

> (rest '(lions tigers bears))
(TIGERS BEARS)

 

这两个选择函数都获取整个列表,返回列表的主要片断。稍后,您将了解递归如何利用这些选择函数。

如果希望构建列表而不是将其分开,就需要构造函数。与在 Java 语言中一样,构造函数构建新元素:在 Java 语言中为对象,在 Lisp 中即为列表。conslistappend 是构造函数示例。核心构造函数 cons 带有两个参数:一个原子和一个列表。cons 将该原子作为第一个元素添加到该列表。如果对 nil 调用 cons,Lisp 将 nil 作为空列表对待,并构建一个含一个元素的列表。append 连接两个列表。list 包含一个由所有参数组成的列表。清单 5 显示了这些构造函数的实际应用:


清单 5. 使用构造函数

				
> (cons 'lions '(tigers bears))
(LIONS TIGERS BEARS)

> (list 'lions 'tigers 'bears)
(LIONS TIGERS BEARS)

> (append '(lions) '(tigers bears))
(LIONS TIGERS BEARS)

 

consfirstrest 一起用时可以构建任何列表。listappend 运算符只是为了方便,但经常会用到它们。事实上,可以使用 consfirstrest 来构建任何列表,或返回任何列表片段。例如,要获取列表的第二或第三个元素,应该获取 rest 中的 first,或 rest 中的 rest 中的 first,如清单 6 所示。或者,若要构建包含两个或三个元素的列表,可以将 consfirstrest 一起使用,来模拟 listappend


清单 6. 构建第二个元素、第三个元素,然后模拟 list 和 append

				
>(first (rest '(1 2 3)))
2

>(first (rest (rest '(1 2 3))))
3

>(cons '1 (cons '2 nil))
(1 2)

>(cons '1 (cons '2 (cons '3 nil)))
(1 2 3)

>(cons (first '(1)) '(2 3))
(1 2 3)

 

这些示例也许无法引起您的兴趣,但在如此简单的原语之上构建一门简洁优美的语言,其中的原理让一些程序员激动不已。这些由列表构建的简单指令构成了递归、高阶函数,甚至是闭包和 continuation 之类高级抽象的基础。因此下面将研究高级抽象。

 



回页首

 

构建函数

可以猜到,Lisp 函数声明为列表。清单 7 构建了一个返回列表第二个元素的函数,展示了函数声明的形式:


清单 7. 构建第二个函数

				
(defun my_second (lst)
  (first (rest lst))
)

 

defun 是用于定义自定义函数的函数。第一个参数是函数名,第二个参数是参数列表,第三个参数是希望执行的代码。可以看出,所有 Lisp 代码都表述为列表。借助这项灵活和强大的功能,就可以像操纵其他任何数据一样操纵应用程序。稍后将看到一些示例使代码和数据之间的区别变得模糊。

Lisp 也处理条件结构,如 if 语句。格式为 (if condition_statement then_statement else_statement)。清单 8 是一个简单的 my_max 函数,用于计算两个输入变量中的最大值:


清单 8. 计算两个整数中的最大值

				
(defun my_max (x y)
  (if (> x y) x y)
)

MY_MAX
(my_max 2 5)

5
(my_max 6 1)

6

 

下面回顾一下到目前为止看到的内容:

  • Lisp 使用列表和原子来表示数据和程序。
  • 对列表求值时将第一个元素看作列表函数,将其他元素看作函数参数。
  • Lisp 条件语句将 true/false 表达式和代码一起使用。

 



回页首

 

递归

Lisp 提供用于迭代的编码结构,但递归是更受欢迎的列表遍历方式。使用 firstrest 组合实现递归效果很好。清单 9 中的 total 函数显示了其运行原理:


清单 9. 使用递归计算列表的总和

				
>(defun total (x)
  (if (null x)
    0
    (+ (first x) (total (rest x)))
  )
)

TOTAL
>(total '(1 5 1))

7

 

清单 9 中的 total 函数将列表当作单个的参数。第一个 if 语句在列表为空的情况下中断递归,返回零值。否则,该函数将第一个元素添加到列表其余部分的总和。现在应该明白如此构建 firstrest 的原因。first 能够去除列表的第一个元素,rest 简化了将尾部递归 (清单 9 中的递归类型)应用于列表其余部分的过程。

由于性能的原因,Java 语言中的递归是有限的。Lisp 提供一项称作尾部递归优化 的性能优化技术。Lisp 编译器或解释器能够将特定形式的递归翻译为迭代,从而允许以一种更为简单明快的方式来使用递归数据结构(如树结构)。

 



回页首

 

高阶函数

如果模糊了数据和代码之间的区别,Lisp 会更有意思。在本系列的前两篇文章中,介绍了 JavaScript 中的高阶函数Ruby 中的闭包。这两项功能都将函数作为参数进行传递。在 Lisp 中,由于函数和列表没有任何区别,高阶函数也就非常简单。

高阶函数的最常见用法或许是 lambda 表达式,这是闭包的 Lisp 版。lambda 函数是用于将高阶函数传入 Lisp 函数的函数定义。例如,清单 10 中的 lambda 表达式计算了两个整数的和:


清单 10. Lambda 表达式

				
>(setf total '(lambda (a b) (+ a b)))
(LAMBDA (A B) (+ A B))

>total
(LAMBDA (A B) (+ A B))

>(apply total '(101 102))
203

 

如果使用过高阶函数或闭包,那么可能更容易理解清单 10 中的代码。第一行代码定义了一个 lambda 表达式并将其和 total 符号绑定到一起。第二行代码仅显示了这个和 total 绑定到一起的 lambda 表达式。最终,最后一个表达式对包含 (101 102) 的列表应用这个 lambda 表达式。

高阶函数提供比面向对象概念更高层次的抽象。可以用它们来更简洁清晰地表达想法。编程的至高境界就是在不牺牲可读性或性能的前提下,用更少的代码提供更强大更灵活的功能。高阶函数能实现所有这些要求。

Lisp 还有两种类型的高阶函数。其中功能最强大的可能是。宏为后面的执行定义 Lisp 对象。可以将宏看作代码模板。请参考清单 11 中的示例:


清单 11. 宏

				
>(defmacro times_two (x) (* 2 x))
TIMES_TWO

>(setf a 4)
4

>(times_two a)
8

 

这个示例应该分为两个阶段进行阅读。第一次赋值定义了宏 times_two。在第二个阶段(称为宏扩展)中,在对 a 求值之前,将 a 扩展为 (* 2 a)。该模板中这项延迟求值方式使宏的功能非常强大。Lisp 语言本身的许多功能都是基于宏的。

结束语

从 年份上讲,Lisp 也许很陈旧,甚至语法也很陈旧。但如果稍作研究,就会发现该语言有着难以置信的强大功能,它的高阶抽象一如既往地有效,并且生产力很高。许多更为现代的语 言从 Lisp 中得到借鉴,但是其中大多数语言的功能无法与 Lisp 媲美。如果 Lisp 拥有 Java 或 .NET 的一部分市场,并且大学中具备 lisp 知识的人也占有一定的比例,我们可能就会立即用它进行编码。

Apr 2

转自http://www.ibm.com/developerworks/cn/rational/r-uml/

回顾20世纪晚期--准确地说是1997年,OMG组织(Object Management Group对象管理组织)发布了统一建模语言(Unified Modeling Language,UML)。UML的目标之一就是为开发团队提供标准通用的设计语言来开发和构建计算机应用。UML提出了一套IT专业人员期待多年的统 一的标准建模符号。通过使用UML,这些人员能够阅读和交流系统架构和设计规划--就像建筑工人多年来所使用的建筑设计图一样。

到了21世纪-- 准确地说是2003年,UML已经获得了业界的认同。在我所见过的专业人员的简历中,75%都声称具备UML的知识。然而,在同绝大多数求职人员面谈之 后,可以明显地看出他们并不真正了解UML。通常地,他们将UML用作一个术语,或对UML一知半解。大家对UML缺乏理解的这种状况,促进我撰写这篇关 于UML 1.4的快速入门文章。当阅读完本文时,您还不具备足够的知识可以在简历上声称自己掌握了UML,但是您已具有了进一步钻研该语言的良好起点。

参考 UML 基础系列的其他文章和教程

 

RSS 订阅 UML 相关文章和教程的 RSS 提要

一些背景知识


正 如前面曾提到过的,UML的本意是要成为一种标准的统一语言,使得IT专业人员能够进行计算机应用程序的建模。UML的主要创始人是Jim Rumbaugh、Ivar Jacobson和Grady Booch,他们最初都有自己的建模方法(OMT、OOSE和Booch),彼此之间存在着竞争。最终,他们联合起来创造了一种开放的标准。(听起来是不 是很熟悉?这个现象类似J2EE、SOAP和Linux的诞生。)UML成为"标准"建模语言的原因之一在于,它与程序设计语言无关。(IBM Rational的UML建模工具被广泛应用于J2EE和.NET开发。)而且,UML符号集只是一种语言而不是一种方法学。这点很重要,因为语言与方法 学不同,它可以在不做任何更改的情况下很容易地适应任何公司的业务运作方式。

既然UML不是一种方法学,它就不需要任何正式的工作产品(即IBM Rational Unified Process?术语中所定义的"工件")。而且它还提供了多种类型的模型描述图(diagram),当在某种给定的方法学中使用这些图时,它使得开发中 的应用程序的更易理解。UML的内涵远不只是这些模型描述图,但是对于入门来说,这些图对这门语言及其用法背后的基本原理提供了很好的介绍。通过把标准的 UML图放进您的工作产品中,精通UML的人员就更加容易加入您的项目并迅速进入角色。最常用的UML图包括:用例图、类图、序列图、状态图、活动图、组 件图和部署图。

深入讨论每类图的细节问题已超出了这篇入门文章的范围。因此,下面仅给出了每类图的简要说明,更详细的信息将在以后的文章中探讨。

 



回页首

 

用例图


用例图描述了系统提供的一个功能单元。用例图的主要目的是帮助开发团队以一种可视化的方式理解系统的功能需求,包括基于基本流程的"角色" (actors,也就是与系统交互的其他实体)关系,以及系统内用例之间的关系。用例图一般表示出用例的组织关系--要么是整个系统的全部用例,要么是完 成具有功能(例如,所有安全管理相关的用例)的一组用例。要在用例图上显示某个用例,可绘制一个椭圆,然后将用例的名称放在椭圆的中心或椭圆下面的中间位 置。要在用例图上绘制一个角色(表示一个系统用户),可绘制一个人形符号。角色和用例之间的关系使用简单的线段来描述,如图1所示。


示例用例图

图1:示例用例图

图字(从上到下):CD销售系统;查看乐队CD的销售统计;乐队经理;查看Billboard 200排行榜报告;唱片经理;查看特定CD的销售统计;检索最新的Billboard 200排行榜报告;排行榜报告服务

用 例图通常用于表达系统或者系统范畴的高级功能。如图1所示,可以很容易看出该系统所提供的功能。这个系统允许乐队经理查看乐队CD的销售统计报告以及 Billboard 200排行榜报告。它也允许唱片经理查看特定CD的销售统计报告和这些CD在Billboard 200排行榜的报告。这个图还告诉我们,系统将通过一个名为"排行榜报告服务"的外部系统提供Billboard排行榜报告。

此外,在用例图中,没有列出的用例表明了该系统不能完成的功能。例如,它不能提供给乐队经理收听 Billboard 200上不同专辑中的歌曲的途径 -- 也就是说,系统没有引用一个叫做"收听Billboard 200上的歌曲"的用例。这种缺少不是一件小事。在用例图中提供清楚的、简要的用例描述,项目赞助商就很容易看出系统是否提供了必须的功能。

 



回页首

 

类图


类 图表示不同的实体(人、事物和数据)如何彼此相关;换句话说,它显示了系统的静态结构。类图可用于表示逻辑类,逻辑类通常就是业务人员所谈及的事物种类 --摇滚乐队、CD、广播剧;或者贷款、住房抵押、汽车信贷以及利率。类图还可用于表示实现类,实现类就是程序员处理的实体。实现类图或许会与逻辑类图显 示一些相同的类。然而,实现类图不会使用相同的属性来描述,因为它很可能具有对诸如Vector和HashMap这种事物的引用。

类在类图上使用包含三个部分的矩形来描述,如图2所示。最上面的部分显示类的名称,中间部分包含类的属性,最下面的部分包含类的操作(或者说"方法")。


类图中的示例类对象

图2:类图中的示例类对象

根 据我的经验,几乎每个开发人员都知道这个类图是什么,但是我发现大多数程序员都不能正确地描述类的关系。对于像图3这样的类图,您应该使用带有顶点指向父 类的箭头的线段来绘制继承关系1,并且箭头应该是一个完全的三角形。如果两个类都彼此知道对方,则应该使用实线来表示关联关系;如果只有其中一个类知道该 关联关系,则使用开箭头表示。


一个完整的类图

图3:一个完整的类图,包括了图2所示的类对象

在 图3中,我们同时看到了继承关系和两个关联关系。CDSalesReport类继承自Report类。一个CDSalesReport类与一个CD类关 联,但是CD类并不知道关于CDSalesReport类的任何信息。CD类和Band类都彼此知道对方,两个类彼此都可以与一个或者多个对方类相关联。

一个类图可以整合其他许多概念,这将在本系列文章的后续文章中介绍。

 



回页首

 

序列图


序列图显示具体用例(或者是用例的一部分)的详细流程。它几乎是自描述的,并且显示了流程中中不同对象之间的调用关系,同时还可以很详细地显示对不同对象的不同调用。

序列图有两个维度:垂直维度以发生的时间顺序显示消息/调用的序列;水平维度显示消息被发送到的对象实例。

序列图的绘制非常简单。横跨图的顶部,每个框(参见图4)表示每个类的实例(对象)。在框中,类实例名称和类名 称之间用空格/冒号/空格来分隔,例如,myReportGenerator : ReportGenerator。如果某个类实例向另一个类实例发送一条消息,则绘制一条具有指向接收类实例的开箭头的连线,并把消息/方法的名称放在连 线上面。对于某些特别重要的消息,您可以绘制一条具有指向发起类实例的开箭头的虚线,将返回值标注在虚线上。就我而言,我总喜欢绘制出包括返回值的虚线, 这些额外的信息可以使得序列图更易于阅读。

阅读序列图也非常简单。从左上角启动序列的"驱动"类实例开始,然后顺着每条消息往下阅读。记住:虽然图4所示的例子序列图显示了每条被发送消息的返回消息,但这只是可选的。


一个示例序列图

图4:一个示例序列图

通 过阅读图4中的示例序列图,您可以明白如何创建一个CD销售报告(CD Sales Report)。其中的aServlet对象表示驱动类实例。aServlet向名为gen的ReportGenerator类实例发送一条消息。该消息 被标为generateCDSalesReport,表示ReportGenerator对象实现了这个消息处理程序。进一步理解可发 现,generateCDSalesReport消息标签在括号中包括了一个cdId,表明aServlet随该消息传递一个名为cdId的参数。当 gen实例接收到一条generateCDSalesReport消息时,它会接着调用CDSalesReport类,并返回一个aCDReport的实 例。然后gen实例对返回的aCDReport实例进行调用,在每次消息调用时向它传递参数。在该序列的结尾,gen实例向它的调用者aServlet返 回一个aCDReport。

请注意:图4中的序列图相对于典型的序列图来说太详细了。然而,我认为它才是足够易于理解的,并且它显示了如何表示嵌套的调用。对于初级开发人员来说,有时把一个序列分解到这种详细程度是很有必要的,这有助于他们理解相关的内容。

 



回页首

 

状态图


状态图表示某个类所处的不同状态和该类的状态转换信息。有人可能会争论说每个类都有状态,但不是每个类都应该有一个状态图。只对"感兴趣的"状态的类(也就是说,在系统活动期间具有三个或更多潜在状态的类)才进行状态图描述。

如 图5所示,状态图的符号集包括5个基本元素:初始起点,它使用实心圆来绘制;状态之间的转换,它使用具有开箭头的线段来绘制;状态,它使用圆角矩形来绘 制;判断点,它使用空心圆来绘制;以及一个或者多个终止点,它们使用内部包含实心圆的圆来绘制。要绘制状态图,首先绘制起点和一条指向该类的初始状态的转 换线段。状态本身可以在图上的任意位置绘制,然后只需使用状态转换线条将它们连接起来。


显示类通过某个功能系统的各种状态的状态图

图5:显示类通过某个功能系统的各种状态的状态图

图 5中的状态图显示了它们可以表达的一些潜在信息。例如,从中可以看出贷款处理系统最初处于Loan Application状态。当批准前(pre-approval)过程完成时,根据该过程的结果,或者转到Loan Pre-approved状态,或者转到Loan Rejected状态。这个判断(它是在转换过程期间做出的)使用一个判断点来表示--即转换线条间的空心圆。通过该状态图可知,如果没有经过Loan Closing状态,贷款不可能从Loan Pre-Approved状态进入Loan in Maintenance状态。而且,所有贷款都将结束于Loan Rejected或者Loan in Maintenance状态。

 



回页首

 

活动图


活 动图表示在处理某个活动时,两个或者更多类对象之间的过程控制流。活动图可用于在业务单元的级别上对更高级别的业务过程进行建模,或者对低级别的内部类操 作进行建模。根据我的经验,活动图最适合用于对较高级别的过程建模,比如公司当前在如何运作业务,或者业务如何运作等。这是因为与序列图相比,活动图在表 示上"不够技术性的",但有业务头脑的人们往往能够更快速地理解它们。

活动图的符号集与状态图中使用的符号集类似。像状态图一样,活动图也从一个连接到初始活动的实 心圆开始。活动是通过一个圆角矩形(活动的名称包含在其内)来表示的。活动可以通过转换线段连接到其他活动,或者连接到判断点,这些判断点连接到由判断点 的条件所保护的不同活动。结束过程的活动连接到一个终止点(就像在状态图中一样)。作为一种选择,活动可以分组为泳道(swimlane),泳道用于表示 实际执行活动的对象,如图6所示。


活动图

图6:活动图,具有两个泳道,表示两个对象的活动控制:乐队经理,以及报告工具

图字(沿箭头方向):乐队经理;报告工具;选择"查看乐队的销售报告";检索该乐队经理所管理的乐队;显示报告条件选择屏幕;选择要查看其销售报告的乐队;从销售数据库检索销售数据;显示销售报告。

该活动图中有两个泳道,因为有两个对象控制着各自的活动:乐队经理和报告工具。整个过程首先从乐队经理选择查看 他的乐队销售报告开始。然后报告工具检索并显示他管理的所有乐队,并要求他从中选择一个乐队。在乐队经理选择一个乐队之后,报告工具就检索销售信息并显示 销售报告。该活动图表明,显示报告是整个过程中的最后一步。

 



回页首

 

组件图


组件图提供系统的物理视图。它的用途是显示系统中的软件对其他软件组件(例如,库函数)的依赖关系。组件图可以在一个非常高的层次上显示,从而仅显示粗粒度的组件,也可以在组件包层次2上显示。

组 件图的建模最适合通过例子来描述。图7显示了4个组件:Reporting Tool、Billboard Service、Servlet 2.2 API和JDBC API。从Reporting Tool组件指向Billboard Service、Servlet 2.2 API和JDBC API组件的带箭头的线段,表示Reporting Tool依赖于那三个组件。


组件图显示了系统中各种软件组件的依赖关系

图7:组件图显示了系统中各种软件组件的依赖关系

 



回页首

 

部署图


部署图表示该软件系统如何部署到硬件环境中。它的用途是显示该系统不同的组件将在何处物理地运行,以及它们将如何彼此通信。因为部署图是对物理运行情况进行建模,系统的生产人员就可以很好地利用这种图。

部 署图中的符号包括组件图中所使用的符号元素,另外还增加了几个符号,包括节点的概念。一个节点可以代表一台物理机器,或代表一个虚拟机器节点(例如,一个 大型机节点)。要对节点进行建模,只需绘制一个三维立方体,节点的名称位于立方体的顶部。所使用的命名约定与序列图中相同:[实例名称] : [实例类型](例如,"w3reporting.myco.com : Application Server")。


部署图

图 8:部署图。由于Reporting Tool组件绘制在IBM WebSphere内部,后者又绘制在节点w3.reporting.myco.com内部,因而我们知道,用户将通过运行在本地机器上的浏览器来访问 Reporting Tool,浏览器通过公司intranet上的HTTP协议与Reporting Tool建立连接。

图8中的部署图表明,用户使用运行在本地机器上的浏览器访问 Reporting Tool,并通过公司intranet上的HTTP协议连接到Reporting Tool组件。这个工具实际运行在名为w3reporting.myco.com的Application Server上。这个图还表明Reporting Tool组件绘制在IBM WebSphere内部,后者又绘制在w3.reporting.myco.com节点内部。Reporting Tool使用Java语言通过IBM DB2数据库的JDBC接口连接到它的报告数据库上,然后该接口又使用本地DB2通信方式,与运行在名为db1.myco.com的服务器上实际的DB2 数据库通信。除了与报告数据库通信外,Report Tool组件还通过HTTPS上的SOAP与Billboard Service进行通信。

 



回页首

 

结束语


尽 管本文仅提供了对统一建模语言UML的简要介绍,但还是鼓励大家把从这里学到的基本信息应用到自己的项目中,同时更深入地钻研UML。已经有多种软件工具 可以帮助您把UML图集成到软件开发过程中,不过即使没有自动化的工具,您也可以使用白板上的标记或者纸和笔来手工绘制UML图,仍然会获益匪浅。