何时使用嵌套的类和嵌套在模块中的类?

我非常熟悉什么时候使用子类和模块,但是最近我看到了这样的嵌套类:

class Foo
class Bar
# do some useful things
end
end

以及像下面这样嵌套在模块中的类:

module Baz
class Quux
# more code
end
end

要么文档和文章很少,要么我没有受过这方面的教育,无法搜索到正确的搜索词,但我似乎找不到关于这个主题的太多信息。

有没有人能提供一些例子或链接,说明为什么/什么时候会使用这些技术?

85753 次浏览

你可能想用这个来把你的类 小组成一个模块。类似于名称空间的东西。

例如,推特上的宝石使用名称空间来实现:

Twitter::Client.new


Twitter::Search.new

因此,ClientSearch类都位于 Twitter模块之下。

如果要检查源代码,可以在 给你给你中找到这两个类的代码。

希望这个能帮上忙!

其他 OOP 语言有 内部课程,如果不绑定到上层类,就不能实例化它。例如,在 Java 中,

class Car {
class Wheel { }
}

只有 Car类中的方法可以创建 Wheel

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

虽然这不是嵌套在模块中的类的唯一用法,但它通常是最常见的。

在 Ruby 中,定义嵌套类类似于在模块中定义类。它实际上并不强制类之间的关联,它只是为常量创建一个名称空间。(类和模块名称是常量。)

接受的答案对任何事都不正确。在下面的示例中,我创建了一个词法封闭类的实例,但是封闭类的实例从未存在过。

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

在先前答案的补充中: 模块是一个类

$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object

在 Ruby 2.5之前,嵌套类和嵌套模块之间还有另一个区别,其他的答案没有涵盖这一点,我觉得这里必须提到。这是一个查找过程。

简而言之: 由于 Ruby 在2.5之前的顶级常量查找,如果使用嵌套类,Ruby 最终可能会在错误的位置(特别是在 Object中)查找嵌套类。< br > < br > 在 Ruby 2.5之前:
嵌套类结构: 假设您有一个具有嵌套类 YX::Y的类 X。然后有一个顶级类,也叫做 Y。如果没有加载 X::Y,那么在调用 X::Y时会发生以下情况: < br > < br > 由于没有在 X中找到 Y,Ruby 将尝试在 X的祖先中查找它。因为 X是一个类而不是一个模块,所以它有祖先,其中包括 [Object, Kernel, BasicObject]。因此,它尝试在 Object中寻找 Y,在那里它成功地找到了它。然而它是顶级的 Y而不是 X::Y。 你会得到这样的警告:

warning: toplevel constant Y referenced by X::Y


嵌套模块结构: 假设在前面的示例中,X是一个模块,而不是一个类。模块只有它自己作为祖先: X.ancestors将产生 [X]。< br > < br > 在这种情况下,Ruby 将无法在 X的一个祖先中查找 Y,而是抛出一个 NameError。Rails (或任何其他具有自动加载的框架)将尝试在此之后加载 X::Y

有关更多信息,请参见本文: https://blog.jetbrains.com/ruby/2017/03/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications/ < br > < br > 在 Ruby 2.5中:
顶级常量查找被删除。 < br > 您可以使用嵌套类,而不用担心会遇到这个 bug。