JavaScriptの動的な性質を利用したDOM要素ファクトリ

JavaScriptは動的な言語である。例えば、与えられた文字列から、その文字列に対応するメソッドを簡単に呼び出せる。そこで、この性質を利用したDOM要素のファクトリを作ってみた。

var Factory = function(){};
Factory.prototype = {
    create: function(name){
	if(typeof(this["create" + name]) == "function"){
	    var args = [];
	    for(var i = 1; i < arguments.length; i++){
		args.push(arguments[i]);
	    }
	    return this["create" + name].apply(this, args);
	}else{
	    return this.createElement.apply(this, arguments);
	}
    },
    createElement: function(tagName, options){
	var element = document.createElement(tagName);
	options.className && (element.className = options.className);
	options.innerHTML && (element.innerHTML = options.innerHTML);
	options.parent && options.parent.appendChild(element);
	return element;
    },
    createA: function(options){
	var element = this.createElement("A", options);
	options.href && (element.href = options.href);
	return element;
    },
    createBorderDIV: function(options){
	var element = this.createElement("DIV", options);
	element.style.border = "solid 1px";
	return element;
    }
}

呼び出し方は、こんな感じ。

var factory = new Factory;
var gd = factory.create("BorderDIV", 
    {parent: document.body, innerHTML: "border"});
var d = factory.create("DIV", 
    {parent: gd, innerHTML: "test"});
var a = factory.create("A", 
    {parent: d, innerHTML: "llamerada", 
     href: "http://d.hatena.ne.jp/llamerada"});

すると、こんなHTMLが作られる。

<div style="border: 1px solid ;">
  border
  <div>test<a href="http://d.hatena.ne.jp/llamerada">llamerada</a>
  </div>
</div>

Factoryクラスのcreateメソッドの基本機能は、tagNameとoptionsを引数にとってtagName要素を生成することである。ただし、createBorderDIVやcreateAなど、tagNameに該当するメソッドが存在する場合は、それらのメソッドが呼び出される。tagNameに該当するcreateメソッドがないときは、デフォルトのメソッドであるcreateElementが呼び出される。
このように記述することの利点は、Factoryクラスのクライアントが利用するメソッドをcreateメソッドに限定されることによる、保守性と柔軟性の向上である。
なお、この手法は、Rubyのmethod_missingの考え方に影響を受けている。method_missingは、メソッドを呼び出した際に、該当するメソッドがない場合に呼び出されるメソッドである。このFactoryクラスでは、method_missingは、該当するtagNameがない場合に呼び出されるcreateElementメソッドに相当する。
このアプローチを発展させて、tagNameにさらに複雑な処理を埋め込めこともできる。例えば、tagNameの文字列を解析して、tagNameがBorderを含むならば、枠で囲む処理を加えることにして、個別にcreateBorderDIVなどを定義しない方法も可能である。