2016-06-08 38 views
5

我正在閱讀Sandi Metz的POODR並遇到了一個我不太明白的編碼原則。以下是代碼:有人可以幫助解釋創建類的post_initialize回調(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 

此代碼將輸出1,2,3,4,5作爲其各自的屬性。我不明白的是查找方法。

當山地自行車被實例化時,由於它沒有自己的initialize方法,它將沿着方法查找鏈遍歷其超類(Bicycle)。但是現在,從那裏開始,似乎Bicycle又回到了MountainBike的post_initialize方法。而不是繼續進行方法鏈,它怎麼會退縮? post_initialize是一個像initialize這樣的紅寶石關鍵字,因爲它提供某種特殊功能?是否有一些其他的紅寶石內省方法可以用來查看發生了什麼?

回答

6

瞭解這裏最重要的是,在該代碼:

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

... post_initialize有一個隱式接收器,self。換句話說,這裏的post_initialize(args)相當於self.post_initialize(args),而self是MountainBike的一個實例。方法查找總是以接收機的類別開頭,因此在查找MountainBike#post_initialize時沒有問題。

這是一個謊言;在涉及隱私時並不等同; private方法cannot be called with an explicit receiver
這也是一個謊言;它實際上是從接收者的單例類開始的,然後它會嘗試它的類。

1

post_initialize方法沒有什麼特別之處。它僅僅是一個簡單的子類的香草實例方法。

在Ruby中,超類實例方法能夠調用子類實例方法,即使在其構造函數中也是如此。看看這個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. 

通常的方式做到這一點是通過讓子類調用super,但我想三弟被暗示post_initialize方法,要求子類提供了自己的初始化,或正式拒絕通過實施一個空方法來實現。 (另外,子類的作者可以忘記調用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

謝謝你基思先生。我只是想補充一下,使其明確清楚 - 「自我」是隱式接收者,並且由於實例是派生類,因此自身就是派生類實例,然後可以訪問存在於其中的相關方法那個特定的派生類(按照約旦的答案)。 – BKSpurgeon