Ruby Language
Singleton Class
Поиск…
Синтаксис
- singleton_class = класс << объект; self end
замечания
В классах Singleton есть только один экземпляр: соответствующий ему объект. Это можно проверить, ObjectSpace
Ruby:
instances = ObjectSpace.each_object object.singleton_class
instances.count # => 1
instances.include? object # => true
Используя <
, они также могут быть проверены как подклассы реального класса объекта:
object.singleton_class < object.class # => true
Рекомендации:
Вступление
Ruby имеет три типа объектов:
- Классы и модули, которые являются экземплярами класса Class или класса Module.
- Экземпляры классов.
- Одиночные классы.
Каждый объект имеет класс, который содержит его методы:
class Example
end
object = Example.new
object.class # => Example
Example.class # => Class
Class.class # => Class
Сами объекты не могут содержать методы, только их класс может. Но с одноэлементными классами можно добавлять методы к любому объекту, включая другие одноэлементные классы.
def object.foo
:foo
end
object.foo #=> :foo
foo
определяется для одноэлементного класса object
. Другие Example
экземпляры не могут ответить на foo
.
Ruby создает классы singleton по требованию. Доступ к ним или добавление к ним методов заставляют Ruby создавать их.
Доступ к Singleton Class
Существует два способа получить одноэлементный класс объекта
- метод
singleton_class
. - Повторное открытие одноэлементного класса объекта и возвращение
self
.
object.singleton_class
singleton_class = class << object
self
end
Доступ к переменным экземпляра / класса в классах Singleton
Одиночные классы делят свои переменные экземпляра / класса с их объектом.
class Example
@@foo = :example
end
def Example.foo
class_variable_get :@@foo
end
Example.foo #=> :example
class Example
def initialize
@foo = 1
end
def foo
@foo
end
end
e = Example.new
e.instance_eval <<-BLOCK
def self.increase_foo
@foo += 1
end
BLOCK
e.increase_foo
e.foo #=> 2
Блокирует вокруг своих целевых переменных экземпляра / класса. Доступ к переменным экземпляра или класса с использованием блока в class_eval
или instance_eval
невозможен. class_eval
с class_variable_get
строки в class_eval
или с помощью class_variable_get
.
class Foo
@@foo = :foo
end
class Example
@@foo = :example
Foo.define_singleton_method :foo do
@@foo
end
end
Foo.foo #=> :example
Наследование класса Singleton
Подкласс также подклассы Singleton Class
class Example
end
Example.singleton_class #=> #<Class:Example>
def Example.foo
:example
end
class SubExample < Example
end
SubExample.foo #=> :example
SubExample.singleton_class.superclass #=> #<Class:Example>
Расширение или включение модуля не расширяет Singleton Class
module ExampleModule
end
def ExampleModule.foo
:foo
end
class Example
extend ExampleModule
include ExampleModule
end
Example.foo #=> NoMethodError: undefined method
Распространение сообщений с помощью класса Singleton
Экземпляры никогда не содержат метода, в котором они переносят данные. Однако мы можем определить одноэлементный класс для любого объекта, включая экземпляр класса.
Когда сообщение передается объекту (метод вызывается), Ruby сначала проверяет, определен ли один-одинечный класс для этого объекта, и если он может ответить на это сообщение, иначе Ruby проверяет цепочку предков класса класса и поднимается на это.
class Example
def foo
:example
end
end
Example.new.foo #=> :example
module PrependedModule
def foo
:prepend
end
end
class Example
prepend PrependedModule
end
Example.ancestors #=> [Prepended, Example, Object, Kernel, BasicObject]
e = Example.new
e.foo #=> :prepended
def e.foo
:singleton
end
e.foo #=> :singleton
Повторное открытие (переключение обезьян) Singleton Classes
Существует три способа повторного открытия класса Singleton
- Использование
class_eval
в одноэлементном классе. - Использование
class <<
block. - Использование
def
для определения метода в одиночном классе объекта непосредственно
class Example
end
Example.singleton_class.class_eval do
def foo
:foo
end
end
Example.foo #=> :foo
class Example
end
class << Example
def bar
:bar
end
end
Example.bar #=> :bar
class Example
end
def Example.baz
:baz
end
Example.baz #=> :baz
Каждый объект имеет одноэлементный класс, к которому вы можете получить доступ.
class Example
end
ex1 = Example.new
def ex1.foobar
:foobar
end
ex1.foobar #=> :foobar
ex2 = Example.new
ex2.foobar #=> NoMethodError
Одиночные классы
Все объекты являются экземплярами класса. Однако это не вся правда. В Ruby каждый объект имеет несколько скрытый одноэлементный класс .
Это то, что позволяет определять методы для отдельных объектов. Класс singleton находится между самим объектом и его фактическим классом, поэтому все методы, определенные на нем, доступны для этого объекта и только для этого объекта.
object = Object.new
def object.exclusive_method
'Only this object will respond to this method'
end
object.exclusive_method
# => "Only this object will respond to this method"
Object.new.exclusive_method rescue $!
# => #<NoMethodError: undefined method `exclusive_method' for #<Object:0xa17b77c>>
Приведенный выше пример можно было бы написать с помощью define_singleton_method
:
object.define_singleton_method :exclusive_method do
"The method is actually defined in the object's singleton class"
end
То же самое, что и определение метода на object
singleton_class
:
# send is used because define_method is private
object.singleton_class.send :define_method, :exclusive_method do
"Now we're defining an instance method directly on the singleton class"
end
До существования singleton_class
как части основного API Ruby, одноэлементные классы были известны как метаклассы и могли быть доступны через следующую идиому:
class << object
self # refers to object's singleton_class
end