现在的位置: 首页 > 综合 > 正文

实用的 Web 服务设计模式,第 2 部分

2012年04月24日 ⁄ 综合 ⁄ 共 5948字 ⁄ 字号 评论关闭
这一系列的第二部分通过对 Command Facade Pattern 作介绍性的描述来继续对 Web 服务领域的应用程序展开讨论,这些应用程序定义完善并符合经过检验的 Web 应用程序设计策略。

在这一系列的上一部分, 我论述了如何利用 Java Messaging Service 来为 Web 服务实现异步编程模型的编程策略。这里我继续着重于 Web 服务环境中简单、被证实的设计模式的使用。我的目标是通过提供实用的范例以提供给您可选择的方式,来对服务实现进行编码以满足特定的操作目的。

Command Facade Pattern

Web 应用程序开发员很熟悉的两个模式是 facadecommand 模式。command facade 模式是这两个模式的联合产物,是为了基于服务的环境而特别设计的。同样地,它包含了这两个源模式的基本特点,但实现的方式却使开发者能够理解用 WSDL 描述并且通过 SOAP 消息可以访问的 Web 服务接口的实现。

Command 模式是非常有名的,它把一些独特的活动封装起来成为可重用的对象,对于每次请求,这些对象的行为都可以被参数化。

图 1. Command Pattern

Command Pattern

Command 对象可能是有状态的,也可能是无状态的。有状态 command 维护它数据的内部状态和特定于每次独立使用的变量。有状态 command 实例通常仅仅使用一次,或者被放入池中,每次客户端使用完以后便回收。当对象实例被创建的时候或者就在通过设置属性调用命令之前,有状态 command 的参数才会指定。无状态 command 实例 不包含内部状态,并且多个客户端可以并发的使用而不需要共享以及回收。当输入这个 command 的调用方法时,无状态 command 的参数才会被指派。

Facade 模式使用单独的高级应用程序组件,用从属组件来服务于封装的互操作,从而简化与系统的交互。换句话说,facade 可以简化客户端和服务提供者之间比较复杂的接口(甚至多个从属组件)。

图 2. Facade Pattern

Facade Pattern

就 像 Command 一样,Facades 也可能是有状态的或者是无状态的。有状态的 Facade 包含多个客户端调用之间的内部状态,这些客户端调用用来调用 facade 中定义的操作。无状态的 Facade 并不包含上一个操作的内部记忆,因此就依赖于每个方法调用传递过来的必需的状态。

Command facade 模式通过引入位于一个或多个 command 对象前端的 facade 接口来合并这两种方式。该模式的目标是向潜在客户端呈现一个不太复杂的、更加友好的用户接口,能完成像 command 模式那样的业务逻辑封装。

图 3. Command facade 模式

Ccommand facade 模式

在 Web 服务世界里,command facade 模式的 facade 组件是通过在 facade 中定义的方法和 port type 中定义的操作之间的一对一关系,从而和用 WSDL 描述的 portType 联系在一起的。为了执行特定的操作,每个方法可以轮流调用一个或多个封装的 command 对象。在给定典型的 Java Web 服务(在这个服务中一个单独的 Java 类支持一个单独的 WSDL portType)中,这个模式的首要目的是把业务逻辑从服务实现类中分离出来,放入更加容易管理并随时间而演变的特定业务对象中。

实现 command 范例

Command facade 模式由两个独特的组件组成:CommandFacadeFacade 的设计与将要指定的 Command 有直接的关系,包含每种类型的 Command 所公开的各种参数选项。因此,适当的设计 command facade 实现的关键首先是设计 command,并从那里定义恰当的 facade 接口。

这个简单的应用程序范例定义了两个非常有用的操作,把输入的字符串转化成大写或小写。这些高度复杂和计算精确的操作被封装进了两个无状态的 command 对象。

然而在您开始以前,您需要定义基础 Command 接口。

清单 1. Command.java



package com.ibm.developerworks.wspattern.two;

public interface Command {
public CommandModel execute(CommandModel model);
}

Command 接口仅仅定义了一个单独的方法,叫做 execute,这个方法接受一个单独的操作,叫做 CommandModelCommandModel 本质上是一个占位符接口,这个特定的 command 实现的目的是允许 command 对象的用户传入特定于这个 command 实现的参数。

清单 2. CommandModel.java



package com.ibm.developerworks.wspattern.two;

public abstract class CommandModel {}

因为每个业务操作都有单独的文本字符串作为输入,这两个操作都使用同样的 CommandModel 实现,如清单 3 所示。

清单 3. CaseCommandModel.java



package com.ibm.developerworks.wspattern.two.commands;

import com.ibm.developerworks.wspattern.two.CommandModel;

public class CaseCommandModel
extends CommandModel {

private String string;

public String getString() {
return string;
}

public void setString(String string) {
this.string = string;
}
}

清单 4. UppercaseCommand.java



package com.ibm.developerworks.wspattern.two.commands;

import com.ibm.developerworks.wspattern.two.Command;
import com.ibm.developerworks.wspattern.two.CommandModel;

