Ruby Language
Clase Singleton
Buscar..
Sintaxis
- singleton_class = clase << objeto; auto final
Observaciones
Las clases de Singleton solo tienen una instancia: su objeto correspondiente. Esto se puede verificar consultando el ObjectSpace
de Ruby:
instances = ObjectSpace.each_object object.singleton_class
instances.count # => 1
instances.include? object # => true
Usando <
, también se puede verificar que sean subclases de la clase real del objeto:
object.singleton_class < object.class # => true
Referencias:
Introducción
Ruby tiene tres tipos de objetos:
- Clases y módulos que son instancias de clase Clase o clase Módulo.
- Instancias de las clases.
- Clases singleton.
Cada objeto tiene una clase que contiene sus métodos:
class Example
end
object = Example.new
object.class # => Example
Example.class # => Class
Class.class # => Class
Los objetos en sí mismos no pueden contener métodos, solo su clase puede. Pero con las clases de singleton, es posible agregar métodos a cualquier objeto, incluidas otras clases de singleton.
def object.foo
:foo
end
object.foo #=> :foo
foo
se define en la clase de object
singleton. Otros Example
casos no pueden responder a foo
.
Ruby crea clases de singleton bajo demanda. Acceder a ellos o agregarles métodos obliga a Ruby a crearlos.
Acceso a la clase Singleton
Hay dos formas de obtener la clase singleton de un objeto.
- método
singleton_class
- Reapertura de la clase singleton de un objeto y regreso del
self
.
object.singleton_class
singleton_class = class << object
self
end
Acceso a variables de instancia / clase en clases singleton
Las clases de Singleton comparten sus variables de instancia / clase con su objeto.
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
Los bloques se cierran alrededor de sus variables de instancia / clase de destino. Acceder a las variables de instancia o clase usando un bloque en class_eval
o instance_eval
no es posible. Pasar una cadena a class_eval
o usar class_variable_get
el problema.
class Foo
@@foo = :foo
end
class Example
@@foo = :example
Foo.define_singleton_method :foo do
@@foo
end
end
Foo.foo #=> :example
Herencia de la clase Singleton
Subclasificación también Subclases Clase Singleton
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>
Extender o incluir un módulo no amplía la clase Singleton
module ExampleModule
end
def ExampleModule.foo
:foo
end
class Example
extend ExampleModule
include ExampleModule
end
Example.foo #=> NoMethodError: undefined method
Propagación de mensajes con Singleton Class
Las instancias nunca contienen un método que solo llevan datos. Sin embargo, podemos definir una clase singleton para cualquier objeto, incluida una instancia de una clase.
Cuando se pasa un mensaje a un objeto (se llama método), Ruby primero verifica si una clase singleton está definida para ese objeto y si puede responder a ese mensaje, de lo contrario Ruby verifica la cadena de ancestros de la clase de la instancia y continúa con eso.
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
Reapertura (parches de monos) Clases Singleton
Hay tres formas de reabrir una clase Singleton
- Usando
class_eval
en una clase de singleton. - Usando la
class <<
bloque. - Usando
def
para definir un método en la clase singleton del objeto directamente
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
Cada objeto tiene una clase singleton a la que puedes acceder
class Example
end
ex1 = Example.new
def ex1.foobar
:foobar
end
ex1.foobar #=> :foobar
ex2 = Example.new
ex2.foobar #=> NoMethodError
Clases singleton
Todos los objetos son instancias de una clase. Sin embargo, esa no es toda la verdad. En Ruby, cada objeto también tiene una clase singleton algo oculta.
Esto es lo que permite definir métodos en objetos individuales. La clase singleton se encuentra entre el objeto en sí y su clase real, por lo que todos los métodos definidos en él están disponibles para ese objeto, y solo ese objeto.
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>>
El ejemplo anterior podría haberse escrito usando define_singleton_method
:
object.define_singleton_method :exclusive_method do
"The method is actually defined in the object's singleton class"
end
Lo que equivale a definir el método en singleton_class
object
:
# 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
Antes de la existencia de singleton_class
como parte de la API central de Ruby, las clases de singleton se conocían como metaclases y se podía acceder a ellas a través del siguiente idioma:
class << object
self # refers to object's singleton_class
end