我遇到的模块问题

时间:2010-02-09 00:10:44

标签: ruby-on-rails ruby

我正在尝试在模块中定义静态变量和方法,这些变量和方法将被许多类扩展/使用。以下示例演示:

module Ammunition
  def self.included(base)    
    base.class_eval("@@ammo = [bullets]") 
  end

  def unload
    p @@ammo #<-- doesn't work
  end  
end

class Tank
  include Ammunition
  @@a += [shells]
end

class Airplane
  include Ammunition  
  @@a += [missiles, photon_torpedoes]
end

Tank.new.unload
Airplane.new.unload

这不起作用,因为弹药由于某种原因不知道如何在类的上下文中评估@@ ammo(我原来认为模块的行为就像包含文件一样)。我必须将'卸载'复制到每个类,我现在正在做,但我想干它b / c我还有很多其他方法可以添加到模块中。

连连呢?合理的解决方案是在类的上下文中评估'unload'而不是模块(但是如何在Ruby中执行此操作?)

谢谢!

3 个答案:

答案 0 :(得分:4)

类变量可以奇怪地工作,这个用法显示了off。 @@ammo的范围是什么? AmmunitionTank有自己的副本吗?事实证明@@ammo的范围是模块,包含它的类可以简单地访问它。

module Ammunition
  def self.included(base)    
    base.class_eval do
      puts "@@ammo was: #{defined?(@@ammo) ? @@ammo.join(',') : 'nil'}"
      @@ammo = ['bullets']
      puts "@@ammo is now: #{@@ammo}"
      puts '---'
    end
  end

  def unload
    @@ammo
  end  
end

class Tank
  include Ammunition
  @@ammo += ['shells']
end

class Airplane
  include Ammunition  
  @@ammo += ['missiles', 'photon_torpedoes']
end

puts "Tank unloaded: #{Tank.new.unload.join(', ')}"
puts "Airplane unloaded: #{Airplane.new.unload.join(', ')}"

这会产生:

@@ammo was: nil
@@ammo is now: bullets
---
@@ammo was: bullets,shells
@@ammo is now: bullets
---
Tank unloaded: bullets, missiles, photon_torpedoes
Airplane unloaded: bullets, missiles, photon_torpedoes

Tank包含模块时,它会将@@ammo从nil设置为包含项目符号的数组。当Airplane包含模块时,它会覆盖我们刚刚设置的弹药值。


以下是您要做的事情

module Ammunition
  def self.included(base)    
    base.class_eval do
      include Ammunition::InstanceMethods
      extend  Ammunition::ClassMethods
      @ammo = ['bullets']
    end
  end

  module ClassMethods
    def ammo
      @ammo
    end
  end

  module InstanceMethods
    def unload
      self.class.ammo.join(',')
    end
  end
end

class Tank
  include Ammunition
  @ammo += ['shells']
end

class Airplane
  include Ammunition  
  @ammo += ['missiles', 'photon_torpedoes']
end

puts "Tank unloaded: #{Tank.new.unload}"
puts "Airplane unloaded: #{Airplane.new.unload}"

类可以有实例变量,它们的范围更容易理解。将模块分成实例和类方法可以为两者提供功能。此代码段生成以下输出

Tank unloaded: bullets,shells
Airplane unloaded: bullets,missiles,photon_torpedoes

答案 1 :(得分:3)

嗯,首先......解释@@变量如何正常工作是一个非常好的主意。

@@变量是可以在实例上下文中访问的类变量,例如:

class Klass

  def my_klass_variable=(str)
    # self here points to an instance of Klass
    @@my_klass_variable = str
  end

  def my_klass_variable
    @@my_klass_variable
  end

end

Klass.new.my_klass_variable = "Say whaat?"
# Note this is a different instance
Klass.new.my_klass_variable # => "Say whaat?" 

然而,这种类型的变量也会产生以下结果:

class OtherKlass < Klass; end

Klass.new.my_klass_variable = "Howdy"
# Note this is a different instance, and from the child class
OtherKlass.new.my_klass_variable # => "Howdy"

疯狂的行为确实如此。创建类变量的另一种方法是在以self.开头的方法上定义实例变量。例如:

class Klass 
  def self.my_class_method
    @class_var = "This is a class var"
  end
end

为什么@也适用于类变量?请记住,Klass中的ClassKlass类的实例,它将拥有自己的实例变量,最后将是{{1的实例的类变量}}

Klass.class # => Class
Klass.instance_of?(Class) # => true
k = Klass.new
k.class # => Klass
k.instance_of?(Klass) # => true

这对类变量更安全(因为它们将具有变量的一个副本,而不是具有子类的共享副本),并且在使用您的示例时将表现为您期望的行为:

module Ammunition

  def self.included(base)    
    base.class_eval do
      @ammo = [bullets] # where bullets come from any way?
    end
  end

  def self.unload
    p @ammo
  end

end

class Tank
  include Ammunition # Probably you meant that instead of Packagable
  @ammo += [shells] # I think you meant @ammo instead of @a
end

class Airplane
  include Ammunition # Probably you meant that instead of Packagable
  @ammo += [missiles, photon_torpedoes] # I think you meant @ammo instead of @a
end

其他人指出的代码不起作用(假设没有炮弹,导弹或photo_torpedoes),但我认为你可以弄明白如何让它自己运作。

答案 2 :(得分:0)

一些问题:

(1)模块名称ammunition必须以大写字母Ammunition

开头

(2)您在课程中加入Packagable,但我认为您的意思是Ammunition

(3)您的所有变量 - missilesphotonphoton_torpedos都未定义,因此您的代码实际上并未运行。

我建议你先修复这段代码:)但是除此之外,类变量@@myvar在大多数Rubyists中都被认为是禁忌。