既可以作为类方法调用又作为类方法混合的方法?

时间:2015-04-17 17:39:41

标签: ruby mixins

在Ruby中,是否可以定义一个可以作为类方法直接调用的方法,并且也可以作为类方法混合使用?也就是说,不使用self.includedself.extended来创建等效的类或实例方法。

这两种方法都不起作用:

module A
  def foo(s)
    puts s
  end
end

class One
  extend A
end

One.foo("one")
#A.foo("a")

module B
  def self.foo(s)
    puts s
  end
end

class Two
  include B
end

#Two.foo("two")
B.foo("b")

对于被问到的内容似乎有些混乱。这是一个不那么抽象的场景。 A是可以直接使用的mixin。 B是一种混合物,旨在独立于A使用,“包裹”A的一种方法。

module A
  # #foo has to be defined in order to be mixed in via `extend`.
  # Being mixed in via `include` has the same issue but inverted.
  def foo(s) A.foo(s) end 
  def self.foo(s) puts "A: " + s end
end

module B
  def foo(s) A.foo("B: " + s) end
end

class One; extend A end

class Two; extend B end

One.foo("one")
Two.foo("two")

为了实现这一点,必须单独定义A#fooA::fooModule#module_function等现有设施在此方案下无效。

2 个答案:

答案 0 :(得分:2)

我会尽量不确定,但据我所知,你问题的答案是否定的。如果你想混合使用实例和类方法,那么标准方法就是这样的:

module A 
  def self.included(base)
    #this will extend the class you included A in
    #using A::ClassMethods definition
    base.extend(ClassMethods)
  end
  #these methods will be added as class_methods to any class
  #that includes A
  module ClassMethods
    def foo(s)
      "You fooed the class with #{s}"
    end
  end
  #this will be added as an instance method as it would be in a standard include
  def bar(s)
    "You barred an instance with #{s}"
  end
end

class Mixed
  include A
end
Mixed.foo("Hello")
#=> "You fooed the class with Hello"
Mixed.new.bar("Hello")
#=> "You barred an instance with Hello"

我希望这能回答你的问题,因为你的意图有点不清楚。由于你的问题似乎不需要实例方法,你也可以这样做

module A 
  def foo(s)
    "called foo with #{s}"
  end
end
module B
  include A
  alias_method :a_foo, :foo
  def foo(s)
    "B called foo from A #{a_foo(s)}"
  end
end
class Mixed
  extend B
end

Mixed.foo("Mixed")
#=>"B called foo from A called foo with Mixed"

再次更新

这是一个奇怪的模式,但它适用于我相信的用例

module A
  def foo(s)
    "fooed with #{s}"
  end
  def bar(s)
    "barred with #{s}"
  end
end

module B
  include A
  included_modules.each do |mod|
    (mod.instance_methods - Object.methods).each do |meth|
      alias_method "#{mod.name.downcase}_#{meth}", meth
    end
  end
end

class Mixed
  extend B
end

Mixed.methods - Object.methods 
#=> [:a_foo, :a_bar, :foo, :bar]

通过这种方式,您可以覆盖B中的方法并调用A版本,但如果您不覆盖,则仍会调用A版本。

如果您希望将此功能设为通用

,您还可以修补Module课程
class Module
  def include_with_namespace(*mods)
    #Module#include runs in reverse so to maintain consistency my patch does as well
    mods.reverse.each do |mod|
      include mod
      (mod.instance_methods - Object.methods).each do |meth|
        alias_method "#{mod.name.downcase}_#{meth}", meth
      end
    end
  end
end 

然后这将起作用

module C
  def foo(s)
    "C's foo with #{s}"
  end
  def see_me
    "You can see C"
  end 
end

module B;include_with_namespace A, C; end

class Mixed;extend B;end

Mixed.methods - Object.methods
#=> [:a_foo, :a_bar, :c_foo,:c_see_me, :foo, :bar, :see_me]
Mixed.foo("name")
#=> "fooed with name"
Mixed.c_foo("name")
#=> "C's foo with name"

答案 1 :(得分:0)

你可以写:

module A
  def foo(s)
    puts s
  end
end

class One
  singleton_class.include A
end

One.foo('hi')
  #=> 'hi'

但这非常接近Object#extend

如果:

module A
  def self.foo(s)
    puts s
  end
end

并且您想知道是否可以从类A引用C以使foo成为C的类方法,我的理解是答案是“没有”。我说“我的理解”,因为它不是可以证明的东西;据我所知,这是Matz做出的一项设计决定。