2016-06-08 10 views
5

Sto leggendo il POODR di Sandi Metz e ho trovato un principio di codifica che non capisco. Ecco il codice:Qualcuno può aiutare a spiegare il callback post_initialize per la creazione di classi (Sandi Metz)

class Bicycle 
attr_reader :size, :chain, :tire_size 

    def initialize(args = {}) 
    @size = args[:size] || 1 
    @chain = args[:chain] || 2 
    @tire_size = args[:tire_size] || 3 
    post_initialize(args) 
    end 
end 

class MountainBike < Bicycle 
    attr_reader :front_shock, :rear_shock 

    def post_initialize(args) 
    @front_shock = args[:front_shock] 
    @rear_shock = args[:rear_shock] 
    end 
end 

mb = MountainBike.new(front_shock: 4, rear_shock: 5) 
puts mb.size 
puts mb.chain 
puts mb.tire_size 
puts mb.front_shock 
puts mb.rear_shock 

Questo codice emette 1,2,3,4,5 per i rispettivi attributi. Quello che non capisco è il metodo di ricerca.

Quando una mountain bike viene istanziata, poiché non ha il suo metodo initialize, percorrerà la catena di ricerca del metodo sulla sua super classe (Bicycle). Ma ora da lì sembra che la bicicletta ritorni al metodo post_initialize di MountainBike. Invece di continuare la catena del metodo, come può tornare indietro? post_initialize è una parola chiave ruby ​​come initialize in quanto serve qualche tipo di funzione speciale? C'è qualche altro metodo di introspezione rubino che posso usare per vedere cosa sta succedendo?

risposta

6

La cosa importante da capire è che in questo codice:

def initialize(args = {}) 
    # ... 
    post_initialize(args) 
end 

... post_initialize dotato di un ricevitore implicita, self. In altre parole, post_initialize(args) è equivalente a a self.post_initialize(args) e self è un'istanza di MountainBike. La ricerca del metodo inizia sempre con la classe del destinatario, quindi non ha problemi a trovare MountainBike#post_initialize.

Questa è una bugia; non è equivalente quando si tratta di privacy; Metodi privatecannot be called with an explicit receiver.
Questa è anche una bugia; in realtà inizia con la classe singleton del ricevitore, ma poi prova la sua classe.

1

Non c'è niente di speciale nel metodo post_initialize. È semplicemente un semplice metodo di istanza di vaniglia della sottoclasse.

In Ruby, un metodo di istanza superclasse è in grado di chiamare un metodo di istanza di sottoclasse, anche nel suo costruttore. Check out questa sessione IRB:

2.3.0 :003 > class Base 
2.3.0 :004?> def initialize 
2.3.0 :005?>  foo 
2.3.0 :006?>  end 
2.3.0 :007?> end 
=> :initialize 
2.3.0 :015 > class Derived < Base 
2.3.0 :016?> def foo 
2.3.0 :017?>  puts 'I am foo.' 
2.3.0 :018?>  end 
2.3.0 :019?> end 
=> :foo 
2.3.0 :020 > Derived.new 
I am foo. 

Il solito modo questo è fatto è di avere la chiamata sottoclasse super, ma credo che Sandi suggerisce l'approccio post_initialize di esigere che la sottoclasse di fornire la propria inizializzazione, o formalmente scendesse a fallo implementando un metodo vuoto. (Inoltre, scrittori di sottoclassi possono dimenticare di chiamare super.) Ecco come sarebbe stato fatto con super:

2.3.0 :001 > class Base 
2.3.0 :002?> def initialize 
2.3.0 :003?>  puts 'in base' 
2.3.0 :004?>  end 
2.3.0 :005?> end 
=> :initialize 
=> #<Derived:0x007fda6ba291d8> 
2.3.0 :012 > class Derived < Base 
2.3.0 :013?> def initialize 
2.3.0 :014?>  super 
2.3.0 :015?>  puts 'in derived' 
2.3.0 :016?>  end 
2.3.0 :017?> end 
=> :initialize 
2.3.0 :018 > Derived.new 
in base 
in derived 
=> #<Derived:0x007fda6b104b98> 
+1

Grazie Mr Keith. Vorrei solo aggiungere per rendere esplicitamente chiaro - "self" è il ricevitore implicito, e dato che l'istanza era una classe derivata, che self sarebbe l'istanza della classe derivata, che potrebbe quindi accedere al metodo pertinente che esisterebbe in quella particolare classe derivata (come da risposta di Jordan). – BKSpurgeon

Problemi correlati