为什么我们要在Ruby中的类中放置一个模块?

时间:2010-08-23 00:52:42

标签: ruby namespaces

在Ruby中,我发现为了命名空间而将类放在模块中会很有用。我也看到可以将模块放在类中。但我不明白你为什么这样做。

模块通常混合成类,对吗?那么,在类中定义模块的目的是什么?

4 个答案:

答案 0 :(得分:52)

我们可以在编写像这样的猿类代码时使用它:

class DrugDealer
  module Drug
    def happy?; true; end
  end

  def approach(victim)
    victim.extend Drug
  end
end

o = Object.new
DrugDealer.new.approach(o)
o.happy? # => true

另一个在现实世界中更实用的例子是使用仅由子类应用的mixins。

当一些事物的某些 facets 适用于某些子类而其他方面适用于其他子类时,这很有用,而这些方面的应用方式没有足够的顺序为明确的类层次结构让路(树)。想多重继承!一个简化的例子:

class Person
  def handshake
    :sloppy
  end

  def mind_contents
    :spam
  end

  module Proper
    def handshake
      :firm
    end
  end

  module Clever
    def mind_contents
      :theories
    end
  end
end

class Professor < Person
  include Proper
  include Clever

  # ...
end

等等。有点好,当合理使用时。即使是超级调用和构造函数(我之前没有定义任何内容)也按照我希望的方式遍历所有mixins和类。

答案 1 :(得分:7)

我已经遇到了具有复杂命名空间的大型Rails应用程序中的用例。一个简化的例子:

# app/models/invoice/dependents/item.rb
class Invoice
  module Dependents
    class Item
      # Define invoice item
    end
  end
end

这里Invoice是它自己的一个类,但对于它的依赖项也是一个很好的命名空间。我们不能说module Invoice因为该常量已经被定义为一个类,但我们仍然可以将它用作命名空间。

巨人警告

如果您使用类作为命名空间,并且您正在使用Rails,请确保不要在其他地方意外声明该类。自动加载会破坏你的一天。例如:

# app/helpers/invoice/dependents/items_helper.rb
class Invoice       # This line will cause you grief
  module Dependents
    module ItemsHelper
      # view helper methods
    end
  end
end

此文件中声明class Invoice的事实会创建加载顺序依赖项;如果此文件的class Invoice行在您的预期类定义之前执行,您的预期类定义可能无法正常运行。在此示例中,如果已经声明Invoice没有父类,我就无法声明ActiveRecord::Base sublcasses Invoice

可能要求你的“真正的”类定义文件位于另一个文件的顶部,但至少在Rails自动加载方案中,如果你这样做,你将会有更少的争吵:

# app/helpers/invoice/dependents/items_helper.rb
module Invoice:Dependents::ItemsHelper  
  # view helper methods
end

使用这种语法,Rails将看到Invoice常量并使用自动加载查找,在模型文件中找到它并按照您的预期方式定义它。

答案 2 :(得分:6)

class Image
    module Colors
        Red = ...
        Blue = ...
    end
    include Colors
end

include Image::Colors

Image.new.set_pixel x, y, Red 

答案 3 :(得分:3)

我想这真的只是将一个类用作命名空间,有时将一切放在一个模块中会更方便。我从来没有在实践中看到过,但无论如何,它都是完全有效的Ruby代码。

我能想到的唯一真实场景是在课堂上使用EventMachine:

class Api
  def initialize
    EM.start_server "0.0.0.0", 8080, Server
  end

  module Server
    def receive_data (data)
      # do stuff
    end
  end
end