Ruby 求解代码疑惑

zhukejun · November 03, 2014 · Last by xu_xiang_yang replied at November 03, 2014 · 1730 hits
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 最近位置插入。

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

You need to Sign in before reply, if you don't have an account, please Sign up first.