在Ruby模块中继承常量

时间:2012-07-17 00:05:10

标签: ruby inheritance module

在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 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

要在你的情况下获得常数,你必须写下这样的东西:

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

但请注意,您正在使用模块Short的声明重新定义Constant。为此,你总会得到“我很矮”。

为了正确地做,你应该尝试:

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

ķ

答案 2 :(得分: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

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

答案 3 :(得分: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是make类,Person是一个构造函数方法,它根据输入返回Tall或Short类。我觉得应该这样做。