2016-06-08 9 views
5

Czytam POODR Sandi Metz i natknąłem się na zasadę kodowania, której nie rozumiem. Oto kod:Czy ktoś może pomóc w wyjaśnieniu wywołania zwrotnego post_initialize w celu utworzenia klasy (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 

Ten kod wyświetli 1,2,3,4,5 dla swoich odpowiednich atrybutów. Czego nie rozumiem, to metoda wyszukiwania.

Gdy tworzony jest rower górski, ponieważ nie ma on własnej metody initialize, przejdzie on w górę łańcucha wyszukiwania metod na swoją super klasę (Bicycle). Ale teraz wydaje się, że rower cofa się w dół do metody post_initialize MountainBike. Zamiast kontynuować łańcuch metod, jak można go wycofać? Czy post_initialize słowo kluczowe ruby, takie jak initialize, służy do jakiejś specjalnej funkcji? Czy istnieją inne metody introspekcji ruby, które mogę wykorzystać, aby zobaczyć, co się dzieje?

Odpowiedz

6

Ważną rzeczą do zrozumienia jest to, że w tym kodzie:

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

... post_initialize ma niejawny odbiornik, self. Innymi słowy, tutaj post_initialize(args) jest odpowiednikiem na self.post_initialize(args), a self jest instancją MountainBike. Wyszukiwanie metod zawsze rozpoczyna się od klasy odbiorcy, , więc nie ma problemu ze znalezieniem MountainBike#post_initialize.

To kłamstwo; nie jest to odpowiednik, jeśli chodzi o prywatność; private metody cannot be called with an explicit receiver.
To także kłamstwo; faktycznie zaczyna się od pojedynczej klasy odbiornika, ale potem próbuje swojej klasy.

1

Nie ma nic szczególnego w metodzie post_initialize. Jest to po prostu zwykła metoda instancji wanilii podklasy.

W języku Ruby metoda klasy nadklasy jest w stanie wywołać metodę instancji podklasy, nawet w jej konstruktorze. Sprawdź tę IRB sesji:

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. 

Zwykły sposób odbywa się to jest poprzez wywołanie podklasy super, ale myślę, Sandi sugeruje podejście post_initialize wymagać, że podklasa dostarczyć własną inicjalizacji lub formalnie odmówić zrób to, wdrażając pustą metodę. (Plus, pisarze podklasy może zapomnieć zadzwonić super.) Oto jak to zrobić z 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

Dziękuję Keith. Chciałbym tylko dodać, aby było to wyraźnie jasne - "self" jest niejawnym odbiorcą, a biorąc pod uwagę, że instancja była klasą pochodną, ​​to self byłoby instancją klasy pochodnej, która mogłaby wtedy uzyskać dostęp do odpowiedniej metody, która ta konkretna klasa pochodna (jak na odpowiedź Jordana). – BKSpurgeon

Powiązane problemy