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>

È stato utile?

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top