在Ruby中,我发现为了命名空间而将类放在模块中会很有用。我也看到可以将模块放在类中。但我不明白你为什么那样做。
模块通常混合成类,对吧?那么,在类中定义模块的目的是什么?
我们可以在编写像这样的类似猿的代码时使用它:
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
在现实世界中更实用的另一个例子是具有仅由子类应用的mixin。
当一个东西的某些方面适用于某些子类而其他方面适用于其他子类时,这很有用,而这些方面的应用方式没有足够的顺序来为明确的类层次结构(树)让路。想多重继承!一个简化的例子:
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
等等。有点好,当合理使用时。即使是超级调用和构造函数(我之前没有定义任何内容)也会按照我希望的方式遍历所有的mixin和类。
我已经遇到了一个具有复杂命名空间的大型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
常量并使用自动加载查找它,在模型文件中找到它并按照您的预期方式定义它。
class Image
module Colors
Red = ...
Blue = ...
end
include Colors
end
include Image::Colors
Image.new.set_pixel x, y, Red
我想这真的只是将一个类用作命名空间,有时候将所有东西放在一个模块中会更方便。我在实践中从未见过它,但它无论如何都是完全有效的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