4

我想用一些方法编写小类,这些方法实际上属于其他类,那么如何在其他类中定义现有副本的方法。我相信这是我不明白的元编程魔术师。

class Foo
  def initialize
    # with blocks, I would just pass block, but this is methods
    # so this won't work
    Bar.class_eval(perform)
    Bar.class_eval(process)
    Bar.class_eval(save)
  end

  def perform
    1+1
  end

  def process
    # some code
  end

  def save
    # some code
  end
end

class Bar; end

foo = Foo.new
foo.perform
#=> 2
Bar.test
#=> 1

为什么我需要这个?我正在研究仅用三种方法上课的 gem。在初始化(将隐藏在父类中)时,它将将此方法传递给不同的类。我可以用块来做这个,但是用方法它就更干净了。

PS:这就像将方法从一个类复制到另一个类

PSS:或者......如何将方法转换为proc,所以我可以将它传递给class_eval

4

2 回答 2

14

要将方法转换为可以像 Proc 一样调用的东西,请使用obj.method(:method_name). 这将为您提供一个绑定的Method 对象,该对象在called 时将被调用 on obj。如果你想在同一个类的不同对象上调用它,你可以调用method.unbind.bind(different_obj).

这仍然不允许您将方法从一个类“复制”到另一个类。如果您想允许用户传递一个定义 3 个方法的类,而不是传递 3 个块,那么如果您在内部存储对该类(或其实例)的引用,并根据需要在其上调用方法,它可能会更好. 这就是评论“代表团”的人的意思。

或者,您可以让用户传递一个模块,并制作您自己的类includeextend模块(根据需要)。

于 2012-02-15T13:43:08.283 回答
2

可以将方法从一个类复制到另一个类,但有一个主要警告:目标类必须是 kind_of?源类,或者源必须是模块。此限制部分记录在UnboundMethod#bind的文档中,但要查看模块异常,您必须查看该方法的源代码。该答案包含有关该主题的更多讨论。

这是一个例子:

module A
  def say_hello
    puts "Hello"
  end
end

class B
  define_method(:say_hello, A.instance_method(:say_hello))
end

b = B.new
b.say_hello
=> Hello

如果A是一个类并且B是一个继承自A. 但在那种情况下,您已经通过继承获得了该方法,所以我个人认为它没有用处。

我发现这种模式很有用的一种情况是在设计 DSL 时使用继承自BasicObject. 由于BasicObjectinclude Kernel喜欢这样Object做,因此您无法轻松访问许多有用的方法,例如#instance_variables甚至#class. 但是您可以将它们从内核单独复制到您的类中:

class Foo < BasicObject
  define_method(:class, ::Kernel.instance_method(:class))
end

f = Foo.new
puts f.class
=> Foo
于 2018-08-23T21:48:58.563 回答