Ruby 求解代码疑惑

zhukejun · 2014年11月03日 · 最后由 xu_xiang_yang 回复于 2014年11月03日 · 1727 次阅读
module TimeStampingWriter
  def write_line(line)
    super("#{Time.new}: #{line} b")
  end
end

module NumberingWriter
  attr_reader :line_number
  def write_line(line)
    @line_number = 1 unless @line_number
    super("#{@line_number}: #{line} a")
    @line_number += 1
  end
end

class SimpleWriter
  def write_line(line)
    puts "#{line}"
  end
end

w1 = SimpleWriter.new
w1.extend(TimeStampingWriter)
w1.write_line('hello')

w2 = SimpleWriter.new
w2.extend(NumberingWriter)
w2.write_line('hello')

w = SimpleWriter.new
w.extend(TimeStampingWriter)
w.extend(NumberingWriter)
w.write_line('hello')

输出: 2014-11-03 17:00:19 +0800: hello b 1: hello a 2014-11-03 17:00:19 +0800: 1: hello a b

extend 两个模块后的输出很疑惑,求解释。

第一个结果 w1 对象继承拥有了 TimeStampingWriter Model 的 write_line 方法,当遇到 super("#{Time.new}: #{line} b")时,会将括号内的值作为参数并调用类方法中 write_line,因此打印出 2014-11-03 17:00:19 +0800: hello b

第二个结果同样和第一个一样,仅仅是 super 内括号内的参数变化了而已。

第三个结果得出的过程就要复杂点了,按照执行顺序,write_line 方法的扩展顺序相当与 Class>TimeStampingWriter > NumberingWriter,也就是想执行 NumberingWriter 中的 write_line 方法,执行完后此时参数应为 "1: hello a" ,即为 line = "1: hello a" 然后再调用 TimeStampingWriter 中的 write_line,执行完后参数又变为 "2014-11-03 17:00:19 +0800: 1: hello a b", 即为 line = "2014-11-03 17:00:19 +0800: 1: hello a b",最后才调用 Class 中的 write_line 方法打印出结果。

w1.singleton_class.ancestors => [#<Class:#<SimpleWriter:0x007fab9120b900>>, TimeStampingWriter, SimpleWriter, Object, Kernel, BasicObject] 我的理解是这样的,对象的方法是在他的 singleton 类中的,当我们w1 = SimpleWriter.new时实际是产生了[#<Class:#<SimpleWriter:0x007fab9120b900>>, SimpleWriter, Object, Kernel, BasicObject]这样一个祖先链,然后 extend 总是在 singleton 类的最近位置插入,所以产生了[#<Class:#<SimpleWriter:0x007fab9120b900>>, TimeStampingWriter, SimpleWriter, Object, Kernel, BasicObject] 的祖先链。根据 ruby 的方法查找机制,找的方法自然就是 TimeStampingWriter 中的方法

同样的你可以试试各种 include extend 方法,他们其实都是在和 singleton 做游戏,一个原则,extend 是往 self 的类的 singleton 类的最近的位置插入,include 是往实例的 singleton 最近位置插入。

不知道我讲的对不对,我们可以交流交流

需要 登录 后方可回复, 如果你还没有账号请 注册新账号