Voglio aggiungere un metodo Singleton con una chiusura a un oggetto di Ruby
-
16-09-2019 - |
Domanda
Vorrei aggiungere un metodo Singleton ad un particolare oggetto. Vorrei che quando un metodo di istanza su un oggetto viene chiamato per primo, che fa un certo lavoro, e quindi crea un metodo per Singleton detto oggetto con lo stesso nome (che contiene il lavoro). Su tutte le chiamate successive su detto oggetto, il metodo singleton avrebbe ombra il metodo di istanza e che sarebbe stato.
Io so come creare un metodo di Singleton, il mio problema è che voglio il metodo Singleton creato per chiamare un lambda (l
in questo caso). def non crea una chiusura, quindi non posso riferire l variabile (codice sotto) quando il metodo viene successivamente chiamato (l.call()
è commentata in questo esempio) desidero sapere come posso creare una chiusura quando si crea un metodo singleton su un particolare oggetto. Qualsiasi aiuto sarebbe apprezzato. Grazie.
class Thing
end
t = Thing.new
t2 = Thing.new
Thing.instance_eval() do
def speak
puts "I speak for all Things, I am a class method"
end
end
Thing.class_eval() do
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
l = lambda {puts r}
instance_eval() do
def speak()
puts "This is the singleton method in the Thing object #{self}"
#l.call() # I want this to work! How?
end
end
end
end
Thing.speak()
t.speak()
t2.speak()
t.speak()
t2.speak()
dà i seguenti risultati quando si esegue: (ho cambiato '<' a '#' così si fanno vedere in html)
Io parlo per tutte le cose, io sono una classe Metodo
Questo è il metodo di istanza fa riferimento dall'oggetto #Thing Thing: 0x1d204>
Questo è il metodo di istanza fa riferimento dal #Thing oggetto Thing: 0x1d1dc>
Questo è il metodo Singleton nel Cosa oggetto #Thing: 0x1d204>
Questo è il metodo Singleton nel Cosa oggetto #Thing: 0x1d1dc>
Soluzione
È possibile definire un metodo con un blocco utilizzando define_method
.
Esempio:
class Object
def eigenclass
class <<self; self end
end
end
a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String
Ecco un'implementazione di un metodo define_singleton_method
:
class Object
def define_singleton_method(name, &block)
eigenclass = class<<self; self end
eigenclass.class_eval {define_method name, block}
end
end
Altri suggerimenti
Ora che 1.9 è fuori, è possibile utilizzare define_singleton_method:
jruby --1.9 -S irb
irb(main):019:0> fn = -> { length * 10 }
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):020:0> s.define_singleton_method :length_times_ten, fn
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):021:0> s
=> "a string"
irb(main):022:0> s.length_times_ten
=> 80
Bene, un modo per farlo sarebbe quello di confezionare in una variabile di istanza:
(FYI si può solo fare class Thing
riaprire Thing
(è un po 'più corto rispetto all'utilizzo #class_eval
, e non è necessario #instance_eval
definire metodi dall'interno di un metodo).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
@l = lambda {puts r}
instance_eval do
def speak()
puts "This is the singleton method in the Thing object #{self}"
@l[]
end
end
end
end
Questa ridefinirà #speak
, ma solo per tale istanza di Thing
. Altri esempi di Thing
avranno ancora la definizione originale.
L'alternativa è, come Chuck sottolineato, utilizzare la classe singleton (alias metaclasse, alias eigenclass) associato con l'istanza. La classe singleton è l'oggetto che memorizza tutti i metodi singleton associati ad un oggetto. È possibile ottenere il contesto per la valutazione di classe Singleton utilizzando la sintassi class <<object ; ... ; end
divertente (simile al contesto data dal #class_eval
per classi normali).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
singleton_class = class <<self # open singleton class context for current instance
# in this context, self now refers to the singleton class itself
self
end
l = lambda {puts r}
singleton_class.class_eval do
# since we used #class_eval, local variables are still in scope
define_method(:speak) do
puts "This is the singleton method in the Thing object #{self}"
# since we used #define_method, local variables are still in scope
l[]
end
end
end
end