第 1 部分:建立过程
按照下面几步简单的步骤,您可以把面向对象的应用程序组织成具有内聚功能的软件包,这些软件包可以通过Web服务访问。在本系列的第一部分,Scott W.Ambler勾勒了一幅进行快速而且简便的转换的指示图。
Web服务:无疑您已经看到好像有无数的杂志文章和书籍告诉你它们是多么的丰富多彩以及它们是如何革新构建软件系统的方式。不幸的是,这些发表的文章很少着眼于现存的设计和标识出应用程序潜在的可以提供的 Web 服务。本系列正是教你如何解决此问题。
根据Designing Object-Oriented Software一书中提到的,并在Building Object Applications That Work一书中(请参阅 参考资料中这些书的链接和其它推荐的读物)得到扩展的技术,本系列文章介绍了一个过程,这个过程是用于分析传统的对象设计,目的是为了把它部署到分布式环境。我已经在过去的基于CORBA和J2EE的系统里使用了此方法,在Java程语言中现在正在使用它来标识和构建 Web 服务系统。据我的经验,即使底层的技术随着时间的流逝而改变,但是设计过程依然不变。
为了阐明这个过程,在这一系列文章里,我将谈到一个假想的案例研究,这个案例是一个经过简化了的称作SWA Online的电子商务系统。这个应用系统是一个在线的店面系统,顾客可以在此下订单,并把商品运回家。 图 1 展示了一个高级分析类图,它使用统一建模语言(UML)符号显示主类及其相互关系。它描述前端(很可能作为Java服务器页(Java Server pages,JSP)的一个集合被实现)为一个带有 <<application>> 模板的软件包。 维护库存和运输的类似的应用程序也存在但是没有出现在这个例子中。其他的类是一些组成系统的主交易类;其中的一些详细资料在这一系列的以后文章中介绍。
图 1.SWA Online的初始类模型
SWA Online项目小组决定采用 Web 服务方法,因为系统要求一个既可伸缩也非常健壮的解决方案。这个解决方案要求可伸缩,因为项目小组将能够根据需要将服务部署到各个单独的环境;这个解决方案要求健壮,因为小组想可以选择扩展其服务到外部组织或者将来在某些地方用Web服务提供者(WSP)提供的服务替换系统。在首版里,小组打算在防火墙的后面实现大部分的 Web 服务,这是许多组织使用的通用策略。
在面向对象设计中标识Web服务的过程
为了从面向对象应用程序派生一套Web服务,需要标识 域包(domain package)― 商业类/域类的高级集群 ― 和每一个软件包提供的服务。这些软件包于是可以用所选的技术实现(对我们的例子来说,我们使用Java平台)并在环境里部署。实际上,您是正在把类分布到这些软件包里然后通过Web服务把它们集成到一块。所以,要问的一个重要问题是,“每一个软件包应该提供给其客户端什么服务?”为了回答这个问题,本系列将采取下面的步骤(通常采用迭代的方式完成)从面向对象模型中标识Web服务。
简化持久性和系统类
简化继承和聚集层次结构
标识类契约
标识域包
标识域包契约
定义 Web 服务接口
在这一系列接下来的三部分,我将详细深入探究这些问题。您将学会如何简化UML模型、标识域包和定义Web服务。在本系列最后,您应该能够有效地将现有的模型和面向对象应用程序转换为您的商业伙伴服务可用的Web服务。
第 2 部分: 通过简化去粗取精
标识Web服务时首要的任务就是从概念上简化对象设计。那样,当您向前推进此过程的时候,您仅仅需要集中精力在主要的方面。
在这一系列的 第 1 部分,我回顾了采用Web服务的方法为一家在线商店店面重构现有的面向对象设计的策略。这一部分探讨了过程的头两步:简化持久性和系统类以及简化层次结构。这两步集中在概念上简化您正在处理的问题。
一个通用的体系结构策略就是把应用程序分层为不同的类类型。我通常采用一个五层方法构建应用程序:
用户界面(UI)类封装组成系统用户界面的元素,例如,HTML页、GUI屏幕和打印/电子报表。
业务/域类,也称为 实体类,实现应用程序里的基本域类型,例如, 图 1 中的Order和Customer类。
流程类实现与几个类有关的复杂的业务逻辑,例如那些由潜在的Web服务提供的。
持久性类封装对持久性存储的访问,包括关系数据库、平面文件和对象库。
系统类封装技术功能,例如进程间通信或者记录错误日志的方法。
这个方法在我的一本书,The Object Primer 2nd Edition: The Application Developer’s Guide to Object Orientation里面有更详细的介绍(请参阅 参考资料)。
图 1和这一系列的所有其它示例都是从SWA Online示例中抽取出来的,请参阅 第 1 部分了解细节。
图 1. SWA Online的初始类模型
步骤 1:简化持久性和系统类
为此目的,标识面向业务的Web服务,您现在将需要选择忽视持久性和系统层。持久性层可以用几种方式实现,包括硬编码的 SQL 、数据访问类(如Java数据对象(Java Data Object,JDOs))、一个服务(如EJB的容器管理持久性(container-managed persistence,CMP))或者一个或多个Web服务,但并不限于此。相似地,系统层可以作为一个API、包装器类集合或者Web服务实现。这些对象存在并且重要,但是由于简单化的原因,可以把它们的存在看作理所当然的,所以您可以集中精力在事情的业务方面。 图 1不包括任何持久性或者系统类,但是如果包括了,您将创建一个我们的模型视图,它没有显示这些类。
步骤 2:简化层次结构
为了标识服务者,继承和聚集层次结构常常可以被简化。对于继承层次结构,有一条经验可以遵循,如果一个子类没有添加新的契约,那么它实际上可以被忽略,并且通常您可以把一个类层次结构看作单个类。在 图 2 概述的 Address 类中, Territory 类层次结构可以通过忽略State而被简化,因为它没有添加新的契约。而且,现在我会倾向于把整个层次结构简单地看作一个实体,因为剩余的类Country仅仅添加了一个契约,坦白地说它并不是那样令人兴奋。
图 2. Address类
对于聚集层次结构,可以忽略与聚集层次结构外部的其它类不相关联的任何“局部类”。在 图 1 里,您可以看到OrderContact可以被忽略,因为Order是唯一与之关联的类。通过分解聚集和继承层次结构,您简化了模型,在定义子系统时使其更易分析。
简化努力的结果是图 3,正如您所看到的,它是一个可以使用的比 图 1中的原先模型更小的模型。
图 3. SWA Online的简化的类模型
一旦您的模型被简化,下一步就是分析设计并开始把它重组为包。这是这一系列的下一部分的主题。到那时,您应该考虑如何把这里概述的技术应用到自己的面向对象的应用程序中。
第3部分:标识域包
在为应用程序标识可能的Web服务之前,您必须首先标识希望通过那些服务访问的功能内聚包。在这一系列的第 3 部分,Scott W. Ambler向您展示如何组织您的应用程序使其成为一个更好的 Web 服务。
在这最后一部分,我将从概念上简化 SWA Online 的对象设计,SWA Online是一家在线商店店面,它能让客户下定单并把商品运送到客户家。接下来的两步就是标识由余下的业务类提供的类契约,然后标识可能的域包 — 最后实现任何Web服务的相关类集合。
步骤 3:标识类契约
一个契约是其它对象可以请求的一个对象的任何服务或者行为。换句话说,它是一个直接响应来自其它类的消息的操作。契约定义了一个类的外部接口(也称公共接口)。例如,图 1 显示 Item 类的契约包括一些操作,如reorder()、getPrice()和getFullDescription()。(图 1 和这一系列的所有其它示例都是从SWA Online示例中抽取出来的,请参阅参考资料获得到这一系列的第一部分的链接。)为简单起见,您可以忽略图 1 中所有不是类契约的操作,因为它们对分布在不同包中的类之间的通信没什么作用。
图 1. Item类
步骤 4:标识可能的域包
一旦标识了类契约,您就可以开始标识它们提供的可能的域包和服务了。一个域包是一个互相协作以支持可以被认为是黑盒的内聚契约集的类集。基本思想是每个域包将提供一个或者更多的Web服务给其它域包、应用程序,甚至可能是外部系统。表面上看来,域包好象很简单,但是因为它们封装了许多类的行为,所以它们的内部通常相当复杂。
关键目标之一就是把设计组织进几个包,这样可以减少它们之间的信息流量。包之间传递的任何信息(以Web服务调用的形式或者以来自那些调用的结果集的形式)都代表着可能的网络流量。因为希望最小化网络流量以改善应用程序的响应时间,因此,您想要以这样的一种方式设计域包,这种方式使大部分信息流在包内发生而不是在包之间发生。
为了确定一个类是否属于一个域包,您需要分析它确定分布类型所涉及到的协作。服务器类接收消息但是不发送它们;客户机类发送消息但是不接收它们;客户机/服务器类既发送又接收消息。图 2 呈现了一个高级UML协作图,这个图中消息流用箭头指示。每个箭头可以代表几个独立的消息,对象的分布类型用标记c、s和c/s指示。
图 2. 简化的协作图
一旦标识了每个类的分布类型,您就可以开始标识可能的域包了。有这样一条经验可以遵循,服务器类属于一个域包并且常常会形成它们自己的域包,因为它们是应用程序里消息流的最后一站。在图 2 中您可以看到Territory类是一个服务器类,插图把它描述为一个可能的域包(用虚线指示)。这个类层次结构(记住,我们更早的时候强调过此层次结构)上的调用量可能太低以致无法激发您把Territory部署到它自己的服务器(或者服务器站)上的热情。
如果您有一个域包,这个域包是唯一的一个其它类或者域包的服务器,您可以决定合并这两个域包。在图 2 中,您可以看到在Address和Territory之间这类关系的一个示例,表明您可能想把它们合并成一个单独的域包而不是把它们当作两个独立的组件。在选择是否把这些类合并成一个单独的包时,您也可能想考虑其它的问题:
安全性:您想隐藏Territory的存在吗?
性能:在它自己的机器上有Territory会提高整个应用程序的速度吗?
可伸缩性:您需要支持将来对Territory的调用次数快速增长吗?
另一条经验就是客户机类并不属于一个域包。客户机类仅仅生成消息但是不接收它们,然而域包的目的就是响应消息;所以,客户机类没有什么可以添加到包所提供的功能里去。在图 3 中,Shipment是一个纯粹的客户机类并没有分配到一个可能的域包。
另外,当两个类频繁协作时,指出它们应该被放在同一域包里以减少在Web服务调用中对信息进行编组所使用的开销。当交互涉及到大对象(作为参数传递或者作为返回值接收)时特别是这样的;通过把这样的两个类包含进相同的域包里,您减少了它们之间可能的网络流量。最基本的想法就是高度耦合的类要合在一块。
一个相关的启发就是客户机/服务器类属于一个域包,但是可以有几个它可能属于的域包。这就是您需要考虑额外问题的地方,例如进出类的信息流以及添加新类如何影响每个组件的内聚度(事物的各部分在一块有意义的程度)。一个基本的设计规则就是,一个类只要存在并去完成一个域包的目标,那么它就应该是该域包的一部分。虽然在某种类型的 Order 包里包含Item类有意义,但item是order外部的重要实体也同样有意义 — 例如,一个库存控制系统显然对item感兴趣。所以,您可能想把item和order概念放在不同的包里。一个更好的安排会把Item、ShippingContainer、ContainerType和ShippingRestriction放在相同的包里,因为它们都封装了与item有关的任务。
最后,您还应该通过考虑可能的改变对类的影响而完善把它们分配到域包的分配工作。做到这一点的一种方式就是开发一个描述对系统实现的要求的可能改变的改变案例。(为获得更多有关改变案例的信息,请阅读 Designing Hard Software: The Essential Tasks和The Object Primer, Second Edition: The Application Developer’s Guide to Object Orientation。在下面的参考资料中可以找到这两本书的链接。)如果您知道某些类可能改变,那么如果在改变确实发生时限制改变的范围有意义,您就可以选择在一两个组件中实现它们。
图 3 描述了SWA Online系统最后的包图,给包取的名反映了它们包含的类集。这个图的一个不理想的功能是Customer和Order包互相依赖,像这样的循环依赖增加了部署和维护的工作量,暗示着您可能要重新考虑两个包之间的交互或者甚至合并它们。然而到现在,我们仍坚持我们的包图。
图 3. SWA Online的包图
我们接下来的几步就是标识每个包提供的Web服务和那些服务的签名。这些将是这一系列的下一部分的主题。到那时,您应该继续把这里概述的技术应用到自己的应用程序中。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
SAP收购CallidusCloud 与Salesforce竞争
一直被称为后台办公巨头的SAP现在似乎也想在前台办公大展拳脚。 最新的迹象是SAP收购CallidusClou […]
-
API设计如龙生九子 各不相同
IT咨询管理公司CA Technologies对API产业做了个问卷调查,问卷内容涉及API设计风格以及管理部署的新动向。调查结果表明,JSON与XML可谓两分天下。
-
从头开始实现领域驱动设计
领域描述业务;它是驱动企业的概念和逻辑的集合。如果遵循领域驱动设计(DDD)这一本质,那么领域就是应用程序中最重要的组成部分。
-
走出思维定式 数据库/大型机现代化不再是问题
升级和改变组织的主要利益驱动应用的前景,正处于一个压倒性的位置,所以组织将要面临一系列的改变。