Web サービス・プログラミングのヒントと秘訣: シンプルで実用的な Web サービスのデザイン・パターンを学習 その 2

コマンド・ファサード・パターンでビジネス・ロジックをカプセル化

このシリーズの第二弾では、Command Facade Pattern(コマンド・ファサード・パターン)への入門を介して、明確で証明済みなWebアプリケーションの設計戦略をWebサービスの世界へ応用することに焦点を合わせた考察を続けます。

James Snell, Software Engineer, IBM

James SnellJames Snell 氏はIBM Emerging Technologies Toolkit チームの一員であり、過去数年間にわたり新興のWebサービスの技術そして標準に焦点を合わせてきました。developerWorks にて、新興の技術に焦点を定めるウェブログ(http://www.ibm.com/developerworks/blogs/snell)を管理しています。


developerWorks 貢献著者レベル

2004年 10月 26日

このシリーズの前編では、Webサービス用の非同期プログラミング・モデルを実装するようにJMS(Java Messaging Service)を推進するプログラミング戦略の応用について考察しました。ここでは、Webサービスの環境内にて簡素で証明済みのパターンの使用に専念します。(サービス実装のコーディングの様々な方法を供給し、特定の操作上の目標を達成するのを手助けする)実用的な用例を、この記事は提供します。

コマンド・ファサード・パターン

Webアプリケーションの開発者にとって馴染み深い2種類のパターンは、ファサード・パターン(facade patterns)とコマンド・パターン(command patterns)です。コマンド・ファサード・パターンはそれら2つの組み合わせであり、サービス指向の環境に向けて特定的に設計されています。それはそれなりに双方のソース・パターンの根源的な側面を取り入れますが、(WSDLを使用することにより説明されSOAPメッセージを介してアクセス可能になっている)Webサービス・インターフェースを実装する開発者から見ても意味を成すようにできています。

それぞれの要求にてパラメーター化可能な振る舞いを持つ再利用可能なオブジェクトへの特殊なアクティビティーのカプセル化により、コマンド・パターンは識別されます。

図1. コマンド・パターン
図1. コマンド・パターン

コマンド・オブジェクトはステートフルにもステートレスにもなり得ます。ステートフルなコマンドは、それぞれ個々の使用に特化した変数とデータの内部状態を保持します。ステートフル・コマンド・インスタンスは一般的に一度だけ使われるか、(クライアントがそれを用済みとみなすと同時に)プールされて再利用されます。属性の設定を介するコマンド呼び出しの直前またはオブジェクト・インスタンスが作成されるときに、ステートフルなコマンドへのパラメーターの割り当てが実行されます。ステートレス・コマンド・インスタンスは内部状態を保持せず、複数のクライアントがプーリングと再利用を必要とせずに並行してそれらを使う事ができます。ステートレスなコマンドへのパラメーターの割り当ては、そのコマンドの呼び出しメソッドへの入力として発生します。

(システムとの相互作用を簡素にするのを目的とする)従属するコンポーネントとの相互作用をカプセル化することに貢献する単独の高レベルなアプリケーション・コンポーネントを、ファサード・パターンは使用します。別の言い方をすれば、(複数の従属するコンポーネントも含めた)クライアントとサービス・プロバイダーの間にあるより複雑なインターフェースをファサードは簡素にできます。

図2. ファサード・パターン
図2. ファサード・パターン

コマンド(Command)同様に、ファサード(Facade)もステートフルそしてステートレスにもなり得ます。ステートフルなファサードは、ファサードにて定義される操作へのクライアント呼び出しの間にて内部状態を保持します。ステートレスなファサードは前回の操作の内部メモリーを保持しませんので、それぞれのメソッド呼び出しにて受け渡される必要な状態に依存します。

複数のコマンド・オブジェクトの前段階に位置するファサード・インターフェースを導入することにより、コマンド・ファサード・パターンはこれら2種類のアプローチを結合します。このパターンの目的は、潜在的なクライアントにとってより使いやすくて簡素なインターフェースを提供すると同時に、コマンド・パターンにより達成されるのと同様のビジネス・ロジックのカプセル化を達成することです。

図3. コマンド・ファサード・パターン
図3. コマンド・ファサード・パターン

Webサービスの世界では、コマンド・ファサード・パターンのファサード・コンポーネントはWSDLに記述されるportTypeと関連します。それは、ファサードにて定義されるメソッドとポート・タイプにて定義されるオペレーションの間にある一対一の関係です。特定のオペレーションを実行するために複数のカプセル化されたコマンド・オブジェクトを、それぞれのメソッドは順々に呼び出せます。1つのJavaクラスが1つのWSDLportTypeを支援するJava Webサービスにおいては、このパターンの主要な目的はビジネス・ロジックをサービス実装クラスから外し(より簡単に管理され時間の経過とともに進化をとげた)特殊なビジネス・オブジェクトに入れることです。


コマンドの例を実装

コマンド・ファサード・パターンは2つの特殊タイプ(Command そしてFacade)から成り立っています。Facade の設計はそれが委任する(Command のそれぞれのタイプが公開する多種にわたるパラメーター化のオプションを含む)Command と直接関連します。それゆえに、コマンド・ファサードの実装を適切に設計する鍵は、まず最初にコマンドを設計し、それから適切なファサード・インターフェースを定義することです。

入力ストリングを大文字または小文字に変換する2つの突出して便利なオペレーションを、この簡素な例のアプリケーションは定義します。かなり複雑かつ計算に集中的なそれらのオペレーションは、2つのステートレスなコマンド・オブジェクトとしてカプセル化されています。

しかしながら、開始する前に、ベースとしてのCommand インターフェースを定義する必要があります。

リスト1. Command.java
package com.ibm.developerworks.wspattern.two;
public interface Command {
  public CommandModel execute(CommandModel model);
}

Command インターフェースは、CommandModelと呼ばれるひとつのパラメーターを受け入れるexecuteと呼ばれるひとつのメソッドのみを定義します。CommandModelとは、(そのコマンドの実装に特化したパラメーターをコマンド・オブジェクトのユーザーに提出させる目的のために)特定のコマンドの実装が実装する(事実上)プレースホルダーのインターフェースだと言えます。

リスト2. CommandModel.java
package com.ibm.developerworks.wspattern.two;
public abstract class CommandModel {}

それぞれのビジネス・オペレーションは単独のテキスト・ストリングを入力として受け入れますので、どちらのオペレーションも(リスト3にて示されるのと)同一のCommandModel 実装を使用します。

リスト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; 
    }
  }
}

