1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

Rubyのシングルトンメソッドとシングルトンクラス

2022-01-08 10:46:32

シングルトン・メソッド

Rubyでは、1つのオブジェクトにメソッドを追加することができ、このような1つのオブジェクトに対してのみ動作するメソッドをシングルピースメソッドと呼びます

コード例

str = "just a regular string"

def str.title?
  self.upcase == self
end

str.title? # => false
str.methods.grep(/title?/) # => [:title?]
str.singleton_methods # => [:title?]

str.class # => String
String.title? #=> NoMethodError



また、上記で使用した define メソッドに加えて、Object#define_singleton_method メソッドでシングルトン・メソッドを定義することができます。

シングルトン・メソッドとクラス・メソッドの比較

前のノートで述べたように、Rubyではクラスもオブジェクトであり、クラス名は単なる定数なので、クラス上のメソッドを呼び出すことは、オブジェクト上のメソッドを呼び出すことと実質的に同じになります。

クラスメソッドの本質は、クラスのシングルトン・メソッドであることで、実際、シングルトン・メソッドの定義とクラスメソッドの定義を比較すると、同じものであることがわかります

def obj.a_singleton_method; end
def MyClass.another_class_method; end


どちらも定義を表すdefキーワードを使用

def object.method
  #method body
end


上のオブジェクトは、*オブジェクトへの参照、定数クラス名、またはselfである。

クラスマクロ attr_accessor

Rubyのオブジェクトは属性を持たないので、属性のようなものが欲しい場合は、最も直接的にこのようにreadメソッドとwriteメソッド(つまりjavaでいうところのobjcのsetとgetメソッド)をそれぞれ定義する必要があるのです。

コード例

class MyClass
  def my_attribute=(value)
    @my_attribute =value  
  end
  def my_attribute
    @my_attribute
  end
end

obj = MyClass.new
obj.my_attribute = 'x'
obj.my_attribute #=> 'x'



しかし、上記のような書き方では、属性が多い場合、Repeat Yourselfの場所があり、そこで、以下の3つのクラスマクロが使えるようになります。

  • Module#attr_reader は読み込みメソッドを生成します。
  • モジュール#attr_writerで書き込みメソッドを生成する
  • モジュール#attr_accessorは、読み出しと書き込みの両方のメソッドを生成します。

サンプルコード

class MyClass
  attr_accessor :my_attribue
end


この方がずっとすっきりしていると思いませんか?もちろん、使い方(読み込みと書き込み)は上の実装と同じです。

シングルトン・クラス

Rubyではオブジェクトのメソッドを見つける順番は「右から上」と決まっています。つまり、まずオブジェクトのクラスを右から見つけ、そのクラスのインスタンスメソッドから見つけようとし、見つからなければ祖先の鎖を下っていくことになるのです。

前回は、特定のオブジェクトに対してのみ有効なメソッドであるシングルトンメソッドについて紹介しました。

class MyClass
  def my_method; end
end

obj = MyClass.new

def obj.my_singleton_method; end



まず、シングルトンメソッドはobjにはありません。objはクラスではないので、次にMyClassにはありません。そうすると、すべてのMyClassはこのメソッドを共有で呼び出せるはずで、シングルトンクラスとは言えなくなります。同じ意味で、シングルトン・メソッドは祖先連鎖の場所(superclass: Objectのような)にはありえません。正しい場所はシングルトン・クラスの中であり、それは実はirbでオブジェクトにクラスを尋ねたときに得られるクラス(obj.class)であるが、このクラスは通常のクラスとは少し異なっていることを除けば、である。メタクラスや固有クラスと呼ぶこともできる。

シングルトン・クラスを開く

Ruby では、単一のクラスへの参照を得るために 2 つの方法が用意されています。

メソッド1

class << an_object
  # Own code
end

obj = Object.new
singleton_class = class << obj
  self
end
singleton_class.class # => Class



シングルトン・クラスへの参照を取得するもう一つの方法は、Object#singleton_class メソッドを使用することです。

メソッド2

"abc".singleton_class # => #<Class: #<String:0xxxxxx>>



シングルピース・クラスの特徴

  • 各シングルトンクラスは1つのインスタンスしか持たず(シングルトンクラスと呼ばれる理由)、継承することができない
  • シングルピース・クラスは、オブジェクトのシングルピース・メソッドが存在する場所である
  • シングルトン・クラス導入後のメソッド検索

上記のシングルトン・クラスの基本的な理解に基づいて、シングルトン・クラスを導入した後、Ruby のメソッド検索はそのクラス(通常のクラス)からではなく、オブジェクトのシングルトン・クラスから始めるべきで、目的のメソッドがシングルトン・クラスに見つからなければ、クラス(通常のクラス)に沿って探し始め、さらに祖先の鎖を上がっていくことになるのです。このように、シングルトン・クラスの後に開始すると、すべてがシングルトン・クラスが導入されていないときの順序に戻る。

以下のコードで、ご自身で確認することができます。

class C
  def a_method
    'C#a_method()'
  end
end

class D < C; end

obj = D.new



シングルトンクラスを開き、シングルトンメソッドを定義する

class << obj
  def a_singleton_method
    'obj#a_singleton_method()'
  end
end

obj.singleton_class.superclass #=> D