public class UppercaseCommand
implements Command {

public CommandModel execute(CommandModel model) {
if (!(model instanceof CaseCommandModel)) {
throw new IllegalArgumentException("Invalid command model");
} else {
CaseCommandModel umodel = (CaseCommandModel)model;
if (umodel.getString() == null) {
throw new IllegalArgumentException("Invalid command model");
} else {
umodel.setString(umodel.getString().toUpperCase());
}
return umodel;
}
}
}

清单 5. LowercaseCommand.java



package com.ibm.developerworks.wspattern.two.commands;

import com.ibm.developerworks.wspattern.two.Command;
import com.ibm.developerworks.wspattern.two.CommandModel;

public class LowercaseCommand
implements Command {

public CommandModel execute(CommandModel model) {
if (!(model instanceof CaseCommandModel)) {
throw new IllegalArgumentException("Invalid command model");
} else {
CaseCommandModel umodel = (CaseCommandModel)model;
if (umodel.getString() == null) {
throw new IllegalArgumentException("Invalid command model");
} else {
umodel.setString(umodel.getString().toLowerCase());
}
return umodel;
}
}
}

请注意 LowercaseCommandUppercaseCommand 这两个对象都没有存储内部状态。更恰当的说,每个 command 需要执行的所有内容都是作为 CommandModel 参数的一部分被传入的。同样注意 execute 方法中返回了一个 CommandModel。Command 可能返回任何它所期望的 CommandModel 实现。在上面的示例那种情况下,同样的 CommandModel 对象被传入 command,并且被更新后返回给调用的应用程序。然而,这些动作不是必需的。

实现 facade

Facade 的目标是为了简化并集中对底层 command 对象的访问,从而使这些 command 更容易作为用 WSDL 描述的 Web 服务而被访问。为了达到这个目的,在 Facade 接口中定义了一些方法,允许被底层 command 公开的各种参数选项作为参数传入到 facade 方法中。在 Facade 接口中一个单独的方法可以执行一个或多个 command 对象,因此 facade 输入参数和方法返回值需要进行很好的选择。

对于这个范例,facade 接口为每个 command 公开了一个公共方法,如清单 6 所示。

清单 6. CommandFacadeService_SEI.java



package com.ibm.developerworks.wspattern.two;

public interface CommandFacadeService_SEI extends java.rmi.Remote {
public java.lang.String toUpper(java.lang.String string);
public java.lang.String toLower(java.lang.String string);
}

这个接口的实现分别包括指定到适当的 command 对象的 toUppertoLower 操作。

清单 7. CommandFacadeService.java



package com.ibm.developerworks.wspattern.two;

import com.ibm.developerworks.wspattern.two.commands.LowercaseCommand;
import com.ibm.developerworks.wspattern.two.commands.UppercaseCommand;
import com.ibm.developerworks.wspattern.two.commands.CaseCommandModel;

public class CommandFacadeService {

private static final String CMD_TOUPPER = "toUpper";
private static final String CMD_TOLOWER = "toLower";

private static java.util.HashMap commands =
new java.util.HashMap();
static {
commands.put(CMD_TOUPPER, new UppercaseCommand());
commands.put(CMD_TOLOWER, new LowercaseCommand());
}

private static Command getCommand(String name) {
return (Command)commands.get(name);
}

public String toUpper(String string) {
CaseCommandModel model = new CaseCommandModel();
model.setString(string);
model = (CaseCommandModel)getCommand(CMD_TOUPPER).execute(model);
return model.getString();
}

public String toLower(String string) {
CaseCommandModel model = new CaseCommandModel();
model.setString(string);
model = (CaseCommandModel)getCommand(CMD_TOLOWER).execute(model);
return model.getString();
}
}

请注意因为 UppercaseCommandLowercaseCommand 对象是无状态的,所以在每次 toUppertoLower 方法被调用的时候,您创建的是一个实例的静态缓存,而不是调用了一个新的实例。

一 旦已经实现了 facade 接口,最后的步骤是使其作为 Web 服务公开给外部。假设该类被公开为符合 JSR-109 的 Web 服务,我强烈推荐使用诸如 IBM® WebSphere® Studio Application Developer(Application Developer)之类的工具来生成所有需要的部署构件。为了使您更加容易使用,我已经包括了一个可下载的 ZIP 文件,包含全部源文件(以 Application Developer 项目的形式),还包括了一个 EAR 文件,包含示例应用程序的所有已编译的源文件。您可以通过点击在这篇技巧顶端或末端的 Code 图标来下载这些文件。

封装

Command facade 模式最重要的优点是,它允许 Web 服务实现后面的业务逻辑被封装进特定的对象,这些对象更容易管理并随时间而变化。只要 command 实现的输入输出保持不变,业务逻辑中的更改不必去影响 Web 服务实现层。更进一步,通过这种方式实现 command 对象允许您在 Web 应用程序的其它领域重用业务逻辑。

本文中提供的例子着重于使用无状态 command 的无状态 facade 上。使用那些通过 HTTP Session 提供的或者 Web 服务资源框架(WS-Resource Framework)中定义的机制,您可以很容易的扩展这个模型,从而支持使用有状态的 facade,并允许一些非常有意思的潜在状况。

【上篇】
【下篇】

抱歉!评论已关闭.