日記帳

日記です。

アクセサメソッド定義メソッド(2)

attr() *1 でアクセサメソッドを定義してもプロパティを直接読み書きできてしまします.

Object.prototype.attr_reader = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var readerName = "get" + capitalizedName;
	this[readerName] = new Function('return this["' + name + '"]')
}

Object.prototype.attr_writer = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var writerName = "set" + capitalizedName;
	this[writerName] = new Function('newValue', 'this["' + name + '"] = newValue')
}

var obj = new Object();
obj.attr("message");
obj.setMessage("Hello, World")
alert(obj.getMessage()) // => "Hello, World"
alert(obj.message)      // => "Hello, World"
obj.message = "Die!"
alert(obj.getMessage()) // => "Die!"

http://d.hatena.ne.jp/sa-y/20060314#1142350837
で書いたようにプロパティを完全に private にすることはできないのですが,それにしても上の例のように書き換えが簡単すぎるのも問題です.

もちろんプログラムの書き手と読み手が同じ場合には「このプロパティは必ずアクセサメソッドを使うようにしよう」と心に誓っておけば問題にはなりません.しかし,書き手と読み手が異る場合*2には「このプロパティはアクセサメソッドを使わないとアカンのよ」という書き手の意思が読み手に伝わらない可能性があります.

ってことでプロパティを直接アクセスし難くしてみる試み.

obj.message = "Die!"

というお手軽な記法でアクセスできるからアクセスしたくなるのであって,直接アクセスするのがアクセサメソッド呼ぶのより面倒だったらみんなアクセサメソッド呼ぶのではないかしら?

例えば以下のようにプロパティ名にスペースを含むように変更(attr_readerも同様に変更)してみると上の記法ではアクセスできなくなります.

Object.prototype.attr_writer = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var writerName = "set" + capitalizedName;
	this[writerName] = new Function('newValue', 'this[" ' + name + ' "] = newValue')
}

こうすると以下のように書かないとアクセスできません.

obj[" message "] = "Die!"

ちょっと面倒ですね.

読み手に書き換えて欲しくないという意思を伝えるということを考えるともっと直接的な方法もありそうです.

Object.prototype.attr_writer = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var writerName = "set" + capitalizedName;
	this[writerName] = new Function('newValue', 'this["Dont write this property! : ' + name + ' "] = newValue')
}

これだと直接アクセスするには以下のように書かないといけません.

obj["Dont write this property! : message "] = "Die!"

このコードをコピーせずに手で書いたら2個所スペルミスしてました.なかなか効果的かもしれません♪

あるいは直接アクセスするような人を貶めてみるのもいいかもしれません.

Object.prototype.attr_writer = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var writerName = "set" + capitalizedName;
	this[writerName] = new Function('newValue', 'this["この ' + name + ' というプロパティに直接アクセスしている私は大馬鹿だ!"] = newValue')
}

もう完全に嫌がらせです.

obj["この message というプロパティに直接アクセスしている私は大馬鹿だ!"] = "Die!"

でもこのプロパティ名はメソッドをオーバーライドする人も書くことになるかもしれないのであんまりアレなのも困りそうですね…

書き手の「このプロパティは private 扱いにしてね♪」という意思が読み手に伝わることが大事なのだと思うので,とりあえずこんな感じにしておきました.

Object.prototype.attr_reader = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var readerName = "get" + capitalizedName;
	this[readerName] = new Function('return this["private ' + name + '"]')
}

Object.prototype.attr_writer = function(name)
{
	var capitalizedName = name.charAt(0).toUpperCase() + name.substring(1)
	var writerName = "set" + capitalizedName;
	this[writerName] = new Function('newValue', 'this["private ' + name + '"] = newValue')
}

このくらいならオーバーライドもそれほどやり難くないでしょう.たぶん…

var obj = new Object();
obj.attr("message");
obj.setMessage("Hello, World")
alert(obj.getMessage()) // => "Hello, World"
alert(obj.message)      // => undefined
obj.message = "Die!"
alert(obj.getMessage()) // => "Hello, World"

// override
obj.setMessage = function(newMessage){
	this["private message"] = newMessage.toString().toUpperCase()
}
obj.setMessage("HELLO, WORLD")
alert(obj.getMessage()) // => "HELLO, WORLD"

*1:http://d.hatena.ne.jp/sa-y/20060311#1142095415

*2:書き手が自分,読み手が未来の自分という場合も含みます