`
Goldice
  • 浏览: 104571 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Ruby Metaprogramming

阅读更多

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
 

 

 

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics