这是模块的问题

时间:2022-05-02 10:38:20

I'm trying to define a static variable and methods in a module that will be extended/used by numerous classes. The following example demonstrates:

我正在尝试在模块中定义一个静态变量和方法,它将被许多类扩展/使用。下面的例子演示了:

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

This doesn't work because ammunition doesn't know how to evaluate @@ammo in the context of the class for some reason (I original thought the module would behave just like an include file). I would have to copy 'unload' to each class, which I'm doing right now, but I want to DRY it up b/c I have many other methods to add to the module.

这不起作用,因为由于某些原因,ammunition不知道如何在类的上下文中计算@@ammo(我最初认为这个模块的行为就像一个include文件)。我将不得不将'unload'复制到每个类,我现在正在做这个,但是我想让它干涸b/c我还有很多其他方法要添加到模块中。

Suggestions? The reasonable solution would be to evaluate 'unload' in the context of the class and not the module (but how to do this in Ruby?)

建议吗?合理的解决方案应该是在类而不是模块的上下文中评估‘卸载’(但是如何在Ruby中实现呢?)

Thanks!

谢谢!

3 个解决方案

#1


4  

class variables can work strangely, and this use shows that off. What is the scope of @@ammo? Ammunition or does Tank have its own copy of it? It turns out that @@ammo is scoped by the module, and the classes that include it can simply access it.

类变量可以奇怪地工作,这个用法显示了这一点。@ammo的作用范围是什么?弹药还是坦克有自己的副本?事实证明,@@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(', ')}"

This produces:

这产生:

@@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

When Tank includes the module, it sets @@ammo from nil to an array with bullets in it. When Airplane includes the module, it overwrites the ammo value we just set.

当Tank包含模块时,它将@@ammo从nil设置为一个数组,其中包含子弹。当plane包含模块时,它会覆盖我们刚刚设置的ammo值。


Here is what you want to do

这是你想做的。

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}"

Classes can have instance variables, and their scope is easier to understand. And separating your module into instance and class methods allow you to provide functionality to both. This snippet generates the following output

类可以有实例变量,它们的作用域更容易理解。将模块分离到实例和类方法允许您同时为这两种方法提供功能。此代码段生成以下输出

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

#2


3  

Well, first of all... it's a really good idea to explain how @@ variables work exactly.

嗯,首先……解释@@变量如何工作确实是个好主意。

@@ variables are class variables that can be accessed on the instance context, say for example:

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

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?" 

However this type of variables will incur also in the following result:

但是,这种类型的变量也会导致以下结果:

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"

Crazy behavior indeed. Another way to create Class variables, is defining instance variables on a method that starts with self.. For example:

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

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

Why a @ for class variables as well? Remember that Klass in this is an instance of the Class class, this will have its own instance variables, that at the end will be class variables for instances of Klass.

为什么类变量也有@ ?记住,Klass是类的实例,它有自己的实例变量,最后是Klass实例的类变量。

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

This is more safe for class variables (as they will have one copy of the variable, and not a shared one with child classes as well), and will behave as you are expecting to behave when using your example:

这对于类变量更安全(因为它们将有一个变量的副本,而不是与子类共享的副本),并且当使用示例时,它们的行为将与预期的一样:

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

This code as pointed by others won't work (given there is no shells, missiles nor photo_torpedoes), but I think you can figure it out how to make it work by yourself.

其他人指出的这个代码不会起作用(考虑到没有炮弹、导弹和光电鱼雷),但我认为您可以自己找出如何使它工作。

#3


0  

A few issues:

几个问题:

(1) The module name ammunition must start with a capital -- Ammunition

(1)模块名称“弹药”必须以大写字母——“弹药”开头

(2) you're including Packagable into your classes but i assume you mean Ammunition ?

(2)你在你的课程中加入了Packagable,但我猜你指的是弹药?

(3) all of your variables - missiles, photon and photon_torpedos are undefined, so your code does not actually run.

(3)您的所有变量——导弹、光子和光子鱼雷都没有定义,因此您的代码实际上并不运行。

I suggest you first fix this code :) But as an aside, class variables @@myvar are considered a no-no among most Rubyists.

我建议您先修复这段代码:)但是作为一个备用的,类变量@@myvar被认为是大多数Rubyists中的一个禁忌。

#1


4  

class variables can work strangely, and this use shows that off. What is the scope of @@ammo? Ammunition or does Tank have its own copy of it? It turns out that @@ammo is scoped by the module, and the classes that include it can simply access it.

类变量可以奇怪地工作,这个用法显示了这一点。@ammo的作用范围是什么?弹药还是坦克有自己的副本?事实证明,@@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(', ')}"

This produces:

这产生:

@@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

When Tank includes the module, it sets @@ammo from nil to an array with bullets in it. When Airplane includes the module, it overwrites the ammo value we just set.

当Tank包含模块时,它将@@ammo从nil设置为一个数组,其中包含子弹。当plane包含模块时,它会覆盖我们刚刚设置的ammo值。


Here is what you want to do

这是你想做的。

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}"

Classes can have instance variables, and their scope is easier to understand. And separating your module into instance and class methods allow you to provide functionality to both. This snippet generates the following output

类可以有实例变量,它们的作用域更容易理解。将模块分离到实例和类方法允许您同时为这两种方法提供功能。此代码段生成以下输出

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

#2


3  

Well, first of all... it's a really good idea to explain how @@ variables work exactly.

嗯,首先……解释@@变量如何工作确实是个好主意。

@@ variables are class variables that can be accessed on the instance context, say for example:

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

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?" 

However this type of variables will incur also in the following result:

但是,这种类型的变量也会导致以下结果:

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"

Crazy behavior indeed. Another way to create Class variables, is defining instance variables on a method that starts with self.. For example:

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

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

Why a @ for class variables as well? Remember that Klass in this is an instance of the Class class, this will have its own instance variables, that at the end will be class variables for instances of Klass.

为什么类变量也有@ ?记住,Klass是类的实例,它有自己的实例变量,最后是Klass实例的类变量。

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

This is more safe for class variables (as they will have one copy of the variable, and not a shared one with child classes as well), and will behave as you are expecting to behave when using your example:

这对于类变量更安全(因为它们将有一个变量的副本,而不是与子类共享的副本),并且当使用示例时,它们的行为将与预期的一样:

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

This code as pointed by others won't work (given there is no shells, missiles nor photo_torpedoes), but I think you can figure it out how to make it work by yourself.

其他人指出的这个代码不会起作用(考虑到没有炮弹、导弹和光电鱼雷),但我认为您可以自己找出如何使它工作。

#3


0  

A few issues:

几个问题:

(1) The module name ammunition must start with a capital -- Ammunition

(1)模块名称“弹药”必须以大写字母——“弹药”开头

(2) you're including Packagable into your classes but i assume you mean Ammunition ?

(2)你在你的课程中加入了Packagable,但我猜你指的是弹药?

(3) all of your variables - missiles, photon and photon_torpedos are undefined, so your code does not actually run.

(3)您的所有变量——导弹、光子和光子鱼雷都没有定义,因此您的代码实际上并不运行。

I suggest you first fix this code :) But as an aside, class variables @@myvar are considered a no-no among most Rubyists.

我建议您先修复这段代码:)但是作为一个备用的,类变量@@myvar被认为是大多数Rubyists中的一个禁忌。