LowercaseCommand そして UppercaseCommand のどちらもが、内部状態を保持しないことに注意してください。それよりも、それぞれのコマンドが実行するのに必要なもの全てがCommandModel パラメーターの一部として受け渡されます。実行メソッドがCommandModelを戻すことにも注目してください。コマンドは希望するどのCommandModel 実装をも戻せます。上記の例の場合では、コマンドに受け渡されたのと同じCommandModel オブジェクトがアップデートされ呼び出しアプリケーションに戻し返されています。しかしながら、この振る舞いは必須ではありません。


ファサードを実装

基本をなすコマンド・オブジェクトへのアクセスを簡素化し集中させることがファサードの目的であり、それらのコマンドをWSDLに記述されたWebサービスとしてよりアクセス可能になるようにします。この目的を達成するには、(基本をなすコマンドが公開する多種にわたるパラメーター化のオプションが、ファサードのメソッドへのパラメーターとして受け渡されることを許可する)Facade インターフェースにてメソッドを定義します。Facade インターフェースにある単独のメソッドが複数のコマンド・オブジェクトを実行できますので、ファサードの入力パラメーターそしてメソッドの戻り値をきちんと選ぶ必要があります。

この例では、リスト6に示されるとおり、ファサード・インターフェースはそれぞれのコマンドにて1つのパブリック・メソッド(public method)を公開します。

