RSS
 

Distruttori di classe #1

19 Jun

Qualche esempio per testare il funzionamento dei distruttori di classe in ruby.

In questo articolo tratterò qualcosa di simile, non proprio distruttori ma metodi utilizzati per terminare il nostro lavoro, senza il diretto scopo di liberare memoria, comunque per comodità chiamerò anche questi distruttori. Se è giusto o no non lo so ma poco importa, tralasciamo la parte filosofica.

Il manuale Programming Ruby di Dave Thomas senza dubbio è ottimo ma non c’è tutto quello che avrei voluto trovare sull’argomento. Molto si trova anche in rete anche se in maniera molto disordinata.
Questi sono miei tentativi, non è detto che non ci sia un modo migliore per fare la stessa cosa, lo scopo è anche cercare di migliorarsi quindi ben vengano eventuali commenti.

Per rendere queste righe appetibili anche ai neofiti ho inserito delle premesse, chi già conosce l’argomento può così saltare la lettura.

Faccio una premessa riguardo la programmazione ad oggetti: per ogni classe esiste un metodo costruttore, utilizzato per inizializzare una classe, ed un metodo distruttore, in genere utilizzato per rilasciare le risorse occupate dalla classe istanziata. In genere sono opzionali e quando vengono specificati viene fatto un override del metodo presente sulla classe base, dalla quale tutte le classi derivano.

Nei linguaggi più moderni come Java, .NET e altri tra cui anche Ruby, il distruttore ha perso la funzionalità che aveva in origine in favore del garbage collector.

Il garbage collector è, per stare fedele alla traduzione, il processo che smaltisce la spazzatura, in altre parole è colui che si occupa di analizzare ogni oggetto istanziato e determinare se è ancora utilizzato, in caso negativo lo si elimina. Una volta era compito del programmatore avere cura di fare pulizia, ora la gestione della memoria è gestita in automatico, oltre che migliorare la comodità questo ha anche impatto sulle prestazioni, migliorandole nella maggior parte dei casi.

Passiamo alla pratica con questo esempio banale:

class E
  #Creo una variabile di istanza, utile per i test
  attr_reader :var1
 
  #Costruttore
  def initialize(var1)
    puts "Initializing #{self.object_id} var #{var1}"
    @var1=var1
  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 "End #{@var1}"
  end
end
 
E.new('Try1').body do |o|
  o.work
end

Nell’esempio viene definita la classe E e subito sotto ci sono le istruzione per testarla, si otterrà un output come questo:


D:\Marco\Progetti\Ruby\Batch\Test>ruby E.rb
Initializing 20807530 var Try1
Work
End Try1

Entrando nel dettaglio, viene istanziata la classe passando al metodo costruttore una stringa per l’inizializzazione della classe. Nel metodo costruttore viene mostrato anche l’id dell’oggetto istanziato, un riferimento univoco nello spazio oggetti.

A questo punto abbiamo istanziato la classe passandola come parametro al blocco.

Utilizzando una metafora, immaginate che la definizione di classe sia uno stampino, come quello che utilizzano i bambini al mare. Lo stampino è la definizione della classe, ogni forma di sabbia che creo equivale ad un istanziamento della classe.

Il metodo body possiamo definirlo di servizio poiché non include nessuna istruzione scritta da noi bensì fa da tramite eseguendo ciò che gli passiamo tramite il blocco:

E.new('Try1').body do |o|
  o.work
end

Dentro questo blocco inseriamo ciò che vogliamo fare con questa classe. Al termine viene eseguito il metodo distruttore “finalize”, lo si può vedere esaminando il contenuto del metodo body:

  def body
    begin
      yield(self)
    rescue StandardError => err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end

Il blocco viene “esploso” con l’istruzione yield(self), dove il parametro si riferisce all’oggetto del blocco. Se capita un imprevisto viene intercettata l’eccezione, stampato l’errore e comunque eseguito il distruttore.

Per motivi di tempo devo fermarmi quà. Nel prossimo articolo scriverò come eseguire automaticamente il distruttore, tramite la generazione di un evento da parte del garbage collector.

 
Comments Off

Posted in Ruby

 

Tags: ,

Comments are closed.