Dynamic Proxies

可以使用 Ruby 的 delegate 库来快速得到一个实用的 动态代理

  require 'delegate'

  class Assistant
    def initialize(name)
      @name = name
    end

    def read_email
      "(#{@name}) It's mostly spam."
    end

    def check_schedule
      "(#{@name}) You have a meeting today."
    end
  end

  class Manager < DelegateClass(Assistant)
    def initialize(assistant)
      super(assistant)
    end

    def attend_meeting
      "Please hold my calls."
    end
  end

DelegateClass() 是一种拟态方法,这种方法创建并返回一个新的 Class。这个类会定义一个 method_missing() 方法,并把对它发生的调用转发到被封装的对象上,比如本例中的 Assistant 对象。Manager 类会继承这个 method_missing() 方法,因此它就成为被封装对象的一个代理。结果, Manager 就会把自己无法识别的方法转发给它封装的 Assistant:

  frank = Assistant.new("Frank")
  anne = Manager.new(frank)
  anne.attend_meeting            #=> "Please hold my calls."
  anne.read_email                #=> "(Frank) It's mostly spam."
  anne.check_schedule            #=> "(Frank) You have a meeting today."

const_missing()

如果喜欢 Object#method_missing() 方法,则推荐关注 Module#const_missing() 方法。当引用一个不存在的常量时,Ruby 将把这个常量名作为一个符号传递给 const_missing() 方法。

可以在一个给定命名空间中定义 const_missing() 方法。如果在 Object 类中定义它,那么所有的对象(包括最顶层的 main 对象)都会继承这个方法:

  def Object.const_missing(name)
    name.to_s.downcase.gsub(/_/, ' ')
  end

  MY_CONSTANT       #=> "my constant"
  • 使用幽灵方法时经常会出现的问题是,由于调用未定义的方法会导致调用 method_missing() 方法,对象可能会因此接受一个直接的错误方法调用。为了避免这样的困扰,应该仅在必要时才使用幽灵方法。

  • 当一个幽灵方法和一个真实方法发生名字冲突时,后者会胜出。如果不需要那个继承来的方法(真实方法),则可以通过删除它来解决这个问题。为了安全起见,应该在代理类中删除绝大多数继承来的方法。这就是所谓的 白板 类(BasicObject),它所拥有的方法比 Object 类还要少。

  • 有两种途径来删除一个方法,可以使用 Module#undef_method() 方法,它会删除所有(包括继承来的)的方法;也可以使用 Module#remove_method() 方法,它只会删除接受者自己的方法,而保留继承来的方法。