Hầu hết các phương pháp dụ được sử dụng trong Ruby là phương pháp toàn cầu. Điều đó có nghĩa là chúng có sẵn trong mọi trường hợp của lớp mà chúng được định nghĩa. Ngược lại, một phương thức singleton được thực hiện trên một đối tượng duy nhất.
Có một mâu thuẫn rõ ràng. Ruby lưu trữ các phương thức trong các lớp và tất cả các phương thức phải được liên kết với một lớp. Đối tượng mà trên đó một phương thức singleton được định nghĩa không phải là một lớp (nó là một thể hiện của một lớp). Nếu chỉ có các lớp có thể lưu trữ các phương thức, làm thế nào một đối tượng có thể lưu trữ một phương thức singleton? Khi một phương thức singleton được tạo, Ruby sẽ tự động tạo một lớp ẩn danh để lưu trữ phương thức đó. Những lớp ẩn danh này được gọi là metaclasses, còn được gọi là lớp singleton hoặc eigenclasses. Phương pháp singleton được kết hợp với metaclass, lần lượt, được kết hợp với đối tượng mà phương thức singleton được định nghĩa.
Nếu nhiều phương thức đơn được xác định trong một đối tượng, tất cả chúng đều được lưu trữ trong cùng một metaclass.
class Zen
end
z1 = Zen.new
z2 = Zen.new
def z1.say_hello # Notice that the method name is prefixed with the object name
puts "Hello!"
end
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
Trong ví dụ trên, phương thức say_hello được xác định trong cá thể z1 của lớp Zen chứ không phải thể hiện z2.
Ví dụ sau cho thấy một cách khác để xác định phương thức singleton, với cùng một kết quả.
class Zen
end
z1 = Zen.new
z2 = Zen.new
class << z1
def say_hello
puts "Hello!"
end
end
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
Trong ví dụ trên, lớp < < z1 thay đổi tự hiện tại để trỏ đến metaclass của đối tượng z1; sau đó, nó định nghĩa phương thức say_hello trong metaclass.
Cả hai ví dụ trên đều nhằm minh họa cách thức hoạt động của phương thức singleton. Tuy nhiên, có một cách dễ dàng hơn để định nghĩa một phương thức singleton: sử dụng phương thức dựng sẵn có tên là define_singleton_method.
class Zen
end
z1 = Zen.new
z2 = Zen.new
z1.define_singleton_method(:say_hello) { puts "Hello!" }
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
Chúng tôi đã học sớm hơn rằng các lớp cũng là đối tượng (các phiên bản của lớp được tạo sẵn có tên là Lớp). Chúng tôi cũng đã học về các phương pháp lớp học. Các phương thức lớp không có gì khác hơn là các phương thức singleton được liên kết với một đối tượng lớp.
Một ví dụ nữa:
class Zabuton
class << self
def stuff
puts "Stuffing zabuton…"
end
end
end
Tất cả các đối tượng có thể có metaclasses. Điều đó có nghĩa là các lớp học cũng có thể có metaclasses. Trong ví dụ trên, lớp < < tự sửa đổi bản thân để nó trỏ đến lớp siêu lớp của lớp Zabuton. Khi một phương thức được định nghĩa mà không có bộ nhận rõ ràng (lớp/đối tượng mà phương thức sẽ được định nghĩa), nó được định nghĩa ngầm trong phạm vi hiện tại, nghĩa là giá trị hiện tại của tự. Do đó, phương thức công cụ được định nghĩa trong metaclass của lớp Zabuton. Ví dụ trên chỉ là một cách khác để định nghĩa một phương thức lớp.
Đọc thêm tại this post about Ruby Classes.
Đây là câu trả lời thực sự xuất sắc về singletons –