2012-03-04
ruby の OOP をひと通り見ておこうかな、とか。
ruby |
タイムラインに名言が。
私もRubyでモテる女子を目指します。RT @endoyuma: Rubyをやるとモテるよ!逆にRubyをやっててもモテないエンジニアはもうダメだよ!って言われたから俺はまだ成長の余地を残している
... はい。ruby にもクラス書いたり何だりと OOP のための仕組みがありますので、それらをざっと見ていました。
1. クラス名は大文字から始まる。
class Point; end # ok class point; end # ng -> class/module name must be CONSTANT
Point という名前のクラスは後から定義し直すことはできません。「ある名前があって、それが何であるかを上書き変更できない」という性質は定数と同じですね。ruby の定数には識別子を大文字で始めるというルールがあるのですがそのコンセプトはクラス名(やモジュール名)においても同じ、という理解で良さそうです。
2. メンバは外部公開しない。
class Point def initialize(x=0,y=0) @x = x @y = y end end p = Point.new(100,200) puts p.x # ng -> undefined method `x'
エラーメッセージを読めば分かりますが、これはメンバ変数が private 指定されているからエラーになっている、というわけではありません。x というメソッドが無いからエラーになっています。↓このようにアクセサメソッドを定義すれば良いです。
class Point def initialize(x=0,y=0) @x = x @y = y end def x=(x) # setter @x = x end def x() # getter @x end end p = Point.new(100,200) puts p.x # ok -> 100
メソッドのアクセス修飾はデフォルトで public なのでこれで動きます。
アクセサメソッドの内容を自分で書きたい場合というのは少ないうえ、アクセサがアクセサ以外のメソッドと混ざって並ぶのは見にくいので、↓略記する機能が用意されています。
class Point def initialize(x=0,y=0) @x = x @y = y end attr_accessor :x, :y end p = Point.new(100,200) p.x = 150 puts p.x
attr_accessor 以外に getter だけを定義する attr_reader, setter だけを定義する attr_writer というのもあるようです。
ここで注目すべきは 「:x, :y」という風にシンボル記法が使われていることです。「アクセサメソッドがシンボル使って定義されるなら、アクセサ以外のメソッドもシンボルで良くね?」とギモンに思ったのですが、調べてみるとなるほど...。そもそも attr_accessor というのは ruby の文法ではなく、「シンボルを受け取ってメソッドを定義する」関数だそうです。
この仕組については、↓ここの説明が短くてわかりやすかったですね。
『まつもと直伝 プログラミングのオキテ 第6回』IT Pro (注: 2007/06/11 の記事)
継承は「<」を使って書きます。
class Point def initialize(x=0,y=0) @x = x @y = y end attr_accessor :x, :y end class Circle < Point def initialize(r=1) @r = r end end r = Circle.new r.x = 100 puts r.x
↑このとき、Point.initialize の引数 x, y はそれぞれデフォルト指定したものが渡されます。もし仮にデフォルト引数が指定されていない場合は↓こう書きます。
class Point def initialize(x,y) # デフォルト引数なし @x = x @y = y end attr_accessor :x, :y end class Circle < Point def initialize(r=1) super(100,100) # 明示的な呼び出し @r = r end end r = Circle.new puts r.x
↑ super は基底クラスの同名メソッドを呼び出すための構文です。このとき、もし super(100,100) が無いと Point.initialize が呼ばれないうえ、それだけでは実行時エラーになりません。また super は何度でも使えるので Circle.initialize の中で Point.initialize を何度も呼ぶこともできちゃいます。
特にオーバーライド用のキーワードを使う必要はなく、同名のメソッドを定義すれば OK です。インスタンスの状態を文字列にする to_s メソッドをオーバーライドしてみました↓。
class Point def initialize(x=0,y=0) @x = x @y = y end def to_s(use_brackets) if use_brackets then "(#{@x},#{@y})" else "#{@x},#{@y}" end end attr_accessor :x, :y end class Circle < Point def initialize(r=1) super(100,100) @r = r end def to_s '(' + super(false) + ",#{@r})" end end p = Point.new puts p.to_s(true) # ok -> (0,0) r = Circle.new puts r.to_s # ok -> (100,100,1)
素直な挙動ですね。オーバーライドは名前のみによって行われるみたいですね。例えば↓こうするとエラーになります。
r = Circle.new puts r.to_s(true) # ng -> wrong number of arguments (1 for 0) (ArgumentError)
従って、オーバーライドしつつデフォルト引数を継承することも不可です。
class Point def initialize(x=0,y=0) @x = x @y = y end attr_accessor :x, :y private :x end class Circle < Point def initialize(r=1) @r = r end public :x end p = Point.new puts p.x # ng -> private method `x' called r = Circle.new puts r.x # ok
↑このように、基底クラスで private 指定したメソッドを派生クラスで public にすることもできます。(Java ではこれは不可ですね。逆に基底クラスで public であるメソッドを派生クラスで private にすることは許されていますけど。)
6. ギモン
ちゃんとした資料を当たらずにやってるのでいくつか分からないことが。時間があるときに確認するつもり。(本稿で言及が無いことへの言い訳)
a.クラスメソッドのアクセス修飾を private, protected に変更できない? b.オーバーライドしたメソッド foo(), bar() があるとき、派生クラスの bar() から基底クラスの foo() を呼びだすことはできない? c.クラス A, B, C があり A < B < C という継承関係になっていて、それぞれでメソッド foo() が定義されているとする。このとき A.foo から C.foo を直接呼び出すことはできない? d.抽象メソッドという概念は無い?
7. 雑感
別に Java や C++ と比較して重箱の隅をつつくつもりは無いんですけども、僕が使っている他の言語と比較して見てゆくと小さい差異が見つかって面白いですね。ruby とか Python のコードって何故か簡潔に見えるのですが、その理由のいくらかは言語仕様にあるなーというのをありありと感じます。
- 30 http://t.co/tXq7JB48
- 23 http://www.kt.rim.or.jp/~kbk/zakkicho/
- 17 http://www.kt.rim.or.jp/~kbk/zakkicho/index.html
- 14 http://t.co/7iG1BjZX
- 8 http://reader.livedoor.com/reader/
- 7 http://t.co/UFrWAoLI
- 7 http://www.google.co.jp/url?sa=t&rct=j&q=関数型言語&source=web&cd=3&ved=0CEsQFjAC&url=http://d.hatena.ne.jp/kura-replace/20111114/1321236695&ei=TPlST8eBMsOYiAf14K22CA&usg=AFQjCNHACAPVWdYVICt73_Ap5d25rhnMeg
- 5 http://longurl.org
- 4 http://hootsuite.com/dashboard
- 4 http://t.co/sUbOdxPM

オープンクラス は?
なんて言うか、"上書きして全く別のものに書き潰す。new 済インスタンスの型も変えてしまう" ぐらいの意味で読んでくだされ。