Skip to content.

Sections
Personal tools
You are here: Home » 技術文書 » パターン » パターンCommand with Story

Document Actions
Command with Story
Ver 1.0 - 1999/5/6

(株)永和システムマネジメント   平鍋健児
作成日:初版 1999,3/23
   目的

GUI をもつアプリケーションにおいて,ユーザとの対話によって 情報を集め,必要な情報がすべて集まった段階で実際の動作を行なうようなコマンドを表現する.


   別名

シナリオ(Scenario),インストーラ(Installer), ウィザード(Wizard)


   動機

ワープロ,表計算,ドローツールのような GUI アプリケーションでは, 通常の動作はユーザ主導である.ユーザがその場その場で選択したメニュー や,ダブルクリックした画面上の要素によって,アプリケーションの動作が 決定される.ユーザのアクションはほとんど制約されていない.

別のパターンとして,インストールプログラム(インストーラ)を考える. インストーラの多くは一連のダイアログ(対話画面)によって構成され, 各ダイアログにおいてユーザがとることができるアクションは限られている. 各ダイアログの入力が終わると,ユーザは「次」を指定して次のダイアログに 進むか,または「戻る」を指定して前のダイアログに戻ることができる. 一連のダイアログでの入力が終わり,インストールに必要な情報がすべてそろうと アプリケーションはユーザに「確認」を求め,インストールの実行となる.
「~ウィザード」と通常呼ばれるコマンドでも,システムが提供する道筋 (対話的なストーリ)に従ってユーザの入力が集められ,最終的に1つのコマンド が実行される.このように,アプリケーションがユーザを導くストーリを 持っているコマンドを「ストーリを持つコマンド」と呼ぶことにする.

ストーリを持つコマンドでは,制御を一旦アプリケーションで掌握する必要がある ため,対話は「モーダル」で行なわれ,ユーザのアクションを制約することが多い. GoF の Command パターンを使用してアプリケーションを構築し,1つの Command の execute メソッドでこのようなモーダルな対話を実現した場合, excute メソッドの中が繁雑になることが多い.また,各ダイアログを複数の Command として分割し,「次」の指定で別のコマンドを起動していく様な実装 では,ユーザの入力データが複数のコマンドにばらまかれ,その授受のための 仕組みが必要となる.

このパターンでは,Command パターンを拡張する.1つのコマンドの execute メソッド終了時に,Invoker に対して次のエントリーポイントを 返却する.Invoker は貰ったエントリーポイントを再度引数に与えて execute メソッドを呼び出す.Command が execute をすべて終えたら, 次のエントリーポイントとして END を返すことで,ストーリを持つコマンド の終了とする.


   適用可能性

  • Command パターンを使用し,ユーザを対話的に導きたい場合.
  • Command パターンにおいて,1つのコマンドが多くの対話を必要とする場合.
  • 複数の Command の間で共有すべき状態があるとき.

   構造

構造
structure

   構成要素

  • Invoker
    • コマンドの起動を行なうクラス
    • Command の execute を呼び出す.
    • execute の返値を見,必要であればその値を引数に再度 execute を呼び出す.
  • Command
    • コマンドを表現するクラス
    • execute を実装し,自分の仕事を行なう.
    • 必要であれば次のエントリーポイントを Invoker に返却することで,再入を依頼する.
  • EntryPoint
    • 再入点を表現するクラス
    • Invoker と Command の間のメッセージとして使用される.

   構造

  • Invoker は Command オブジェクトの execute を呼び出す.
  • Command は execute を終了して一旦制御を Invoker に戻すことができる.
  • その際,必要なら EntryPoint オブジェクトを指定して再度 execute の呼び出しを要請する.

協調関係
collaboration


   実装

実装に置いては,以下の点を考慮する必要がある.

  • java においては,inner オブジェクトとして EntryPoint を表現できる. inner クラス自身も Command クラスを継承することで,Invoker は execute メソッド の返値に対してさらに execute を呼び出す実装となる. inner クラスのオブジェクトはそれを囲むクラスのオブジェクトを含んでいるため, inner クラスにおいて親の Command オブジェクトの状態をセットし, 必要なパラメータをCommand オブジェクトに集めることができる.
  • C++ においては,Command クラスのメンバ関数ポインタとして EntryPoint を 表現できる.Invoker は execute メソッドの返値を Command オブジェクトに対する メソッド呼び出しとして起動する.
  • Command から見ると execute のリターンは,次のエントリポイントへの 疑似 goto として記述することになる.

   バリエーション

以下のようなバリエーションが考えられる.

  • エントリポイントを prepare, execute の2つに縮退させ,Invoker がこの 順序で呼び出すように実装する例がある.

   結果

  • Command パターンを利用しながら,ストーリを持ったコマンドをモーダル性を低くして実装できる.
  • Command のチェーンを表現できる.
  • 複数のコマンドが共通の情報を必要とする時,その情報を集める場所を提供することができる.

   サンプルコード

java の inner class による実装

class command {
    public static void main(String v[]) {
        Invoker inv = new Invoker();
        inv.invoke(new InstallCommand());
    }
}
class Invoker {
  public void invoke(Command c) {
    Command next = c;
    while (next != null) {
      System.out.println(next);
      next = next.execute();
    }
  }
}

interface Command {
  public Command execute();
}

class InstallCommand implements Command {
  Hashtable regInfo;   // 登録情報
  String comp[];       // インストールコンポーネント
  String destDir;      // インストール先ディレクトリ

  // 全体のエントリポイント
  public Command execute() {
       // 最初のサブコマンド
       return new RegInfo();
  }

  // 登録情報収集サブコマンド
  private class RegInfo implements Command {
    public Command execute() {
       // regInfo を収集
      regInfo = new Hashtable();
      regInfo.put("name", "hiranabe");
       return new Comp(); // 次のエントリポイント
    }
  }

  // コンポーネント収集サブコマンド
  private class Comp implements Command {
    public Command execute() {
       // comp[] を収集
        comp = new String[] { "compA", "compB", "compC" };
       return new DestDir();
    }
  }

  // インストール先ディレクトリ収集サブコマンド
  private class DestDir implements Command {
    public Command execute() {
       // destDir を収集
       destDir = "/home/hiranabe";
       boolean ok = true;
       if (ok) 
          return new Go();  // インストール実行
       else
          return new RegInfo();  // 最初に戻る
    }
  }

  // インストールの実行
  private class Go implements Command {
    public Command execute() {
       // 実際の実行
       // ここで regInfo, comp[], destDir を
       // 参照することができる.
       System.out.println("regInfo = " + regInfo);
       System.out.println("comp[] = " + comp);
       System.out.println("destDir = " + destDir);
                          
       return null;
    }
  }
  
}


   使用例

  • 弊社で開発中の UML エディタ.

   関連するパターン

  • GoF の Command パターン.このパターンは Command パターンの拡張である.

Kenji Hiranabe <hiranabe@esm.co.jp>
Last modified: Sat May 1 15:07:16 1999




この記事への評価にご協力をお願いします。

良かった 普通 イマイチ