Ruby Language
Singleton-klasse
Zoeken…
Syntaxis
- singleton_class = class << object; zelf einde
Opmerkingen
Singleton-klassen hebben slechts één instantie: het bijbehorende object. Dit kan worden geverifieerd door te vragen naar Ruby's ObjectSpace
:
instances = ObjectSpace.each_object object.singleton_class
instances.count # => 1
instances.include? object # => true
Met <
kunnen ze ook worden geverifieerd als subklassen van de werkelijke klasse van het object:
object.singleton_class < object.class # => true
Referenties:
Invoering
Ruby heeft drie soorten objecten:
- Klassen en modules die instanties zijn van klasse Klasse of klasse Module.
- Gevallen van klassen.
- Singleton klassen.
Elk object heeft een klasse die zijn methoden bevat:
class Example
end
object = Example.new
object.class # => Example
Example.class # => Class
Class.class # => Class
Objecten zelf kunnen geen methoden bevatten, alleen hun klasse kan dat. Maar met singleton-klassen is het mogelijk om methoden aan elk object toe te voegen, inclusief andere singleton-klassen.
def object.foo
:foo
end
object.foo #=> :foo
foo
is gedefinieerd op de singleton-klasse van het object
. Andere Example
kunnen niet op foo
reageren.
Ruby maakt op verzoek singleton-lessen. Door ze te openen of methoden toe te voegen, moet Ruby ze maken.
Toegang tot Singleton Class
Er zijn twee manieren om de singleton-klasse van een object te verkrijgen
- methode
singleton_class
. - Heropening singleton klasse van een object en terug te keren
self
.
object.singleton_class
singleton_class = class << object
self
end
Toegang tot instantie- / klassenvariabelen in Singleton-klassen
Singleton-klassen delen hun instantie / klassenvariabelen met hun object.
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
Blokken sluiten rond hun doel / klasse variabelen doel. Toegang tot instantie- of klassenvariabelen met een blok in class_eval
of instance_eval
is niet mogelijk. Een string class_eval
aan class_eval
of class_variable_get
gebruiken, class_variable_get
het probleem.
class Foo
@@foo = :foo
end
class Example
@@foo = :example
Foo.define_singleton_method :foo do
@@foo
end
end
Foo.foo #=> :example
Overerving van Singleton Class
Subklasse ook Subklasse 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>
Het uitbreiden of opnemen van een module verlengt de Singleton Class niet
module ExampleModule
end
def ExampleModule.foo
:foo
end
class Example
extend ExampleModule
include ExampleModule
end
Example.foo #=> NoMethodError: undefined method
Berichtpropagatie met Singleton Class
Instanties bevatten nooit een methode, ze dragen alleen gegevens. We kunnen echter een singleton-klasse definiëren voor elk object, inclusief een instantie van een klasse.
Wanneer een bericht wordt doorgegeven aan een object (methode wordt de methode genoemd), controleert Ruby eerst of een singleton-klasse voor dat object is gedefinieerd en of het op dat bericht kan reageren, anders controleert Ruby de klasse van de voorouders van de instantie en loopt daarop verder.
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 heropenen (monkey-patching)
Er zijn drie manieren om een Singleton Class te heropenen
-
class_eval
op een singleton-klasse. -
class <<
blok gebruiken. -
def
gebruikt om rechtstreeks een methode voor de singleton-klasse van het object te definiëren
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
Elk object heeft een singleton-klasse waartoe u toegang hebt
class Example
end
ex1 = Example.new
def ex1.foobar
:foobar
end
ex1.foobar #=> :foobar
ex2 = Example.new
ex2.foobar #=> NoMethodError
Singleton lessen
Alle objecten zijn instanties van een klasse. Dat is echter niet de hele waarheid. In Ruby heeft elk object ook een ietwat verborgen singleton-klasse .
Hierdoor kunnen methoden worden gedefinieerd voor afzonderlijke objecten. De klasse singleton bevindt zich tussen het object zelf en de werkelijke klasse, dus alle methoden die erop zijn gedefinieerd, zijn beschikbaar voor dat object en alleen voor dat object.
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>>
Het bovenstaande voorbeeld had kunnen worden geschreven met define_singleton_method
:
object.define_singleton_method :exclusive_method do
"The method is actually defined in the object's singleton class"
end
Dat is hetzelfde als het definiëren van de methode in de singleton_class
van het 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
Vóór het bestaan van singleton_class
als onderdeel van Ruby's kern-API, stonden singleton-klassen bekend als metaclasses en waren ze toegankelijk via het volgende idioom:
class << object
self # refers to object's singleton_class
end