RSS
 

Distruttori di classe #3

19 Jun

Eccoci all’ultimo articolo della serie rivolta ai distruttori di classe e più in generale a come fare per inserire un metodo da richiamare alla fine dell’utilizzo di una classe.

Nel precedente articolo abbiamo visto come fare per generare un evento in fase di distruzione dell’oggetto da parte del garbage collector e che richiami un metodo per svolgere le ultimissime operazioni.

Passiamo alla pratica e dopo i commenti:

class E
  #Creo una variabile di istanza, utile per i test
  attr_reader :var1
  @@count=0
 
  #Costruttore
  def initialize(var1)
    puts "Initializing #{self.object_id} var #{var1}"
    ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc)
    @var1=var1
    @@count +=1
  end
 
  #Metodo di servizio
  def body
    begin
      yield(self)
    rescue StandardError => err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end
 
  #Metodo che fa il lavoro sporco
  def work
    puts 'Work'
  end
 
  private
  #Distruttore
  def finalize
    puts "Finalize #{@var1}"
  end
  def self.finalize(id)
    puts "*** Finalize id #{id} nr. #{@@count} ***"
    @@count -=1
    #puts "Var1 #{ObjectSpace._id2ref(id).var1}"
  end
 
end
 
#Istanzio tre oggetti e li utilizzo
['aaa','bbb','ccc'].each do |str|
  E.new(str).body do |o| 
    o.work
    #...e tutti gli altri metodi
  end
end

L’output generato sarà questo:


D:\Marco\Progetti\Ruby\Batch\Test\Destructor>ruby D3.rb
Initializing 20806360 var aaa
Work
Finalize aaa
Initializing 20806220 var bbb
Work
Finalize bbb
Initializing 20806080 var ccc
Work
Finalize ccc
*** Finalize id 20806080 nr. 3 ***
*** Finalize id 20806220 nr. 2 ***
*** Finalize id 20806360 nr. 1 ***

Ci sono un pò di differenze rispetto al precedente articolo:

Questa volta il distruttore è richiamato direttamente nel costruttore perchè ho ipotizzato che servisse ad ogni oggetto, l’alternativa è definirlo come in un esempio precedente, solo per gli oggetti interessati, dopo l’istanziamento:

E.new('xxx').body do |o|
  ObjectSpace.define_finalizer(o, o.class.method(:finalize).to_proc)
  o.work
end

Un altra differenza è che questa volta non si associa una procedura creata “al volo” ma un metodo di classe, nella definizione della classe è infatti presente un nuovo metodo: self.finalize

Ho inserito una variabile di classe, chiamata anche attributo statico, @@count. E’ in comune con tutte le istanze e nell’esempio serve per conteggiare gli oggetti della classe E. Nel distruttore viene mostrato il valore per informarci del numero rimanente di oggetti.

Questa volta non è necessario forzare l’esecuzione del garbage collector in quanto al termine dello script viene fatto in automatico generando l’evento che richiama il costruttore per ogni oggetto.

Ho cercato di semplificare il codice e cosi facendo ho omesso una finezza di ruby che devo però indicarla almeno come nota anche se non c’entra nulla coi distruttori. Se i metodi da richiamare sono molti si può utilizzare questa sintassi lasciando il codice molto leggibile:

E.new('xxx').body do |o|
  ObjectSpace.define_finalizer(o, o.class.method(:finalize).to_proc)
    ['work', 'hardwork', 'ecc'].each do |method_string|
        o.send(method_string)
    end
end

Purtroppo non riesco ad utilizzare un metodo distruttore che sia univoco per istanza. Un altro tentativo poteva essere quello di puntare all’istanza tramite ObjectSpace che tramite il metodo _id2ref restituisce l’oggetto con id uguale a quello passato come parametro:

ObjectSpace._id2ref(id)

Sfortunatamente non funziona dall’interno del distruttore, probabilmente il garbage collector ha già distrutto l’istanza, questo è un peccato e spero esista un altro sistema che però non conosco…

 
Comments Off

Posted in Ruby

 

Tags: , ,

Comments are closed.