元编程

元编程 #

元编程类似 Java 的反射。

修改方法 #

Ruby 元编程支持动态增加方法,重定义方法和删除方法。

class EmptyClass
end

# 添加方法
class EmptyClass
  def everything_changes
    'Wait, what? You just added a method to me!'
  end
end

puts EmptyClass.new.everything_changes

# 重定义方法
class EmptyClass
  def everything_changes
    'Redefine everything changes!'
  end
end

puts EmptyClass.new.everything_changes

动态方法调用 #

动态方法调用主要依靠 send() 方法,该方法第一个参数为需要调用的方法名。

class Glider
  def lift
    'do lift'
  end

  def turn(dir)
    "do turn #{dir}"
  end
end
class Nomad
  def initialize(glider)
    @glider = glider
  end

  def do(action, args = nil)
    if args==nil
      @glider.send(action)
    else
      @glider.send(action, args)
    end
  end
end

nomad = Nomad.new(Glider.new)
puts nomad.do('lift')
puts nomad.do('turn', 'left')

方法缺失 #

动态方法调用时很可能调用不存在的方法,这是就会发生 method missing 的异常。 此时可以通过定义 method_missing() 方法来处理所有不存在的方法的调用。

class Spy
  def method_missing(method_name, *args, &block)
    puts "#{method_name} was called with #{args} and #{block}"
  end
end
spy = Spy.new
spy.hello('hello', 'world') { |n| puts n } # => hello was called with ["hello", "world"] and #<Proc:

send 与代理模式 #

send() 结合方法缺失可以实现基本的代理模式。

class Wrapper
  def initialize(agent)
    @agent = agent
  end

  def method_missing(method_name, *args, &block)
    @agent.send(method_name, *args)
  end
end

wrapper= Wrapper.new([10, 20, 30])
puts wrapper.max # => 30

另一种定义方法的方式 #

class Doctor
  define_method('perform_rhinoplasty') do |argument|
    "performing rhinoplasty on #{argument}"
  end

  define_method('perform_checkup') do |argument|
    "performing checkup on #{argument}"
  end

  define_method('perform_interpretive_dance') do |argument|
    "performing interpretive dance on #{argument}"
  end
end

doctor = Doctor.new
puts doctor.perform_rhinoplasty("nose") # => performing rhinoplasty on nose
puts doctor.perform_checkup("throat") # => performing checkup on throat
puts doctor.perform_interpretive_dance("in da club") # => performing interpretive dance on in da club

这种方式最大的特点就是可以通过 each 语句同时定义多个方法。以上示例可以写成以下形式:

class SuperDoctor
  ["rhinoplasty", "checkup", "interpretive_dance"].each do |action|
    define_method("perform_#{action}") do |argument|
      "performing #{action.gsub('_', ' ')} on #{argument}"
    end
  end
end

Inspect Method #

class Mock
  def add(x, y)
    x+y
  end

  private
  def info
    'info'
  end
end

mock = Mock.new
method_add = mock.method(:add)
p method_add
p method_add.parameters
# the object on which the method is bound
p method_add.receiver
# the class that object belongs to
p method_add.owner

p mock.methods
p mock.public_methods

method(sym) 用于获得对象拥有的方法 parameters() 用于获得方法对象的参数 receiver() 用于获得该方法绑定的对象 owner() 用于获得定义该方法的类

常量引用 #

const_get() 可以用于获得已定义的常量对象。不属于任何类的常量存在于 Module 对象中。

class Mock
  Foo = 'foo'
end

CONSTANT_A = 100
puts CONSTANT_A
puts Module.const_get('CONSTANT_A')
puts Mock.const_get('Foo')

由于类名通常也是常量,所以也可以通过这种方式创建对象

puts Module.const_get('Mock').new.add(2, 3)

成员变量引用 #

类似常量,成员变量使用 instance_variable_get() 进行引用。

p mock.instance_variable_get('@sum')

LifeCycle Hook #

元编程中可能需要监控方法的声明周期,这时需要使用 hook 方法。

method_added #

method_added 用于当类中增加了新的实例方法时被调用。

class Dojo
  @@method_added = []

  def self.method_added(method_name)
    puts "a new method '#{method_name}' has added"
    @@method_added << method_name
  end

  def self.all_added_methods
    "class contains #{@@method_added}"
  end

  def foo

  end

  def bar

  end
end

puts Dojo.all_added_methods

以上结果输出

a new method 'foo' has added
a new method 'bar' has added
class contains [:foo, :bar]

singleton_method_added #

singleton_method_added 用于监控单例方法的添加。

d1 = Dojo.new

def d1.singleton_method_added(method_name)
  puts "a new singleton method '#{method_name}' has added"
  @singleton_methods_added ||= []
  @singleton_methods_added << method_name
end

def d1.all_added_singleton_methods
  "instance contains #{@singleton_methods_added}"
end

def d1.inc(x)
  x+ 1
end

puts d1.inc(3)
puts d1.all_added_singleton_methods

以上输出

a new singleton method 'singleton_method_added' has added
a new singleton method 'all_added_singleton_methods' has added
a new singleton method 'inc' has added
4
instance contains [:singleton_method_added, :all_added_singleton_methods, :inc]

其它方法 #

与之类似还有定义方法的删除的 methods_removed 和方法未定义的 methods_undefined 以及这两个的单例版本。

模块或类的引入 #

included() 用于监控一个模块或类的引用情况。

module SparringArea
  @@included_into = []

  def self.included_into
    @@included_into
  end

  def self.included(class_or_module)
    puts "class_or_module '#{class_or_module}' is included"
    @@included_into << class_or_module
  end
end

include SparringArea
puts SparringArea.included_into

对象的扩展 #

extended() 用于监控对象从某一个模块中进行扩展。

class Dojo
end

module SparringArea
  @@extended_objects = []

  def self.extended_objects
    @@extended_objects
  end

  def self.extended(object)
    puts "object '#{object}' is extended"
    @@extended_objects << object
  end
end

dojo1 = Dojo.new
dojo2 = Dojo.new
dojo1.extend(SparringArea)
dojo2.extend(SparringArea)

继承 #

inherited() 用于监控一个类是否被继承。

class Room
  @@subclasses = []

  def self.subclasses
    @@subclasses
  end

  def self.inherited(subclass)
    puts "subclass '#{subclass}' inherits Room"
    @@subclasses << subclass
  end
end
class SmallRoom < Room
end
沪ICP备17055033号-2