【问题标题】:Ruby class loading mechanismRuby 类加载机制
【发布时间】:2013-04-18 20:51:06
【问题描述】:

我是从 Ruby 编程语言开始的,我有兴趣在开始研究 Rails 框架之前深入了解它。

我目前有点失望,因为每个人都只关心 Rails 框架,而语言的其他方面只是没有深入讨论,例如它的类加载机制。

考虑到我开始做一些桌面/控制台实验,我想更好地理解以下事项:

  1. 将每个 Ruby 类放在单独的 Ruby 文件中是否是一种好习惯? (*.rb)
  2. 如果我有,假设 .. 10 个类 .. 并且它们都相互引用,通过相互实例化并调用彼此的方法,我是否应该在每个文件中添加“require”语句来说明需要哪些类由该文件中的类? (就像我们在每个 Java 类文件中使用“import”语句一样?)
  3. 在类声明之前或之后(内部)放置“require”语句有区别吗?
  4. 什么可以被认为是合适的 Ruby 程序的“入口点”?在我看来,任何 .rb 脚本都足够了,因为该语言没有像 C 或 Java 这样的约定,我们总是需要方法的“主”函数。
  5. 类加载是否被视为 Ruby 程序执行中的一个“阶段”?我们是否应该一开始就加载应用程序所需的所有类?
  6. 当我们运行需要类的代码时,解释器本身不应该负责查找和加载类吗?通过搜索 $LOAD_PATH 变量中的路径,就像 Java 使用 $CLASSPATH 一样?

谢谢。

【问题讨论】:

  • 问题太多了。请将它们减少到您真正想知道的两三个。 Stack Overflow 更喜欢每页一个,但几个密切相关的也可以。六个……呃……没那么多。
  • 关于这么多人喜欢 Rails 的事实......它很华而不实。在我工作的地方,我们不使用 Rails,而是使用纯 Ruby 编写内部后端代码。如果我们需要 Web 服务,它可以在 Sinatra 中完成。

标签: ruby


【解决方案1】:

一般来说,为每个 Ruby 类创建一个单独的 .rb 文件是一种很好的做法,除非这些类具有实用程序性质并且太琐碎而无法保证分离。这种情况的一个实例是自定义的 Exception 派生类,将其放在单独的文件中会比它的价值更麻烦。

传统认为类名和文件名是相关的。类被称为ExampleClass,文件被称为example_class,相同的“下划线”版本。有时你会违反这个约定,但只要你对此保持一致,就不会有问题。如果您遵循惯例,Rails ActiveSupport 自动加载器将帮助您很多,因此很多人都遵循这种做法。

同样,您需要将应用程序组织到 libbin 等文件夹中,以将命令行脚本与后端库分开。命令行脚本通常没有 .rb 扩展名,而库应该有。

对于require,应该谨慎使用。如果你正确地构建你的库文件,一旦你在顶层调用require,它们都可以自动加载。这是通过autoload 功能完成的。

例如,lib/example_class.rb 可能看起来像:

class ExampleClass
  class SpecialException < Exception
  end

  autoload(:Foo, 'example_class/foo')

  # ...
end

您可以在单独的目录或文件下组织其他内容,例如 lib/example_class/foo.rb,其中可能包含:

class ExampleClass::Foo
  # ...
end

您可以一直链接autoloads。这样做的好处是只加载实际引用的模块。

有时您可能希望将require 推迟到类实现内部的某个位置。如果您想避免在一个繁重的库中加载,除非使用了某个特定功能,而在一般情况下不太可能使用此功能,这将非常有用。

例如,您可能不想加载 YAML 库,除非您正在进行一些调试:

def debug_export_to_yaml
  require 'yaml'

  YAML.dump(some_stuff)
end

如果您查看常见 Ruby gem 的结构,“入口点”通常是您的库的顶层或包含该库的实用程序脚本。因此,以ExampleLibrary 为例,您的入口点将是lib/example_library.rb,其结构将按需包含其余部分。您可能还有一个脚本 bin/library_tool 可以为您执行此操作。

至于什么时候加载,如果一个东西被使用的机会很大,就提前加载,提前付出代价,所谓的“急加载”。如果它被使用的可能性很小,请按需加载它,或者让它保持“延迟加载”的状态。

查看一些简单但流行的 gem 的来源,了解大多数人如何构建他们的应用程序。

【讨论】:

    【解决方案2】:

    我会尽力帮助你解决第一个问题:

    1. 将每个 Ruby 类放在单独的 Ruby 文件中是否是一种好习惯? (*.rb)

    归结为这些类的相关程度。让我们看几个例子。看这个班:https://github.com/resque/resque/blob/master/lib/resque.rb ,它"imports" 几个类的功能,虽然它们一起工作,但它们并没有密切相关,要捆绑在一起。

    另一方面,看看这个模块:https://github.com/resque/resque/blob/master/lib/resque/errors.rb。它捆绑了 5 个不同的类,但它们确实属于同一类,因为它们本质上都代表相同的。

    此外,从设计的角度来看,一个很好的重击规则可能会问自己,还有谁在使用这个类/功能(意味着代码库的哪些其他部分需要它)?

    假设您要表示由Mouse 执行的ClickWheelScroll。在这个简单的例子中,将这些类捆绑在一起会更有意义:

    module ComputerPart
       class Mouse; end
       class WheelScroll; end
       class Click; end
    end
    

    最后,我建议您仔细阅读其中一些热门项目的代码,以了解社区通常如何做出这些决定。

    【讨论】:

      【解决方案3】:

      1.) 我遵循这个做法,但不是必须的,你可以根据需要将一堆类放在一个文件中。

      2.) 如果这些类在同一个文件中,不,当您运行脚本时,它们都可以访问。如果它们在单独的文件中,那么您应该要求它们,您也可以要求文件(自身)所在的整个目录。

      3.)是的,它应该在文件的顶部。

      4.) 在 ruby​​ 中,一切都源自 Main 对象,解释器只是为您处理创建它。如果您正在编写 OO ruby​​ 而不仅仅是脚本,那么入口点将是您调用的第一个类的 init 方法。

      5.) 是的,在程序运行之前它会加载所有依赖项。

      6.) 我认为它可以做到这一点,您所要做的就是要求文件顶部的正确文件,之后您可以随意使用它们而无需再次隐式加载它们。

      【讨论】:

        猜你喜欢
        • 2014-03-12
        • 1970-01-01
        • 2019-08-24
        • 2015-03-03
        • 2012-03-09
        • 1970-01-01
        • 2011-05-10
        • 1970-01-01
        • 2014-10-09
        相关资源
        最近更新 更多