まめージェント

Android, GAE, AngularJSの技術ネタ中心。Twitter: @mame01112

Javascriptでデザインパターン (その7: Chain of Responsibility)

8回目の今回は、Chain of responsibilityパターン。

日本語に訳すると”責任のたらい回し”。訳すると、響きはかなり悪いです(笑)

Chain of Responsibilityとは?

Wikipediaによると、"一つの コマンドオブジェクトと一連の 処理オブジェクトから構成される。各処理オブジェクトは、処理できるコマンドオブジェクトの種類と、自身が処理できないコマンドオブジェクトをチェーン内の次の処理オブジェクトに渡す方法を記述する情報を保持する。また、新たな処理オブジェクトをチェーンの最後に追加する機構を備える。"とのこと。

僕の言葉で説明すると、
・ソフトウェアを作る上でいろいろな条件分岐をすることがあるけど、あまりにその条件が増えると見にくくなるよね?
・ので、それをオブジェクトのチェーンで表現するようにして、使う側がその分岐を意識する必要がなくなるようにするよ
ですかね。

下記のように、入力された文字のバリデーションに使うこともできるかとは思います。が、最近のJavaScriptのライブラリはそのあたりも充実していたりするので、あまりがんばっても車輪の再発明かなぁと思ったり思わなかったり。。。

今回のサンプルコード

今回はユーザ入力する文字列に対して、Null / 最小の文字数 / 最大の文字数を確認するためのValidatorを作りたいと思います。このテのチェックはプロジェクト後半にも追加になったりしてその都度if - elseのネストが深くなるので、このChain of responsibilityを使えばそれを防ぐことができます。

まずは、最小、最大の文字数を定義します。これは普通ですね。

var MIN_LENGTH = 4;
var MAX_LENGTH =8;

続いて、確認するポイントごとにチェーンを作っていきます。まずはNullチェック。コンストラクタで定義しているthis.nextは、チェーンでつながる先です。setNext()メソッドの引数として渡され、各インスタンスがつながる先を持つことになります。

request()の引数には対象となる文字が渡され、nullの場合は、その旨を表示してチェーンを終了します。もしnullでない場合は、次のチェックに写るため、自分が持つthis.nextのrequest()を呼びます。

var NullCheckHandler = function(){
	this.next= null;
	console.log("NullCheckHandler constructor");
};

NullCheckHandler.prototype = {

	setNext : function(chain){
		console.log("NullCheckHandler setNext");
		this.next = chain;
		return this;
	},

	request : function(input){
		console.log("NullCheckHandler request");
		if(input === null){
			console.log("Input is null, End of chain");
		} else {
			if(this.next !== null){
				console.log("Go to next chain");
				this.next.request(input);
			} else {
				console.log("End of chain");
			}
		}
	}
};

ここからはほぼ繰り返し。最低の文字列以上かを確認しています。

var MinLengthHandler = function(){
	this.next= null;
	console.log("MinLengthHandler constructor");
};

MinLengthHandler.prototype = {

	setNext : function(chain){
		console.log("MinLengthHandler setNext");
		this.next = chain;
		return this;
	},

	request : function(input){
		console.log("MinLengthHandler request");
		if(input.length < MIN_LENGTH){
			console.log("Input is less than minimum length, End of chain");
		} else {
			if(this.next !== null){
				console.log("Go to next chain");
				this.next.request(input);
			} else {
				console.log("End of chain");
			}
		}
	}
};

最大文字数以下であることを確認。もし最大文字数以下(すべてのチェックが問題ない場合)は、その旨を表示して終了します。

var MaxLengthHandler = function(){
	this.next= null;
	console.log("MaxLengthHandler constructor");
};

MaxLengthHandler.prototype = {

	setNext : function(chain){
		console.log("MaxLengthHandler setNext");
		this.next = chain;
		return this;
	},

	request : function(input){
		console.log("MaxLengthHandler request");
		if(input.length > MAX_LENGTH){
			console.log("Input is more than minimum length, End of chain");
		} else {
			if(this.next !== null){
				console.log("Go to next chain");
				this.next.request(input);
			} else {
				console.log("End of chain");
			}
		}

	}
};

そして、上記のValidatorを確認するためのClientの定義です。

チェックする項目ごとにインスタンスを生成し、それをsetNextでつなげます。Javaであれば一続きで書くことができるのですが、Javascriptは同じプロトタイプベースで同じクラスを継承していないため、一つずつ別の行に書く必要があります。ここ、ちょっとカッコ悪い。

var Client = function(){
	var nullCheckHandler = new NullCheckHandler();
	var minLengthHandler = new MinLengthHandler();
	var maxLengthHandler = new MaxLengthHandler();
	nullCheckHandler.setNext(minLengthHandler);
	minLengthHandler.setNext(maxLengthHandler);

作ったチェーンを、今回確認したいユースケースごとに呼び出します。Nullと、最小文字数以下、最大文字数以上の3つのケースですね。ここは特に説明する部分はないかと思います。

	console.log(" --- Check null ---");
	var wordA = null;
	nullCheckHandler.request(wordA);

	console.log(" --- Check less than minimum lenght string ---");
	var wordB = "123";
	nullCheckHandler.request(wordB);

	console.log(" --- Check more than minimum lenght string ---");
	var wordC = "1234567890";
	nullCheckHandler.request(wordC);
};

最後にclientを呼び出します。

var client = new Client();

その結果、ログはこんな感じで表示されます!

NullCheckHandler constructor
MinLengthHandler constructor
MaxLengthHandler constructor
NullCheckHandler setNext
MinLengthHandler setNext
 --- Check null ---
NullCheckHandler request
Input is null, End of chain
 --- Check less than minimum lenght string ---
NullCheckHandler request
Go to next chain
MinLengthHandler request
Input is less than minimum length, End of chain
 --- Check more than minimum lenght string ---
NullCheckHandler request
Go to next chain
MinLengthHandler request
Go to next chain
MaxLengthHandler request
Input is more than minimum length, End of chain

Chain of Responsibilityは以上です!