Ruby 的 Singleton 類
Ruby 對象模型有一個很特殊的存在,他就是單例類(Singleton Class),本章節(jié)中我們會對單例類做一個簡單的介紹。
1. 什么是單例類
讓我們從一個簡單的例子開始:
實例
class Person
def a_name
'Andrew'
end
end
person = Person.new
puts person.a_name
puts person.class.instance_methods.grep /name/
# ---- 輸出結(jié)果 ----
Andrew
a_name
這個是一個簡單的例子,在類的本質(zhì)中我們學(xué)習(xí)到了,如果要查找一個對象中的方法,要從他的類中查找,因為方法都是定義在類中的。
類其實也是對象,那么當(dāng)我們定義類方法的時候,同理,方法應(yīng)該存在于Class
類中的,但是答案卻是否定的。
實例:
class Person
def self.list
[ 'Andrew', 'Tom', 'Alice' ]
end
end
p Person.list
p Person.class.instance_methods.grep /list/
# ---- 輸出結(jié)果 ----
["Andrew", "Tom", "Alice"]
[]
那么這個類方法到底放在了哪里呢,答案就是存在于單例類中。
我們可以使用singleton_class
來獲取一個對象的單例類。
實例:
class Person
def self.list
[ 'Andrew', 'Tom', 'Alice' ]
end
end
p Person.list
p Person.singleton_class.instance_methods.grep /list/
# ---- 輸出結(jié)果 ----
["Andrew", "Tom", "Alice"]
[:list]
**解釋:**當(dāng)我們定義self.list
的時候,實際上是告訴Ruby打開self
的單例類,并在其定義list
方法。
同理可知,所有的對象他們的方法實際上都存在于單例類之中。
實例:
class Person
def a_name
'Andrew'
end
end
person = Person.new
puts person.a_name
puts person.singleton_class.instance_methods.grep /name/
# ---- 輸出結(jié)果 ----
Andrew
a_name
2. 單例類與類、對象之間的關(guān)系
現(xiàn)在讓我們定義一個Student
類和Person
類。
實例:
class Person
def a_name
'Andrew'
end
end
class Student < Person
end
student = Student.new
p student.a_name
# ---- 輸出結(jié)果 ----
'Andrew'
讓我們輸出它的祖先鏈:
p Student.ancestor
# ---- 輸出結(jié)果 ----
[Student, Person, Object, Kernel, BasicObject]
讓我們從頭輸出一下它祖先鏈類的部分:
p student.class
p student.class.superclass
p student.class.superclass.superclass
p student.class.superclass.superclass.superclass
# ---- 輸出結(jié)果 ----
Student
Person
Object
BasicObject
現(xiàn)在讓我們在中間輸出它的每一個對象的單例類:
p student.singleton_class
p student.class
p student.class.singleton_class
p student.class.superclass
p student.class.superclass.singleton_class
p student.class.superclass.superclass
p student.class.superclass.superclass.singleton_class
p student.class.superclass.superclass.superclass
p student.class.superclass.superclass.superclass.singleton_class
# ---- 輸出結(jié)果 ----
#<Class:#<Student:0x007f82f4170060>>
Student
#<Class:Student>
Person
#<Class:Person>
Object
#<Class:Object>
BasicObject
#<Class:BasicObject>
Tips:單例類輸出前面會有一個
#
,比如:通常我們說Person
的單例類是#Person
。
實例對象的單例類的超類是實例對象的類。
p student.class
p student.singleton_class.superclass
# ---- 輸出結(jié)果 ----
Student
Student
類的單例類的超類等于類的超類的單例類。
p student.class.singleton_class.superclass
p student.class.singleton_class.superclass.superclass
p student.class.singleton_class.superclass.superclass.superclass
p student.class.superclass.singleton_class
p student.class.superclass.superclass.singleton_class
p student.class.superclass.superclass.superclass.singleton_class
# ---- 輸出結(jié)果 ----
#<Class:Person>
#<Class:Object>
#<Class:BasicObject>
#<Class:Person>
#<Class:Object>
#<Class:BasicObject>
單例類同樣也是Class
類的對象。
p student.singleton_class.class
p student.class.singleton_class.class
# ---- 輸出結(jié)果 ----
Class
Class
3. 打開單例類
我們可以通過class <<
來打開單例類并且在其中定義方法。
實例:
普通實例對象:
class Person
end
person = Person.new
class << person
def name
'Andrew'
end
end
puts person.name
# ---- 輸出結(jié)果 ----
Andrew
對于類您同樣可以這么使用:
class Person
end
class << Person
def list
['Andrew', 'Alice']
end
end
p Person.list
# ---- 輸出結(jié)果 ----
["Andrew", "Alice"]
或者在類的內(nèi)部打開單例類:
class Person
class << self
def list
['Andrew', 'Alice']
end
end
end
p Person.list
# ---- 輸出結(jié)果 ----
["Andrew", "Alice"]
4. 小結(jié)
本章節(jié)我們學(xué)習(xí)了 Ruby 中單例類的知識,學(xué)習(xí)到了:
- 定義對象的方法的時候(包括類),所有的方法都是定義到了單例類中。
- 每一個對象都有單例類(包括類)。
- 實例對象的單例類的超類是實例對象的類。
- 類的單例類的超類等于類的超類的單例類。
- 單例類同樣也是
Class
類的對象。 - 我們可以通過
class <<
來打開單例類并定義方法。