静的状態管理 〜第2回〜

クラスの例

ベースとなるクラスの例を挙げます。
CPTのCPTコアに依存しない形で定義したクラスです。

/**
 * 画面要素マネージャークラス定義
 */
var ElementManager = (function() {



  //:V Proto クラスのprototypeオブジェクト
  var Proto = Class.prototype;



  //:V instanceMap Object このクラスのインスタンスのマップ
  var instanceMap = {};



  //:V isRegisting Boolean 指定のメソッドでインスタンスを生成中かどうか
  var isRegisting = false;



  /**
   * インスタンスを生成する
   * @param key String キー
   * @param id [String] 画面要素のid
   * @return ElementManager|null インスタンス
   */
  Class.createInstance = function(key, id) {
    var instance = null;
    if(key && !instanceMap[key]) {
      isRegisting = true;
      instance = new Class(id);
      instanceMap[key] = instance;
      isRegisting = false;
    }
    return instance;
  };



  /**
   * インスタンスを取得する
   * @return ElementManager|null インスタンス
   */
  Class.getInstance = function(key) {
    var instance = key ? instanceMap[key] : null;
    if(!instance) {
      instance = null;
    }
    return instance;
  };



  /**
   * あるべき状態を判定する
   * @notice このメソッドはオブジェクトごとにオーバーライドする。
   * @return String あるべき状態
   */
  Proto.judgeState = function() {
    return null;
  };



  /**
   * 画面要素マネージャークラスのコンストラクタ
   * @param id [String] 画面要素のid
   */
  function Class(id) {
    if(!isRegisting) {
      throw new Error("直接コンストラクタを起動することはできません。");
    }



    //:V element Object|null 画面要素のキャッシュ
    var element = null;



    //:V stateMap Object 状態遷移メソッドのマップ(キーは状態名)
    var stateMap = {};



    /**
     * 画面要素を取得する
     * @return Object|null 画面要素
     */
    this.getElement = function() {
      if(!element && id) {
        element = document.getElementById(id);
      }
      return element;
    };



    /**
     * 状態を定義する
     * @param stateName String 状態名
     * @param stateMethod Function その状態にするメソッド
     */
    this.defineState = function(stateName, stateMethod) {
      if(stateName && )((typeof stateName) == "string") && ((typeof stateMethod) == "function"))( {
        if(!stateMap[stateName]) {
          stateMap[stateName] = stateMethod;
        }
      }
    };



    /**
     * 状態を遷移させる
     * @param stateName String 状態名
     */
    this.applyState = function(stateName) {
      if(stateName) {
        var stateMethod = stateMap[stateName];
        if((typeof stateMethod) == "function") {
          try {
            stateMethod.call(this);
          } catch(e) {
            // ignore.
          }
        }
      }
    };



    return this;
  }



  return Class;
})();

このクラスは、次のように使います。
まず、管理したい部分ごとに以下の定義を行います。

  1. 管理したい画面部分ごとにインスタンスを生成します。
  2. その画面部分の状態名とその状態に遷移させるメソッドを定義します。
    これは遷移するかもしれない状態は全て挙げておきます。
  3. その画面部分の“あるべき状態”を判定し、状態名を返すメソッドを実装します。


/* (1) 担当者 */



var 担当オブジェクト = ElementManager.createInstance("キー文字列", "タグのid");






/* (2) 状態定義(1個以上) */



担当オブジェクト.defineState("状態名X", function() {
  // その状態Xに遷移させるメソッド
});



担当オブジェクト.defineState("状態名Y", function() {
  // その状態Yに遷移させるメソッド
});






/* (3) 状態判定 */



担当オブジェクト.judgeState = function() {
  // 定義した状態の何れかをreturnする
};
次に、イベントを実装します。

  1. あるイベントが発生したら、状態をチェックして修正する画面部分ごとに以下を行います。
  2. まずチェック,修正したい画面部分を担当するオブジェクトを取得します。
  3. その担当オブジェクトにあるべき状態を判定させ、
  4. 通常はその結果をそのまま適用させます。


/* イベント */



画面要素.onイベント = function() {



  /* Aを状態チェック&修正 */
  var 担当A = ElementManager.getInstance("キーA");
  var Aのあるべき状態 = 担当A.judgeState();
  担当A.applyState(Aのあるべき状態);



  /* Bも状態チェック&修正 */
  var 担当B = ElementManager.getInstance("キーB");
  var Bのあるべき状態 = 担当B.judgeState();
  担当B.applyState(Bのあるべき状態);



};

課題

増補改訂版Java言語で学ぶデザインパターン入門
Java言語で学ぶデザインパターン入門*1」初版の増補改訂版より引用させて頂きます。
以下のようなログイン画面があります。


<html>
<head>
<title>サンプルプログラム</title>
</head>
<body>
<table cellspacing="0" style="background-color:#CCCCCC;border-style:outset;border-width:4px;">
  <thead>
    <tr>
      <td>
        <input type="radio" id="UserType_Guest" name="UserType" value="Guest" />
        <label for="UserType_Guest">Guest</label>
      </td>
      <td>
        <input type="radio" id="UserType_Login" name="UserType" value="Login" checked="checked" />
        <label for="UserType_Login">Login</label>
      </td>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        Username:
      </td>
      <td>
        <input type="text" id="UserName" name="UserName" size="16" value="" style="width:8em;" />
      </td>
    </tr>
    <tr>
      <td>
        Password:
      </td>
      <td>
        <input type="password" id="Password" name="Password" size="16" value="" style="width:8em;" />
      </td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td>
        <input type="submit" id="Submit" name="Submit" value="OK" style="width:8em;" />
      </td>
      <td>
        <input type="button" id="Cancel" name="Cancel" value="Cancel" style="width:8em;" />
      </td>
    </tr>
  </tfoot>
</table>
</body>
</html>
使い方は、

  • ゲストログイン[Guest]か、ユーザログイン[Login]かを選択する
  • ユーザログインの場合には、ユーザ名[Username]とパスワード[Password]を入力する
  • ログインするなら[OK]ボタン、やめるなら[Cancel]ボタンを押す
    (サンプルプログラムでは、どこにもログインしません。ボタンを押したら単に終了するだけです)

単純そうなプログラムですが、以下の動作を実装するものとします。

  • ゲストログインが選ばれているときには、ユーザ名とパスワードを「無効状態」にして、文字列が入力できないようにする
  • ユーザログインが選ばれているときには、ユーザ名は「有効状態」になり、文字列が入力できるようにする
  • ユーザ名に文字が1文字も入っていない場合には、パスワードは「無効状態」になる
  • ユーザ名に文字が1文字でも入っていたら、パスワードは「有効状態」になる(もちろんゲストログインの場合には、パスワードは「無効状態」である)
  • ユーザ名とパスワードの両方に文字が1文字でも入っている場合には、OKボタンは「有効状態」になり、押せる状態になるが、ユーザ名とパスワードのどちらか一方でも空ならば、OKボタンは「無効状態」になって、押せない状態になる(もちろんゲストログインの場合には、OKボタンは常に「有効状態」である)
  • Cancelボタンは常に「有効状態」で、いつでも押せる状態になっている

この「有効状態」と「無効状態」の管理を実装してください。
なお、ボタンの押下によるイベントは実装不要です。

次回予告

次回は具体例(課題)を用いて説明します。

*1:結城 浩 著 ソフトバンクパブリッシング株式会社