# rubyにとってクラスとは特別な識別子ではなく、スコープの境界線を定めてそれをオブジェクトの形にしているものに過ぎない。 class A puts 'aiueo' # よって class の中にある処理は普通に走る def a; puts 'a'; end end # クラスはただのスコープの境界線なので、 # 「class A を再定義する」という形ではなく「A スコープを開いて追加する」という形になる。 # なので、a と b のメソッドを両方呼ぶことが出来る。 class A def b; puts 'b'; end end obj = A.new # a も b も両方よべる obj.a obj.b class A # 既存のメソッドの上書きすることが可能。しかし場合によっては既存のプログラムが動かなくなる。 # 組込みライブラリやフレームワークを迂闊にオープンクラスによって拡張したものを「モンキーパッチ」という # 上書きは継承と違って override にならない。 def a # 「override」ではなく「上書き」なので super で呼べない # super => error puts 'aa' end end A.new.a
aiueo a b aa
http://d.hatena.ne.jp/LukeSilvia/20090222/p1
# Object#instance_eval は全てのオブジェクトにあるが Module#class_eval はクラスオブジェクトにしかない。 obj = Object.new obj.instance_eval { def hoge; 'hoge'; end } puts obj.hoge #obj.class_eval {} clazz = Class.new # instance_eval の場合は clazz のクラスメソッドに追加して、 # class_eval の場合は clazz のインスタンスメソッド を追加する。 clazz.instance_eval { def foo; 'foo'; end } clazz.class_eval { def bar; 'bar'; end } puts clazz.foo puts clazz.new.bar # クラスオブジェクトに class_eval か instance_eval のどちらを使うか迷った場合 # self に変更を加えたい場合は instance_eval を # オープンクラス として使用した場合は class_eval を使用するといい。
hoge foo bar
class C @@a = 'aiueo' A = 30 end # class_eval を使ってクラス変数(@@)やクラス定数する場合 # 直接コードでアクセス使用することは出来ない C.class_eval do #p @@a #=> NameError #p A #=> NameError end # 文字列を渡せばアクセス出来る C.class_eval <<-EOS p @@a p A EOS
"aiueo" 30
class A attr_accessor :val def set_local # ローカル変数の val か self.val=() のどちらかを判別する術がない。 # こういう場合 ruby はローカル変数と認識する val = 10 end # ここで ローカル変数 val のスコープが切れて解放される。 def set_self # 明示的に self. を付けてやれば アクセサメソッドだと判別することができる。 self.val = 20 end end a = A.new a.set_local puts a.val a.set_self puts a.val
nil 20
必要最小限のメソッドしかないクラスのこと
ruby 1.9 では BasicObject があるが、1.8では自分で作る
ruby 1.9 では BasicObject があるが、1.8では自分で作る
# Module.undef_method(メソッド名) # メソッドを消す。 class C # __send__, __id__, method_missing, respond_to? 以外のメソッドを消す。 instance_methods.each do |m| undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/ end def method_missing(name, *args) puts "call #{name}" end end obj = C.new # method_missing で class などの Object のメソッドも呼べるようになる。 obj.class
call class
# ruby の変数のスコープは def, class, module 単位で切り変わる。 # java のように「外部スコープ」「内部スコープ」という機能はなくスコープが切りかわると前(外や内)のスコープにはアクセスできない。 # 変数のスコープを越えたい場合 define_method, Class.new, Module.new を使う。 local = 0 self.class.send :define_method, :show do puts "local = #{local}" end show C = Class.new do local += 1 define_method :add do local += 1 show end end show obj = C.new obj.add obj.add puts local
local = 0 local = 1 local = 2 local = 3 3
# Rubyでは特定のオブジェクトにメソッドを追加することが出来る。 # このようなメソッドを「特異メソッド」と言う。 str = 'aiueo' # オブジェクト.メソッド名 の形でそのオブジェクトだけが使用できるメソッドが定義できる。 def str.title? self.upcase == self end puts str.title? # str のオブジェクトには title? メソッドは追加されている。 puts str.methods.grep(/title?/) # オブジェクトの特異メソッドを取得する場合は Object#singleton_methods を使用する。 puts str.singleton_methods # title? は str のオブジェクトのみに追加されたメソッドなので他の String では使用できない。 # "AIUEO".title? # 厳密な型よりも walk()(歩く) や quack()(鳴く) に反応さえ返せば型がどれかは問題ない。 # このような流動的な型のことを「ダックタイピング」を呼ぶ。 # 「アヒルのように歩いて、アヒルのように鳴けば、それはアヒルに違いない」という話が由来。 # Ruby にとって「型」はオブジェクトが反応するメソッドの集合に過ぎない。 # ちなみに実はクラスメソッドの正体はクラスオブジェクトの特異メソッド class C # オブジェクト.メソッド名 という特異メソッドの定義をとっているのが分かる。 def self.foo; 'foo'; end def self.bar; 'bar'; end end puts C.singleton_methods
false title? title? bar foo
# obj に特異メソッド hoge を定義したとする。 # この hoge はどこに住んでいるのだろうか? obj = Object.new def obj.hoge; 'hoge'; end p obj.hoge # obj はクラスではないので住むことはできない # hoge は obj 専用なので Object にも住むことが出来ない p Object.instance_methods.grep(/hoge/) # hoge なんてメソッドないよ! # 特異メソッドが住んでいる場所を特異クラスという # 特異クラスは全てのオブジェクトが一つだけもっている(だから特異クラスはシングルトンクラスとも呼ばれる) # 通常だと特異クラスは隠れているが # class << obj の後に self を戻すことによって取得することが出来る eigenclass = class << obj self end p eigenclass.class p eigenclass.instance_methods.grep(/hoge/) # 特異クラスは継承出来ない # class A < eigenclass; end
"hoge" [] Class ["hoge"]
module M1 # モジュールのクラス(特異)メソッドを定義 def self.say; puts 'M1'; end end class C1 # include したのでクラスメソッドもミックスインして欲しい顔になる include M1 end # が!!!だめ!! # C1.say #=> NoMethodError # クラスがモジュールをインクルードすると、モジュールのインスタンスメソッドが手に入る # しかしクラスメソッド(モジュールの特異メソッド)は手に入らない # クラスメソッドを拡張したい場合はモジュールに通常のインスタンスメソッドを定義して # 特異クラスのなかでモジュールをインクルードすればいい # これを「クラス拡張」という module M2 def say; puts 'M2'; end end class C2 class << self include M2 end end C2.say # 普通のオブジェクトでも特異クラスのなかで include すれば同じように拡張出来る # これを「オブジェクト拡張」という obj2 = Object.new class << obj2 include M2 end obj2.say p obj2.singleton_methods # クラス拡張やオブジェクト拡張をする Object#extend メソッドが用意されていて # やっていることは上記の特異クラスのスコープを開いて include をするという作業と一緒である。 module M3; def say; puts 'M3'; end; end obj3 = Object.new obj3.extend M3 obj3.say class C3 extend M3 end C3.say
M2 M2 ["say"] M3 M3
# inherited: クラスを継承した場合呼び出されるメソッド class A def self.inherited(subclass) puts "#{subclass} extends #{self}" end end class B < A; end # include: モジュールをミックスインした場合呼び出されるメソッド module M def self.included(othermod) puts "#{othermod} include #{M}" end end class C include M end # method_added: 新しいメソッドを追加した場合呼び出されるメソッド # method_removed: メソッドを削除した場合呼び出されるメソッド # method_undefined メソッドを未定義にした場合呼び出されるメソッド # # TODO: method_removed と method_undefined の違いを調べる。 module M2 def self.method_added(method) puts "new method: #{method}" end def self.method_removed(method) puts "removed method: #{method}" end def self.method_undefined(method) puts "undefined method: #{method}" end def hoge; end remove_method :hoge def moke; end undef_method :moke end # 上記のメソッドの先頭に singleton_ を付けると特異メソッドに対しての追加、削除、未定義になる。 class C2 def singleton_method_added(method) puts "new singleton_method: #{method}" end def singleton_method_removed(method) puts "removed singleton_method: #{method}" end def singleton_method_undefined(method) puts "undefined singleton_method: #{method}" end end obj = C2.new def obj.hoge; end def obj.moke; end class << obj remove_method :hoge undef_method :moke end
B extends A C include M new method: hoge removed method: hoge new method: moke undefined method: moke new singleton_method: hoge new singleton_method: moke removed singleton_method: hoge undefined singleton_method: moke
# クラスメソッドの拡張を全てモジュール内で済ませる場合 # フックメソッドの included が呼ばれた時に extend する。 class Base def self.before_filter name; puts "before_filter: #{name}"; end end module M def self.included(base) base.class_eval do extend ClassMethods # include 先のクラスメソッドを呼びたい場合も # included時にコールする。 before_filter :check end end module ClassMethods def after_filter name; puts "after_filter: #{name}"; end end end class C < Base include M after_filter :show end
before_filter: check after_filter: show
最新コメント