Hatena::Groupimplementationpatterns

sumim が Implementation Patterns と SBPP の比較を通じて Ruby と Java を学ぶ日記

 | 

2007-12-02

● Getting Method, Setting Method

13:49 | はてなブックマーク - ● Getting Method, Setting Method - sumim が Implementation Patterns と SBPP の比較を通じて Ruby と Java  を学ぶ日記 ● Getting Method, Setting Method - sumim が Implementation Patterns と SBPP の比較を通じて Ruby と Java  を学ぶ日記 のブックマークコメント


見出しは共通ですが、主張は逆。Java [IPB95,96] では get、set をフィールド名に冠した getVarName()、setVarName(newValue) を推奨している一方で、Smalltalk [SBPP103,105] ではそうではなく、余計な my など(get、set とも読み替え可?)を付けずに単純に変数名をそのまま用いた instVarName、instVarName: を推奨しています。

なお、Smalltalk ではコロンを含めてメソッド名なので最後の : を省略してはいけないので注意してください。つまり、instVarName と instVarName: は一見、引数の有無を違えたオーバーロードに見えますが、実際のところ別のメソッドとして名前レベルで区別されます。Java では引数の有無も含めたシグネチャの違いによるオーバーロードが可能なので、おそらく Beck は当初、varName()、varName(newValue) というのを試したのでしょう。でもすぐに Java 従来の慣習に従う getVarName()、setVarName(newValue) に趣旨替えしたと、そう読めます。


あと、Smalltalk でも private なメソッドであることを主張するときに Setting Method に set を冠することがあります(get のほうはないです)。Smalltalk にはアクセスコントロール機構はないのですが、プロトコルというメソッドのカテゴライズ機構や set のような命名でユーザーにその旨を伝える慣習があるのです。



これらをうけて Ruby では、Setting Method のメソッド名の末尾に = (Smalltalk のコロンに対応)をメソッド名に含めることができるアナロジーから、SBPP での主張が対応するように思われます。


Ruby
def x
  @x
end
def x=(new_x)
  @x=new_x
end
Smalltalk
x
  ^x
x: newX
   x := newX

なにより attr_accessor はこれらアクセッサの定義を自動化してくれます。これを利用しない手はないでしょう。

class Hoge
  attr_accessor :x
end

hoge = Hoge.new
hoge.x = 4
p hoge.x   #=> 4

Geeting Method 、Setting Method は、Indirect Access[IPB47,SBPP101] と密接な関わりがあります。Beck も指摘するように、Indirect Access は柔軟性と、読みやすさやその他諸々の手間とのトレードオフです。


Ruby
# Direct Access
@x
# Indirect Access
x
Smalltalk
"Direct Access"
x
"Indirect Access"
self x

なお、メソッドコール時に Smalltalk のような self、Java のような () を省略することができる Ruby では、一見、Indirect Access がかかえる問題はすこし軽減しているように感じられます。しかし、Ruby には一時変数が同名の Getting Method を遮蔽してしまうという“落とし穴”があるので、新たにひとつ余計な注意が必要になってしまう、と考えた方がよいかも。

def x; @x end
p x   #=> nil
x = 4
p x   #=> 4
p @x   #=> nil

self や () の省略をやめればこの問題は回避できます。

def x; @x end
p x   #=> nil
x = 4
p x   #=> 4
p self.x   #=> nil
p x()   #=> nil
 |