singleton method
singleton method
: 屬於某一個 object 的方法,也代表只屬於該 object,儘管有相同的 class 也無法使用別人的 singleton method
(但 extend
可以)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class User
def self . class_hi
'class_hi'
end
class << self
def class_hi_2
'class_hi_2'
end
end
def instance_hi
'instance_hi'
end
end
def User . class_hi_3
'class_hi_3'
end
因為 User class
所定義的 class_method
都屬於 User class
object
1
User . singleton_methods # => [:class_hi, :class_hi_2, :class_hi_3]
每次 new 的 instance 都會有 instance_hi 所以不算是 singleton_method,並沒有屬於哪一個 object
1
2
3
User . new . singleton_methods # => []
leon = User . new
leon . singleton_methods # => []
建立專屬於 leon object 的 method
1
2
3
4
5
6
7
8
9
10
11
def leon . hello
'hello'
end
class << leon
def foo
"Hello World!"
end
end
leon . singleton_methods # => [:hello, : foo]
leon object 所定義的 method 並不存在於 User class
,而是在 leon object 後面的 singleton_class
1
2
3
4
5
6
User . method_defined? ( :instance_hi ) # => true
User . method_defined? ( :hello ) # => false
leon . singleton_class . method_defined? ( :instance_hi ) # => true
leon . singleton_class . method_defined? ( :hello ) # => true
User . new . singleton_class . method_defined? ( :instance_hi ) # => true
User . new . singleton_class . method_defined? ( :hello ) #=> false
覆蓋原本 instance 的 method
1
2
3
4
5
6
7
leon . instance_hi # => "instance_hi"
def leon . instance_hi
'singleton_hi'
end
leon . instance_hi # => "singleton_hi"
instance_eval 是所有 instance 都會有,所以也不是 singleton_method
1
2
3
4
5
6
7
User . instance_eval do
def test
'instance_eval'
end
end
User . new . singleton_methods # => []
透過 singleton_method
取得 proc 在用 call 呼叫
1
2
hi = leon . singleton_method ( :instance_hi )
hi . call
singleton class
singleton class
: 一個隱藏在物件(不管是普通物件還是類)後面的一個特殊類,它只有一個實例(就是它自己)
When you add a method to a specific object Ruby inserts a new anonymous class into the inheritance hierarchy as a container to hold these types of methods.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
leon . singleton_class
# => #<Class:#<User:0x007fa99c887c58>>
User . singleton_class
# => #<Class:User>
# #<Class: 開頭的都是 singleton_class
User . singleton_class . ancestors
# => [#<Class:User>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
leon . singleton_class . singleton_methods
# => [:class_hi, :class_hi_2, :class_hi_3]
User . singleton_class?
# => false
User . singleton_class . singleton_class?
# => true
singleton module
先來說一下 singleton pattern
在 class
當中,可以一直建立 instance (object_id 都不同)
,每個 instance
都會佔用 memory,但有時候只需要建立一個,但為了避免建立多個,而造成 memory 的浪費,於是就有了 singleton module
在 ruby 當中不是 core library 所以必須引入 singleton
,可以發現當 include Singleton
後 User.new
就不能使用了
1
2
3
4
5
6
7
8
9
10
require 'singleton'
class User
include Singleton
attr_accessor :name , :phone
end
User . instance # => #<User:0x007f951a821ed0>
User . new # => NoMethodError: private method `new' called for User:Class
可以發現,每次的 instance
其實都是同一個 object(這樣就不會浪費 memory)
1
2
3
4
5
6
User . instance . object_id
# => 70139185794920
User . instance . object_id
# => 70139185794920
User . instance . object_id
# => 70139185794920
原本的 new
則是變成了 private method
,而不是不見了
1
2
3
4
5
6
User . send ( :new ) . object_id
# => 70139173678780
User . send ( :new ) . object_id
# => 70139173207900
User . send ( :new ) . object_id
# => 70139177568960
透過 ObjectSpace 來看一下所有存在的 object
The ObjectSpace module contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'singleton'
class User
include Singleton
attr_accessor :name , :phone
end
ObjectSpace . each_object ( User ){} # => 0
User . instance # => #<User:0x007fa18f02e008>
ObjectSpace . each_object ( User ){} # => 1
User . instance # => #<User:0x007fa18f02e008>
ObjectSpace . each_object ( User ){} # => 1
User . send ( :new ) #<User:0x007fa18f185cd0>
ObjectSpace . each_object ( User ){} # => 2
singleton 行為一樣會繼承 使用範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'singleton'
class User
include Singleton
attr_accessor :name , :phone
end
class Worker < User
end
Worker . new # => NoMethodError: private method `new' called for Worker:Class
worker = Worker . instance # => #<Worker:0x007fb663837158>
worker . name = 'name' # => "name"
worker . phone = 'phone' # => "phone"
user = User . instance # => #<User:0x007fa673119790>
user . name # => nil
user . phone # => nil
1
2
worker . clone # => TypeError: can't clone instance of singleton Worker
worker . dup # => TypeError: can't dup instance of singleton Worker
參考文件