オブジェクト指向っぽくオセロを作る5

続きです。前回Othelloオブジェクトを作ると書いたのですが、やっぱりやめて、Viewオブジェクトに手を加えてみました。前回ちょろっと書きましたが、Viewオブジェクトは内部に_boardというプロパティを持っています。これを外部から隠したいという話がありました。クロージャのテクニックを使うことで、これを実現します。ただ、ソースが前のものに比べて少し見難くなったかもしれません。なお、処理的にはたいして変化はありませんので、この回を飛ばして、次の内容に進んでもらっても問題ありません。

こんな感じになりました

var View = {};
(function(){
	var board;
	/**
	 * Boardクラスから生成したboardオブジェクトをセットする
	 */
	function setBoard(_board){
		board = _board;
	}
	View.setBoard = setBoard; //外部からアクセスできるようにする
	/**
	 * boardの情報を実際に盤として描写するための処理
	 * 変更された箇所のみを再度描写しなおす
	 */	
	function paint() {
		if(!board)
			throw new Error("board is null/undefined. please call setBoard method before paint.");
		var board_element = document.getElementById("board");
		for ( var y = 0; y < 8; y++) {
		    for ( var x = 0; x < 8; x++) {
		    	/*
		    	 * boardオブジェクトが示す駒と
		    	 * DOM要素が示す盤の駒を比較
		    	 * 差異がある場合だけ描写を行う
		    	 */
		    	var nType; //boardオブジェクトの示す駒の種類
		    	switch ( board.getPiece(x, y) ) {
				case Piece.BLACK:
					nType="black";
					break;
				case Piece.WHITE:
					nType="white";
					break;
				case Piece.EMPTY:
					nType="empty";
					break;
				}
		    	//DOM要素の示す駒を取ってくる
		    	var id = "cell" + (x + 1  + y * 8); //idはcell1からcell64まで
		    	var bElement = document.getElementById(id);
		    	if(bElement){ //一番最初はDOM要素がないのでエラーではない
		    		var bType = bElement.className; //classは駒の種類に対応している
		    	}
		    	//同じなら描写しなおす必要がないので次の周に
		    	if(nType && bType == nType)
		    		continue;
		    	else{ //違うなら
		    		if(bElement) //なおかつ現在DOM要素があるなら
		    			board_element.removeChild(bElement); //DOM要素を取り除く
		    	}		    		
		    	//新たに追加するDOM要素を作る
		    	var nElement = makeElement(nType, id, x, y);
		    	if(nElement){
		    		board_element.appendChild(nElement);
		    	}
		    }
		}
	}
	View.paint = paint; //外部からアクセスできるようにする
	/**
	 * 指定された種類のセルのDOM要素を作成する
	 * typeはblack, white, emptyのいずれか
	 * なお、この関数はpaintメソッド内でのみ有効
	 */
	function makeElement(type,id,x,y){
		var element;
		if(type != "black" && type != "white" && type != "empty"){
			throw new Error("illegal argument 'type': "+type);
		}else{
	    	element = document.getElementById(type).cloneNode(true);
			if(!element)
				throw new Error("idが"+type+"のDOM要素をクローンできませんでした。");				
		}
		element.style.left = 32 * x + "px";
		element.style.top = 32 * y + "px";
		element.id = id;
		return element;
	}
})();

クロージャについての詳しい解説はここではしません。以前書いたクロージャのテクニックに関する記事をご覧下さい。ポイントは、以前this._boardとしていたものをvar boardとして、ローカル変数にしています。これによって外部から直接boardにアクセスできなくなりました。また、setBoardとpaintメソッドは外部から利用できるようにしなくてはいけないので、

View.setBoard = setBoard; //外部からアクセスできるようにする

View.paint = paint; //外部からアクセスできるようにする

という風に、ViewのsetBoardプロパティ、paintプロパティから参照しています。またmakeElementメソッドは外部からアクセスする必要がないので、上記のような処理が必要ありません。

次回は「オセロのルール」を表現するOthelloオブジェクトを作ります。