我非常熟悉何时使用子类和模块,但最近我一直在看这样的嵌套类:
class Foo
class Bar
# do some useful things
end
end
除了嵌套在如下模块中的类:
module Baz
class Quux
# more code
end
end
文档和文章都很稀疏,或者我没有对这个主题进行足够的教育以找到合适的搜索术语,但我似乎无法找到关于该主题的更多信息。
有人可以提供关于为什么/何时使用这些技术的帖子的示例或链接?
答案 0 :(得分:123)
其他OOP语言有inner classes,如果不绑定到高级类,则无法实例化。例如,在Java中,
class Car {
class Wheel { }
}
只有Car
类中的方法才能创建Wheel
s。
Ruby没有那种行为。
在Ruby中,
class Car
class Wheel
end
end
与
不同class Car
end
class Wheel
end
仅限于班级Wheel
与Car::Wheel
的名称。这种名称差异可以使程序员明确Car::Wheel
类只能代表车轮,而不是普通车轮。在Ruby中嵌套类定义是一个优先考虑的问题,但它的目的在于它更强有力地强制执行两个类之间的契约,并且这样做会传达有关它们及其用途的更多信息。
但是对于Ruby解释器来说,它只是名称上的差异。
至于你的第二个观察,嵌套在模块内的类通常用于命名类。例如:
module ActiveRecord
class Base
end
end
与
不同module ActionMailer
class Base
end
end
虽然这不是嵌套在模块内部的类的唯一用法,但它通常是最常见的。
答案 1 :(得分:45)
在Ruby中,定义嵌套类与在模块中定义类类似。它实际上并不强制类之间的关联,它只是为常量创建一个命名空间。 (类和模块名称是常量。)
接受的答案对于任何事情都不正确。 1 在下面的例子中,我创建了一个词法封闭类的实例,而没有封闭类的实例。
class A; class B; end; end
A::B.new
优点与模块的优点相同:封装,仅在一个地方使用的分组代码,以及将代码放置在更接近使用位置的位置。一个大型项目可能有一个外部模块在每个源文件中反复出现,并包含许多类定义。当各种框架和库代码都执行此操作时,它们每个只为顶级提供一个名称,从而减少冲突的可能性。平淡无奇,但是这就是他们被使用的原因。
使用类而不是模块来定义外部命名空间可能在单文件程序或脚本中有意义,或者如果您已经使用顶级类,或者如果您实际上要添加代码来链接这些类以真正的内部类样式组合在一起。 Ruby没有内部类,但没有什么能阻止你在代码中创建相同的行为。从内部对象引用外部对象仍然需要从外部对象的实例中加入点,但是嵌套类将表明这是您可能正在做的事情。精心模块化的程序可能总是首先创建封闭类,并且可以合理地使用嵌套类或内部类进行分解。您无法在模块上调用new
。
即使是非常需要名称空间的脚本,您也可以使用通用模式,只是为了娱乐和练习......
#!/usr/bin/env ruby
class A
class Realwork_A
...
end
class Realwork_B
...
end
def run
...
end
self
end.new.run
答案 2 :(得分:13)
您可能希望将此类分组您的类用于模块。命名空间事物的排序。
例如Twitter gem使用命名空间来实现此目的:
Twitter::Client.new
Twitter::Search.new
因此,Client
和Search
类都位于Twitter
模块下。
如果您想查看来源,可以找到这两个类的代码here和here。
希望这有帮助!
答案 3 :(得分:3)
除了之前的答案: Ruby中的模块是一个类
$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object
答案 4 :(得分:2)
在2.5之前的Ruby中,嵌套类和嵌套模块之间还有另一个区别,就是我认为必须在此提及其他答案。这是查找过程。
简而言之:由于在2.5之前的Ruby中会进行顶级常量查找,因此如果您使用嵌套类,Ruby可能最终会在错误的位置(特别是select t.*
from table t
where exists (select 1 from table t1 where t1.amount = t.amount and t1.name <> t.name);
)查找嵌套的类。
在2.5之前的Ruby中:
嵌套的类结构:
假设您有一个类Object
,嵌套类X
或Y
。然后,您还有一个名为X::Y
的顶级类。如果未加载Y
,则在调用X::Y
时会发生以下情况:
在X::Y
中没有找到Y
的地方,Ruby会尝试在X
的祖先中查找它。由于X
是类而不是模块,因此它具有祖先,其中X
是祖先。因此,它尝试在[Object, Kernel, BasicObject]
中寻找Y
,并成功找到它。
它是顶层Object
而不是{{1} }。
您将收到以下警告:
Y
嵌套模块结构:
假设在上一个示例中,X::Y
是一个模块而不是一个类。
一个模块仅以其自身为祖先:warning: toplevel constant Y referenced by X::Y
会产生X
。
在这种情况下,Ruby将无法在X.ancestors
的祖先之一中寻找[X]
并将抛出Y
。之后,Rails(或具有自动加载功能的任何其他框架)将尝试加载X
。
有关更多信息,请参见本文:https://blog.jetbrains.com/ruby/2017/03/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications/
在Ruby 2.5中:
顶级常量查询已删除。
您可以使用嵌套类,而不必担心遇到此错误。