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

时间:2011-06-01 02:37:58

标签: ruby oop

我非常熟悉何时使用子类和模块,但最近我一直在看这样的嵌套类:

class Foo
  class Bar
    # do some useful things
  end
end

除了嵌套在如下模块中的类:

module Baz
  class Quux
    # more code
  end
end

文档和文章都很稀疏,或者我没有对这个主题进行足够的教育以找到合适的搜索术语,但我似乎无法找到关于该主题的更多信息。

有人可以提供关于为什么/何时使用这些技术的帖子的示例或链接?

5 个答案:

答案 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

仅限于班级WheelCar::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

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

如果您想查看来源,可以找到这两个类的代码herehere

希望这有帮助!

答案 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,嵌套类XY。然后,您还有一个名为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中:
顶级常量查询已删除。
您可以使用嵌套类,而不必担心遇到此错误。