読者です 読者をやめる 読者になる 読者になる

気軽に楽しくプログラムと遊ぶ

自分が興味があってためになるかもって思う情報を提供しています。

メタプログラミング Ruby 第4章 クラス定義

メタプログラミング本 Ruby Ruby資格取得

クラスが返却する値

クラスもメソッドのように最後に評価した値を返却する

class MyClass
  puts "Hello"
end

Hello

指定クラスにメソッドを追加

オープンしたいクラス名がわからない場合、classキーワードでは
メソッド追加できないため、class_evalを用いる

def add_method_to(class_name)
  class_name.class_eval do
    def inc(x)
      self + x
    end
  end
end

add_method_to Fixnum
1.inc(1) # => 2

instance_eval()とclass_eval()

instance_eval() :self(オブジェクト)を変更
class_eval() :self(オブジェクト)とカレントクラスを変更

クラスインスタンス変数

インスタンス変数は2種類ある。
クラスに紐づくものとオブジェクトに紐づくもの

class MyClass
  @v1 = 1
  # クラスメソッド。内部はクラススコープ
  def self.read
    @v1
  end
  
  # インスタンスメソッド。オブジェクトスコープ
  def write
    @v1 = 2
  end
  def read
    @v1
  end

end

MyClass.read # => 1 クラスインスタンス変数を参照
obj = MyClass.new
obj.write
obj.read # =>2 objのインスタンス変数を参照

クラス変数の落とし穴

下記のような現象が起こるため、クラス変数ではなく、
クラスインスタンス変数を使う人が多い。

#トップレベル定義するとObject属するクラス変数となる
@@v1 = 1

class MyClass
  #クラスすべてはObjectを継承し、クラス変数も継承(共有)される
  @@v1 = 2
end

# 結果、MyClass内でトップレベルのクラス変数を変更できてしまう。
@@v1 # => 2

特異メソッド

特定オブジェクトにのみから呼び出せるメソッドのこと

str1 = "hello"

def str1.hello?
  self == "hello"
end

str1.hello? # => true

クラスメソッドは特異メソッド
クラスメソッドはself(クラスオブジェクト)への特異メソッド定義らしい。

class Myclass
  # クラスオブジェクトにクラスメソッドを定義
  def Myclass.my_method1
  end

  # クラスオブジェクト(self)にクラスメソッドを定義
  def self.my_method2
  end
end

クラスマクロ
クラス定義内で使用可能なクラスメソッド
Module#attr_*()のメソッドが代表的。

特異クラス

特異メソッドはどのクラスに所属しているか。
特異メソッドの定義クラスにおけるインスタンスメソッドだと全インスタンスに引き継がれてしまう。
Objectクラスだと全クラスに引き継がれてしまう。ではどこに所属しているのか。
特異クラスという特殊なクラスに所属している。

特異クラスのオープン

class << an_object
 #処理を記述
end

特異クラスの参照を取得

eigenclass = class << obj
  self
end

eigenclass.class # => Class

selfを使わず、クラスに属性定義をする

class MyClass
  # これだとインスタンスの属性定義になってしまう
  attr_accessor :a
end

MyClass.a # => NoMethodError: undefined method `c' for MyClass:Class
mc = MyClass.new()
mc.a = 1
mc.a # => 1

特異クラスを用いてクラス属性を追加します。

class MyClass
  # 特異クラスに属性追加することでクラスに属性追加
  class << self
    # クラス属性の定義コンテキストで定義
    attr_accessor :c
  end
end

MyClass.c = '属性定義'
MyClass.c # => '属性定義'

クラス拡張

moduleのインスタンスメソッドをクラスメソッドとして定義させるとき
クラス拡張という方法で実現できる

module MyModule
  def my_method
    puts 1
  end
end

class MyClass
  class << self
    # 特異クラス内でインスタンスメソッドを拡張させるとクラスメソッドとして定義可能
    include MyModule
  end
end

MyClass.my_method # => 1

Object#extendを用いれば、もっとシンプルに定義可能

class MyClass
  extend MyModule
end

Myclass.my_method # => 1

エイリアス

aliasキーワードを用いて、Rubyメソッドへの別名をつけることができる。

class MyClass
  def my_method; "my_method()"; end
  # aliasはキーワードのため、二つのメソッド名の間にカンマ不要
  alias :my_method2 :my_method
end

m = MyClass.new()
m.my_method # =>  "my_method()"
m.my_method2 # =>  "my_method()"

aliasの用途
元のメソッドを修正したいが、外部ライブラリなどで修正できない場合がある。
その場合は、元メソッドを別名定義し、元メソッド名でメソッドを再定義する。
その際、元メソッドを呼びながら、追加処理を記述できる。これをアラウンドエイリアスと呼ぶ

class MyClass
  def my_method
    puts "original"
  end

  # my_methodを別名定義
  alias :org_my_method :my_method

  # my_methodを再定義
  def my_method
    org_my_method
    puts "Redefinition"
  end
end

mc = MyClass.new()
mc.my_method # => original Redefinition
mc.org_my_method # => original

次はコードを記述するコード
メタプログラミング Ruby 第5章 コードを記述するコード - 気軽に楽しくプログラムと遊ぶ