リスト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);
}

このインターフェースの実装は、それぞれのオペレーション(toUpper またはtoLower)が適切なコマンド・オブジェクトに委任することに関与します。

リスト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();   }
}

UppercaseCommand そしてLowercaseCommand のオブジェクトがステートレスなので、toUpper メソッドまたはtoLower メソッドが呼び出されるたびに新規のインスタンスを呼び出すのではなく、呼び出されるインスタンスの静的なキャッシュを作成することに注目してください。

一度ファサード・インターフェースが実装されれば、あとはそれをWebサイトとして公開するのみです。JSR-109に準拠するWebサイトとしてクラスが公開されていると想定した場合、全ての必要なデプロイメントの成果物を生成するためにIBM® WebSphere® Studio Application Developer (Application Developer)のようなツールの使用を強く推奨します。物事をより簡単にするには、例にあるアプリケーションのコンパイルされたソースを含むEARファイルそしてApplication Developer のプロジェクトの形式としての完全なソースを含むZIPファイルのダウンロードをおすすめします。この記事の最初と最後の部分にあるCodeアイコンをクリックすれば、ダウンロードができます。


まとめ

コマンド・ファサード・パターンの重要な利点は、(Webサービス実装に隠れる)ビジネス・ロジックが時間の経過とともに進化をとげてより簡単に管理ができる特殊なオブジェクトへとカプセル化されることを可能にすることです。コマンドの実装の入力と出力が一定であるかぎり、ビジネス・ロジックの変更がWebサービスの実装レイヤーに影響を与える必要はありません。さらに、この方法でコマンド・オブジェクトを実装することはWebアプリケーションの別の領域でビジネス・ロジックを再利用することを可能にします。

この記事で提供された例では、ステートレスなコマンドを使用するステートレスなファサードに専念しました。(HTTP Sessions に提供され、または WS-Resource Frameworkにて定義されたもののような)メカニズムを使用することにより、ステートフルなファサードの使用をサポートするようにこのモデルを容易に拡張でき、興味深い可能性の一部を実現します。


ダウンロード

内容ファイル名サイズ
WebSphereの配置可能なEARファイルws-tip-altdesign2ear.ear701KB
例にて使用されたプログラムのソース・ファイルws-tip-altdesign2code.zip719KB

参考文献

  • コマンド・パターンそしてファサード・パターンについての情報をどうぞ。
  • この記事にあるアプリケーションの例にて使用したWebSphere Studio Application Developerのトライアル版をダウンロードしてみましょう。
  • この記事の前編である第一弾「シンプルで実用的なWebサービスのデザイン・パターンを学習 その1: JMSを使用した非同期Webサービス操作」(developerWorks、2004年10月)をお読みください。
  • Webサービス(それのみならず)を含むEmerging Technology のトピックに関するより深い考察をお求めでしたら、著者のdeveloperWorks weblog を閲覧するのも手です。
  • IBMからのJavaを基にした最新のソフトウェア開発ツールとミドルウェア(トライアル版)、それとオンラインのチュートリアルと記事、そしてオンラインの技術フォーラムを提供するSpeed-start web servicesにて、Webサービスに関する知識(情報)、ツール、そしてスキルを取得しましょう。
  • Webサービスに関連する数百ものタイトルを含む技術書物の包括的なリストのあるDeveloper Bookstoreを訪れてみて下さい。
  • 知識欲の袋に底はないのでしょうか?SOA and web services のページは、数百にもわたる情報豊かな記事と、Webサービス・アプリケーションの開発方法についての(初級、中級、そして上級の)チュートリアルを提供します。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services
ArticleID=245077
ArticleTitle=Web サービス・プログラミングのヒントと秘訣: シンプルで実用的な Web サービスのデザイン・パターンを学習 その 2
publish-date=10262004