有人可以帮助解释类创建的post_initialize回调(桑迪·梅茨)



我正在阅读Sandi Metz的POODR,遇到了一个我不太理解的编码原理。这是代码:

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)。但现在从那里开始,Bike似乎又回到了MountainBike的post_initialize方法。而不是继续沿着方法链向上,它怎么能回落呢?post_initializeinitialize一样的 ruby 关键字是否具有某种特殊功能?我可以使用其他一些 ruby 内省方法来查看正在发生的事情吗?

这里要了解的重要一点是,在此代码中:

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

post_initialize有一个隐式接收器,self .换句话说,这里的post_initialize(args)等价于†self.post_initialize(args)self是山地自行车的一个实例。 方法查找始终从接收方的类开始, 因此查找MountainBike#post_initialize没有问题。

这是一个谎言; 在隐私方面,它并不等同; private方法不能使用显式接收器调用。
这也是一个谎言;它实际上从接收方的单例类开始,但随后它会尝试其类。

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,但我想 Sandi 建议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>

最新更新