カプセル化 + メッセージハンドラ としてクラスを使う

クラスには、継承とカプセル化の役割があります。
JavaScript は、クラスベースの継承をサポートしませんが、カプセル化JavaScript でも自然に扱えます。
# 継承は委譲などで代用できますし、ダックタイピングという手もあります。

以下は、

  • [1] クラス A1 のインスタンスを作成しメッセージ("meat or fish") をパラメタ("fish")付きで送信, 結果を rv に保存する
  • [2] クラス B1 をインスタンスを生成しメッセージ("selected") を送信。rv をパラメタで渡す

といったクラスインスタンス間のメッセージングを、ライブラリが提供する機能を利用してコード化したものです。

サンプル http://pigs.sourceforge.jp/blog/20090926/uu.msg.htm
# IE で動かない場合は http://pigs.sourceforge.jp を(一時的に)信頼済みサイトに追加してみてください。

<!doctype html><html><head><title></title>
<script src="uu.js"></script>
<script>
function boot(uu) {
  uu.exp();

  var a1 = factory("A1", {
    msgbox: function(msg, p1, p2) {
      return prompt(p1, p2);
    }
  });
  var b1 = factory("B1", {
    msgbox: function(msg, p1, p2) {
      puff("%s: %j", msg, p1);
    }
  });

  var rv = a1.send("select", "meat or fish", "fish"); // [1] インスタンス生成時に自動で定義される a1.send() でメッセージを送信
  b1.post("selected", rv); // [2] インスタンス生成時に自動で定義される b1.post() でメッセージを送信
}
</script>
</head><body>
</body></html>

この例では、 コンビニエンス関数 uu.factory("クラス名", prototype, 引数, ...) により、その場でクラスの定義とインスタンスの生成を行っています。

以下の構文を利用すると、クラスの定義とインスタンス化のタイミングを分離できます。また、シングルトンクラスを作成することもできます。

<!doctype html><html><head><title></title>
<script src="uu.js"></script>
<script>
function boot(uu) {
  uu.exp();

  Class.singleton("A1", { // シングルトンクラス
    init:   function() {}, // コンストラクタ(省略可能)
    stable: function() {}, // セカンダリコンストラクタ(省略可能)
    fin:    function() {}, // デストラクタ(省略可能)
    evbox:  function(evt, p1, p2) {}, // イベントハンドラ(省略可能)
    msgbox: function(msg, p1, p2) { return prompt(p1, p2); } // メッセージハンドラ(省略可能)
  });
  Class("B1", { // ジェネリッククラス
    init:   function() {},
    fin:    function() {},
    evbox:  function(evt, p1, p2) {},
    msgbox: function(msg, p1, p2) { puff("%s: %j", msg, p1); }
  });

  var rv = factory("A1").send("select", "meat or fish", "fish"); // [1]
  factory("B1").post("selected", rv); // [2]
}
</script>
</head><body>
</body></html>

インスタンスは new uu.Class("クラス名", 引数, ...) または uu.factory("クラス名", 引数, ...) で生成します。

[1],[2] は、インスタンス生成時に自動で定義される send(), post() メソッドで個別にメッセージのやり取りを行っていますが、uu.msg.send() や uu.msg.post() を使うと大勢のインスタンスとメッセージのやり取りをすることもできます。

  var a1 = new Class.A1;
  var b1 = new Class.B1;

  var rv = msg.send(a1, "select", "meat or fish", "fish"); // [1]
  msg.post(b1, "selected", rv); // [2]
  • uu.msg.send(インスタンス, "メッセージ", パラメタ1, パラメタ2)
    • インスタンスにメッセージを送信し、結果を返します(ユニキャスト)(同期)。
  • uu.msg.post(インスタンス, "メッセージ", パラメタ1, パラメタ2)
    • インスタンスにメッセージを送信します。結果は返しません(ユニキャスト)(非同期)。
  • uu.msg.send(0, "メッセージ", パラメタ1, パラメタ2)
    • 全てのインスタンスにメッセージを送信し結果を配列で返します(ブロードキャスト)(同期)。post(0) で非同期になります。
  • uu.msg.send([インスタンス1, インスタンス2, ...], "メッセージ", パラメタ1, パラメタ2)
  • uu.factory() や uu.Class() でクラスを定義すると、自動的に uu.msg.send(), post() でメッセージのやり取りが可能になります。

その他の機能

evbox() は DOMイベント用のイベントハンドラです(今回の例では使っていません)。カスタムイベントを受け取ることもできますが、msgbox() があるのでカスタムイベントを利用する機会は滅多にありません。
uu.puff("%s: %j", msg, p1); は alert + sprintf 相当の機能を持ちます。%j は JSON.stringify 相当です。

JavaScriptカプセル化 + メッセージングが使えると

アプリケーションの土台をスッキリ記述できるような気がしてきませんか?