2016-06-08 4 views
5

Ich lese Sandi Metz's POODR und bin auf ein Codierprinzip gestoßen, das ich nicht ganz verstehe. Hier ist der Code:Kann jemand helfen, den post_initialize Callback für die Klassenerstellung zu erklären (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 

Dieser Code gibt 1,2,3,4,5 für seine jeweiligen Attribute. Was ich nicht verstehe, ist die Methode nachschlagen.

Wenn ein Mountainbike instanziiert wird, weil es keine eigene initialize-Methode hat, wird es die Methoden-Lookup-Kette auf seine Super-Klasse (Bicycle) hochfahren. Aber jetzt scheint es, dass Bicycle dann zurück in MountainBikes Methode post_initialize fährt. Anstatt die Methodenkette fortzusetzen, wie kann sie wieder runter gehen? Ist post_initialize ein Ruby-Schlüsselwort wie initialize in dem es eine Art besonderer Funktion dient? Gibt es noch andere Rubin-Introspektionsmethoden, mit denen ich sehen kann, was vor sich geht?

Antwort

6

Wichtig ist hier zu verstehen, dass in diesem Code:

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

... post_initialize einen impliziten Empfänger hat, self. Mit anderen Worten, post_initialize(args) entspricht hier bis self.post_initialize(args), und self ist eine Instanz von MountainBike. Methodensuche beginnt immer mit der Klasse des Empfängers, , so dass es keine Probleme gibt, MountainBike#post_initialize zu finden.

Das ist eine Lüge; es ist nicht gleichwertig, wenn es um Privatsphäre geht; private Methoden cannot be called with an explicit receiver.
Das ist auch eine Lüge; es beginnt tatsächlich mit der Singleton-Klasse des Empfängers, aber dann versucht es seine Klasse.

1

Es gibt nichts Besonderes an der post_initialize Methode. Es ist lediglich eine einfache Vanilla-Instanzmethode der Unterklasse.

In Ruby kann eine Superklasseninstanzmethode eine Unterklasseninstanzmethode aufrufen, selbst in ihrem Konstruktor. Sehen Sie sich diese irb Sitzung aus:

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. 

Die übliche Art und Weise dies geschieht, ist durch die Unterklasse Anruf mit super, aber ich denke, Sandi den post_initialize Ansatz schlägt vor, zu verlangen, dass die Unterklasse eine eigene Initialisierung bereitzustellen, oder formell ablehnen Tun Sie dies, indem Sie eine leere Methode implementieren. (Plus, Schriftsteller von Subklassen kann vergessen super zu nennen.) Hier ist, wie es mit Super geschehen würde:

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

Vielen Dank, Herr Keith. Ich möchte nur hinzufügen, um es explizit klar zu machen - "self" ist der implizite Empfänger, und da die Instanz eine abgeleitete Klasse ist, dann wäre self die abgeleitete Klasseninstanz, die dann auf die relevante Methode zugreifen könnte, die in existieren würde diese bestimmte abgeleitete Klasse (gemäß Jordans Antwort). – BKSpurgeon

Verwandte Themen