5

在 Ruby 中,我正在尝试创建一个类,该类基于初始化期间给出的值将从以下模块之一继承。我想创建一个基础模块,这两个模块都从中继承,其中包含使用在继承它的模块中定义的常量的通用方法。例子:

module BaseMod
  def what_am_i
    puts OUTPUT
  end
end

module Tall
  OUTPUT = "I am tall"
  include BaseMod
end

module Short
  OUTPUT = "I am short"
  include BaseMod
end

class Person
  def initialize type
    if type =~ /short/i
      extend Short
    else
      extend Tall
    end
  end
end

p = Person.new "short"
p.what_am_i

我的问题是,当调用“p.what_am_i”时,出现以下错误:

NameError: uninitialized constant BaseMod::OUTPUT
  const_missing at org/jruby/RubyModule.java:2642
      what_am_i at test_logic2.rb:3
         (root) at test_logic2.rb:28

我也想知道是否有更好的方法来做这件事。

4

4 回答 4

4
module BaseMod
  def what_am_i
    puts self.class::OUTPUT
  end
end

module Tall
  OUTPUT = "I am tall"
  include BaseMod
end

module Short
  OUTPUT = "I am short"
  include BaseMod
end

class Person
  def initialize(type)
    if type =~ /short/i
      self.class.send(:include, Short)
    else
      self.class.send(:include, Tall)
    end
  end
end

p = Person.new "short"
p.what_am_i

编辑:上面的代码实际上不起作用:

p = Person.new "short"
p.what_am_i
>> I am short
p = Person.new "tall"
p.what_am_i
>> I am tall
p = Person.new "short"
p.what_am_i
>> I am tall

这是另一个尝试:

module BaseMod
  def self.included(base)
    base.send(:define_method, :what_am_i) do
      puts base::OUTPUT
    end
  end
end

module Tall
  OUTPUT = "I am tall"
  include BaseMod
end

module Short
  OUTPUT = "I am short"
  include BaseMod
end

class Person
  def initialize type
    if type =~ /short/i
      extend Short
    else
      extend Tall
    end
  end
end

p = Person.new "short"
p.what_am_i
p = Person.new "tall"
p.what_am_i
p = Person.new "short"
p.what_am_i
于 2012-07-17T22:30:53.130 回答
2

要在您的情况下获得常量,您必须编写如下内容:

module Tall
 ::OUTPUT = "I am tall"
 include BaseMod
end

但是请注意,您正在使用 Short 模块的声明重新定义常量。为此,您将永远得到“我很矮”。

所以要正确地做到这一点,你应该尝试:

module BaseMod
 OUTPUT="Before"
 def what_am_i
  puts OUTPUT
 end
end

module Tall
 def self.extended(k)
  OUTPUT.replace  "I am tall"
 end
 include BaseMod
end

module Short
 def self.extended(k)
  OUTPUT.replace "I am short"
 end
 include BaseMod
end

ķ

于 2012-07-17T01:00:08.670 回答
1

我将再提出一个选项。我不太确定您的复杂的真实案例是什么,所以这是我的选择:

module BaseMod
  def what_am_i
    puts output
  end
end

module Tall
  include BaseMod
  def self.extended klass
    define_method :output do
      "I am tall"
    end
  end
end

module Short
  include BaseMod
  def self.extended klass
    define_method :output do
      "I am short"
    end
  end
end

class Person
  def initialize type
    extend (type =~ /short/i ? Short : Tall ) # Because I didn't wanna type all those lines
  end
end

p = Person.new "short"
p.what_am_i

请注意,对于这种情况,您可以轻松地执行以下操作:

module Tall
  include BaseMod
  def output
    "I am tall"
  end
end

但我不知道这是否真的对你有帮助。

于 2012-07-17T15:39:35.277 回答
0

似乎当您使用#what_am_i 消息向您的人p 发送消息时,解释器在类祖先中不断地寻找方法实现,最终在BaseMod 中找到它,但在该级别,OUTPUT 常量不再定义。所以我认为 Ruby 继续在层次结构中向上寻找 OUTPUT 常量,但不会考虑在定义它的 Tall 和 Short 模块中向下看。士气是,即使您包含许多子模块,它们也不会进入一个堆,每个人都可以访问所有常量,而是将它们的层次结构保持在它们包含的相反顺序(参见 Tall.Ancestors)。在任何级别,只能访问相同级别或更高级别的常量。我将通过以下方式解决您的问题:

module Personhood
  def what_am_i; @output end
end

class Tall
  include Personhood
    def initialize
      @output = "I am tall"
    end
  end
end

class Short
  include Personhood
    def initialize
      @output = "I am short"
    end
  end
end

def Person( type )
  if type =~ /short/i
    Short.new
  else
    Tall.new
  end
end

pete = Person "short"
pete.what_am_i
=> I am short

我处理了一个常量以支持实例变量。在 Ruby 中,无论如何都没有真正的常量。Tall 和 Short 是类,Person 是一个构造方法,它根据输入返回 Tall 或 Short 类。这就是我觉得应该这样做的方式。

于 2012-07-17T03:19:57.813 回答