Hachirog

都内で働いているWebエンジニアのブログです

Revealing Module Patternで盲点だったこと

これまで業務ではJavaScriptを場当たり的にコーディングしてきたので、今年は基本的なところから見直そうと思っています。JavaScriptの実装でいつもに気になっていたのがオブジェクトの生成で、どういった書き方が定石なのか、本で調べてもネットで調べてもいろいろ意見があって悩ましいところでした。

その中で、良さそうと思ったのが『JavaScriptデザインパターン』の中でも紹介されている「リビーリングモジュールパターン(Revealing Module Pattern)」です。

JavaScriptデザインパターン

JavaScriptデザインパターン

Revealing Module Patternによるオブジェクトの作り方

JavaScriptデザインパターン』にならったRevealing Module Pattern

var messageCard = function(){

  var decoration = '+';
  var history = [];

  function create(to, message){
    var head = createHead(to);
    var body = createBody(message);
    var contents = head + body;
    history.push(contents);
    return contents;
  }

  function createHead(to){
    return 'Dear ' + to + '\n';
  }

  function createBody(message){
    var line = createLine(message.length + 4, decoration) + '\n';
    var body = line;
    body += decoration + ' ' + message + ' ' + decoration + '\n';
    body += line;
    return body;
  }

  function createLine(length, char){
    var line = '';
    for(var i = 0; i < length; i++){
      line += char;
    }
    return line;
  }

  function getHistory(index){
    return history[index];
  }

  return {
    create: create,
    getHistory: getHistory
  };

}();


console.log(messageCard.create('tanaka', 'hello!'));
/*
Dear tanaka
++++++++++
+ hello! +
++++++++++
*/

console.log(messageCard.getHistory(0));
/*
Dear tanaka
++++++++++
+ hello! +
++++++++++
*/

いくつかfunctionが宣言されていますが、最終的に一番下でreturnしているオブジェクトが外部に公開する部分となり、それ以外は内部的に扱われるprivateな部分となります。JavaScriptにはpublicやprivateなどの修飾子はありませんが、この手法をとれば実現ができます。

JavaScriptでprivateを実現するのは不自然という意見もありますが、内部的な参照のみを意図しているものを不用意に外部にさらすのはカプセル化の観点からどうなのと思う所があります。なので明瞭にpublicとprivateを分けられるRevealing Module Patternは素晴らしい実装パターンだと思いました、が・・・

Revealing Module Patternで複数のオブジェクトをつくる

しかし、上記の実装では複数のオブジェクトをつくることができません。messageCardが即時関数の返却値となっているためです。シングルトンなオブジェクトの生成方法としては全く問題ないのですが、messageCardを何個も用意したいケースではどうしたらいいのかなと思っていました。

調べたところ、その点に言及した記事を見つけました。 http://weblogs.asp.net/dwahlin/archive/2011/09/05/creating-multiple-javascript-objects-when-using-the-revealing-module-pattern.aspx

複数オブジェクト生成に対応したRevealing Module Pattern

var MessageCard = function(){

//省略

}; //即時関数として実行しない!

//異なるインスタンスのMessageCardが生成できる
var messageCard1 = MessageCard();
var messageCard2 = MessageCard();

単純に即時関数にしなければいい訳ですね!『JavaScriptデザインパターン』の内容を鵜呑みにしていて、これは盲点でした。

複数のオブジェクト生成時の問題点

ただし、これはこれで問題がないわけではありません。 messageCard1とmessageCard2は同じ構造を持つオブジェクトですが、MessageCard関数が呼び出される度にまるまる新規に生成されます。つまり内部で宣言している関数も複製されることになり、メモリの使い方としてはあまり嬉しくありません。 この点は上記の紹介記事にも触れており、筆者はその解決策としてRevealing Prototype Pattern を紹介しています。次回はこれを調べてみたいと思います。