LoginSignup
1
0

More than 5 years have passed since last update.

メタプログラミングruby 5章

Last updated at Posted at 2018-07-07

注 classは高性能なmoduleなので、classにおいてできることはmoduleにも適用できる場合が多い

classとmoduleの比較

classはインスタンスをもつ
moduleはメソッド群

メソッドを定義するとself(カレントクラス)のインスタンスメソッドになる
クラスの定義の中ではカレントクラスとカレントobject(self)は同じである
クラスへの参照を持っていれば、クラスはclass_evalで参照できる

クラスインスタンス変数

インスタンス変数の定義する場所によって、そのインスタンス変数をもつclassが異なることに気をつけなくてはいけない
メソッドの中ではメソッドのレシーバがselfになる、よって、インスタンスメソッドの中ではそのインスタンスがselfに、classの定義の中ではclass自身がselfになる。(あらゆるクラスはclassクラスのインスタンス クラスの定義内ではselfはclassクラスのインスタンスである、そのクラス自身を参照する)
classクラスのsuperclassはmoduleでmoduleのsuperclassはobjectとなる。だが、objectはclassクラスのインスタンスである。
よってあらゆるクラスはclassクラスのインスタンスである。また、新しく生成されたclassのsuperclassはobjectだが、これもクラスはclassである。また、class自身も例外ではなく、classクラスはclassクラスのインスタンスである。
ただ、ここで注意しなくてはならないのは継承しているということとそのクラスのインスタンスであるということは違うということ。継承はメソッドの引き継ぎ、インスタンスは継承されてきたものによって作られたクラスを用いた物体。

meta.rb
class Myclass
  @my_var = 1
end

クラス定義の中ではselfはクラス自身になるので、@my_varはクラスに属していることになる。
よって、クラスのインスタンス変数とクラスのオブジェクトのインスタンス変数は別物になることを注意しなくてはならない

meta.rb
class Myclass
  @my_var = 1
  def self.read; @my_var; end
  def write; @my_var = 2; end
  def read; @my_var; end
end
obj = Myclass.new
obj.read #=> nil
obj.write
obj.read #=> 2
Myclass.read #=> 1

Myclassのインスタンスメソッドはwriteとreadで、objのメソッドでもある。

特異メソッド

rubyでは特定のobjectに対してメソッドを追加できる。
例えば、

a = User.find(3)
def a.black?
  true if self.name == "ウエハラ"
end

的な
これはclassメソッドも実は特異メソッドで、classクラスの特定のobjectに対してメソッドを追加していることになるから。
特異メソッドが出た場合、メソッドの継承チェーンに特異クラスが挟まれる。つまり、そのobjectは特異クラスのインスタンスになる。特異クラスのインスタンスは通常のclassを継承しているので、それまで通りに使うことができる。つまり、moduleを挟み込むのと同じイメージ。

ダックタイピング

静的型付け言語ではそのobjectがTの型を持つのはそのTクラスに属しているからだと考える。
動的型付け言語ではそのobjectがDuckクラスに属しているかどうかは重要ではない、そのメソッドが反応するかどうかである。
rubyではインスタンスのクラスがどのような継承関係をもっていても、ある特定のメソッドに応答できさえすればレシーバーとして振る舞うような処理がかける。

メソッドラッパー

メソッドの書き換えに3つの方法がある。

 アラウンドエイリアス

meta.rb
class String
  alias_method :real_length, :length

  def length
    real_length > 5 ? 'long' : 'short'
  end
end
"War and Peace".length # => "long" ①
"War and Peace".real_length # => 13 ②

①の場合:まず、新しく定義されているlengthに入る。その中で、real_lengthが呼ばれ、aliasにしたがって、もともとのlengthが呼ばれ、13が返ってくる。13に対し評価を行い"long"という結果になる。
②の場合:real_lengthを呼ぶとaliasにしたがってlengthが返ってくる。
このようにlengthを再定義することが可能である。
他にもmodule内でsuperを使って再定義したメソッドを使うという手もある。

meta.rb
module ExplicitString
  def length
    super > 5 ? 'long' : 'short'
  end
end

String.class_eval do
  prepend ExplicitString
end

"War and Peace".length # => "long" 
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0