Ruby使用者对attr_accessor一定不会陌生。
?
class A attr_accessor :num end
等效于:
class A def num @num end def =(value) @num = value end end
在类的定义中,attr_accessor定义了num的读写方法,只用了一行代码就生成了两个实例方法,很cool,不是嘛?。这就是Metaprogramming,用程序来编写程序。
?
Ruby中到底是用了什么trick能实现Metaprogramming这样cool的功能呢?
我们把attr_accessor当做一个method,那么调用attr_accessor的是self,这个self是class A,也就是说,attr_accessor是一个类方法。按照这个思路,我们可以这样写:
?
?
class A def self.log s attr_reader s define_method("#{s}=") do |val| instance_variable_set("@#{s}",val) end end log "num" end a=A.new a.num = 10 puts a.num
在这里,使用attr_reader来定义实例变量的读方法,define_method来定义实例变量的写方法。
?
使用attr_reader或者自定义的log可以用于扩展代码,我们可以将它们称为macros.
?
使用上面这段代码,并没有体现出Metaprogramming的威力,因为在class A中直接定义方法,要比你定义一个类方法简单明了。但是如果是这样:
?
?
class B < A log "bnum" end b = B.new b.bnum = 11 puts b.bnum # => 11
?我们就可以从A中直接继承log方法,来轻松构建B中的实例读写方法的了!这里的log,是不是很类似attr_accessor呢?
?
?
到目前为止,我们通过定义类方法,来实现继承类的Metaprogramming的方法。可是,有些时候,我们并不想通过继承的方式来获得元编程的能力,如果所有类想获得某种元编程的方法只能通过继承类的方式来实现,那太麻烦了吧!这个时候,module出现了。
?
?
module A def log s attr_reader s define_method("#{s}=") do |val| instance_variable_set("@#{s}",val) end end end class B extend A log "bnum" end b=B.new b.bnum = 11 puts b.bnum
体会一下module和extend的作用,easy,不是嘛?
?
如果对于一个module,级想包含其中的instance method,又想包含其中的class method,肿么办?这个时候,就要来一点trick了。
?
module A module ClassMethod def log s attr_reader s define_method("#{s}=") do |val| instance_variable_set("@#{s}",val) end end end def log puts "Just a instance method" end def self.included(hostclass) hostclass.extend ClassMethod end end class B include A log "bnum" end b = B.new b.log b.bnum = 10 puts b.bnum?
?
?
?