美文网首页
元编程:方法

元编程:方法

作者: Jayzen | 来源:发表于2016-01-07 19:26 被阅读73次

    这个章节主要是通过两种方式来解决静态语言中重复编码的问题,这两种方法分别是动态方法和幽灵方法。

    1.待改写的冗余代码
    #Ds模拟数据库
    class Ds
      def get_mouse_info(id)
        puts "this is #{id} mouse information"
      end
    
      def get_cpu_info(id)
        puts "this is #{id} cpu information"
      end
    end
    
    class Computer
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
      end
    
      def mouse
        info = @data_source.get_mouse_info(@id)
        puts info
      end
    
      def cpu
        info = @data_source.get_cpu_info(@id)
        puts info
      end
    end
    
    cp = Computer.new(1, Ds.new)
    cp.mouse
    
    2.动态派发和动态方法
    动态派发:在代码运行期间,直到最后一刻才决定调用哪个方法,比如使用send方法.
    动态方法:在运行时定义方法的技术,比如define_method.
    
    #动态派发实例
    class MyClass
      def my_method(my_arg) 
        puts my_arg*2 
      end
    end
    obj = MyClass.new
    obj.my_method(3) #=>6
    #当你调用一个方法时,实际上是给一个对象发送了一条消息代码如下所示:
    obj.send(:my_method, 3) #=>6
    obj.send("my_method", 6) #=>6
    obj.send(my_method, 6) #=> undefined local variable or method
    
    #动态方法实例
    class MyClass
      define_method :my_method do |my_arg|
        puts my_arg*3
      end
    end
    
    obj = MyClass.new
    obj.my_method(2)
    
    上面的两段代码分别显示了方法定义和方法调用的非常规形式:
    1. 使用define_method代替def关键词,实现类定义的作用.
    2. 使用“.”来进行方法调用.
    
    3.动态方法改写冗余代码
    3.1.添加动态派发
    class Ds
      def get_mouse_info(id)
        puts "this is #{id} mouse information"
      end
    
      def get_cpu_info(id)
        puts "this is #{id} cpu information"
      end
    end
    
    class Computer
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
      end
    
      def mouse
        component :mouse
      end
    
      def cpu
        component :cpu
      end
    
      def component(name)
        info = @data_source.send "get_#{name}_info", @id
        puts info
      end
    end
    
    cp = Computer.new(1, Ds.new)
    cp.mouse
    
    3.2.添加动态方法
    class Ds
      def get_mouse_info(id)
        "this is #{id} mouse information"
      end
    
      def get_cpu_info(id)
        "this is #{id} cpu information"
      end
    end
    
    class Computer
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
      end
    
      def self.define_component(name)
        define_method(name){
          info = @data_source.send "get_#{name}_info", @id
          puts info
        }
      end
    
      define_component :mouse
      define_component :cpu
    end
    
    cp = Computer.new(2, Ds.new)
    cp.keyboard  # this is 2 keyboard information
    
    3.3.使用内省方法
    内省方法:在初始化的时候就对代码进行了优化,其中在初始化过程中使用如下代码:
    
    class Ds
      def get_mouse_info(id)
        "this is #{id} mouse information"
      end
    
      def get_cpu_info(id)
        "this is #{id} cpu information"
      end
    end
    
    class Computer
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
        #内省
        data_source.methods.grep(/^get_(.*)_info$/){ Computer.define_component $1 }
      end
    
      def self.define_component(name)
        define_method(name){
          info = @data_source.send "get_#{name}_info", @id
          puts info
        }
      end
    end
    
    cp = Computer.new(2, Ds.new)
    cp.keyboard  # this is 2 keyboard information
    
    4. 幽灵方法
    我自己的理解就是当一个对象调用方法的过程中,当这个方法不存在的时候,那么后台会报没有定义方法这个错误.
    其实ruby的机制是当这个方法不存在的时候,对象会调用method_missing方法,这个方法是BasicObject类的实例方法.
    
    class Demo
    end
    
    obj = Demo.new
    obj.cc #undefined method "cc"
    
    #使用method_missing方法对代码进行改写.
    class Demo
      def method_missing(method, *args)
        puts "this is the method missing"
      end
    end
    
    obj = Demo.new
    obj.cc #this is the method missing
    
    #上面的代码其实用到的是monkey patch,下面介绍另外一个关键词super
    class Demo
      def method_missing(method, *args)
        return "this is the method missing" if method =~/cc/\
        #若与/cc/不匹配,那么执行super代码,返回到BasicObject类定义的method_missing
        super
      end
    end
    
    obj = Demo.new
    obj.cc #this is the method missing
    obj.dd #undefined method "dd"
    
    5. 通过幽灵方法改写代码
    class Ds
      def get_mouse_info(id)
        "this is #{id} mouse information"
      end
    
      def get_cpu_info(id)
        "this is #{id} cpu information"
      end
    end
    
    class Computer
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
      end
    
      def method_missing(name, *args)
        super if !@data_source.respond_to?("get_#{name}_info")
        info = @data_source.send("get_#{name}_info", args[0])
        puts info   
      end
    end
    
    cp = Computer.new(2, Ds.new)
    cp.mouse  # this is  mouse information
    
    上述的代码出现两个问题:
    问题1:没有呈现出id的值,这个问题目前找不到比较合适的解决方法。
    问题2:像比如keyboard这些ghost不是真正的方法,如下面的代码所示:
    cp = Computer.new(2, Ds.new)
    cp.respond_to?(:keyboard)   #=>false
    
    解决问题2的方式可以通过猴子补丁的形式respond_to?方法来解决
    
    class Computer
      def respond_to?(method)
        @data_source.respond_to("get_#{method}_info") || super
      end
      ##
    cp = Computer.new(2, Ds.new)
    cp.respond_to?(:keyboard)   #=>true
    
    6. 幽灵方法中出现死循环
    #method_missing方法出现了未被定义的方法或者变量
    class Roulette
      def method_missing(name, *args)
        person = name.to_s.capitalize
        3.times do 
          number = rand(10) + 1
          puts "#{number}"  
        end
        #number是未被定义的变量
        "#{person} got a #{number}"
      end
    end
    
    number_of = Roulette.new
    puts number_of.bob
    puts number_of.frank 
    
    #代码修正
    class Roulette
      def method_missing(name, *args)
        person = name.to_s.capitalize
        super unless %w[Bob Frank Bill].include? person
        number = 0
        3.times do 
          number = rand(10) + 1
          puts "#{number}"  
        end
        "#{person} got a #{number}"
      end
    end
    
    number_of = Roulette.new
    puts number_of.Bob
    puts number_of.frank 
    
    7. 幽灵方法中出现方法冲突
    #method_missing和真实存在方法冲突时,执行真实方法,忽略method_missing方法
    class Ds
      def get_display_info(id)
        "this is #{id} display information"
      end
    end
    
    class Computer
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
      end
    
      def method_missing(name, *args)
        super if !@data_source.respond_to?("get_#{name}_info")
        info = @data_source.send("get_#{name}_info", args[0])
        puts info   
      end
    end
    
    cp = Computer.new(2, Ds.new)
    cp.display#  <#Computer: xxxxxx>
    
    #display是Object的实例方法:
    Object.instance_methods.grep(/display/) #=>display
    
    8. 使用白板
    作用:避免方法冲突
    两种方式:
    1、Module#undef_method方法来删除所有继承来的方法
    2、Module#remove_method方法来删除接受者自己的方法,而保留继承来的方法。
    
    #通过下面的语句可以把Computer类编程一个白板
    class Computer
      instance_methods.each do |m|
        undef_method m unless m.to_s =~ /method_missing|respond_to?/
      end
    #……
    #……
    end
    
    #上面的代码会报两个warning:
     warning: undefining `object_id' may cause serious problems
     warning: undefining `__send__' may cause serious problems
    
    #为了安全起见,这两个保留方法不能够删除,因此改动上面的代码:
    class Computer
      instance_methods.each do |m|
        undef_method m unless m.to_s =~ /^__|object_id|method_missing|respond_to?/
      end
    #……
    #……
    end
    
    9.使用method_missing和白板进行改写
    class Ds
      def get_mouse_info(id)
        "this is #{id} mouse information"
      end
    
      def get_cpu_info(id)
        "this is #{id} cpu information"
      end
    end
    
    class Computer
      instance_methods.each do |m|
        undef_method m unless m.to_s =~ /^__|object_id|method_missing|respond_to?/
      end
    
      def initialize(id, data_source)
        @id = id
        @data_source = data_source
      end
    
      def method_missing(name, *args)
        super if !@data_source.respond_to?("get_#{name}_info")
        info = @data_source.send("get_#{name}_info", args[0])
        puts info   
      end
    
      def respond_to?(method)
        @data_source.respond_to("get_#{method}_info") || super
      end 
    end
    
    cp = Computer.new(2, Ds.new)
    cp.keyboard  # this is  keyboard information
    
    10. 幽灵方法的性能考虑
    #使用幽灵方法比使用普通方法要慢,因为在调用幽灵方法时,方法查找的路径一般要更长一些
    class String
      def method_missing(method, *args)
        method == :ghost_reverse ? reverse : super
      end
    end
    
    require 'benchmark'
    
    Benchmark.bm do |b|
      b.report 'Normal method' do
        1000000.times{ "abc".reverse }
      end
    
      b.report 'Ghost method' do
        1000000.times{ "abc".ghost_reverse }
      end
    end
    
    性能比较

    相关文章

      网友评论

          本文标题:元编程:方法

          本文链接:https://www.haomeiwen.com/subject/irpfkttx.html