Ruby 为什么要使用extend/include而不是简单地在主对象中定义方法?

Ruby 为什么要使用extend/include而不是简单地在主对象中定义方法?,ruby,Ruby,RSpec在顶级名称空间中添加了一个“descripe”方法。但是,它们不是简单地在任何类/模块之外定义方法,而是这样做: # code from rspec-core/lib/rspec/core/dsl.rb module RSpec module Core # Adds the `describe` method to the top-level namespace. module DSL def describe(*args, &example_

RSpec在顶级名称空间中添加了一个“descripe”方法。但是,它们不是简单地在任何类/模块之外定义方法,而是这样做:

# code from rspec-core/lib/rspec/core/dsl.rb 
module RSpec
  module Core
    # Adds the `describe` method to the top-level namespace.
    module DSL
      def describe(*args, &example_group_block)
        RSpec::Core::ExampleGroup.describe(*args, &example_group_block).register
      end
    end
  end
end

extend RSpec::Core::DSL
Module.send(:include, RSpec::Core::DSL)

与在任何模块和类之外简单地定义descripe相比,使用这种技术有什么好处?(据我所知,DSL模块在rspec core的其他任何地方都没有使用过。)

事实上,如果这就是代码中的全部内容,我真的不相信它会更好——如果有的话。一个常见的参数是,通过检查方法所有者,可以轻松地检查RSpec是否负责在全局命名空间中添加此方法。不知何故,它从未觉得有必要这样做,因为方法的位置已经存储了这些信息

在任何范围之外定义方法将等同于在对象中定义私有实例方法:

class Object
  private
  def double(arg)
    arg * 2
  end
end

double(3)      # OK
3.double(3)    # Error: double is private
self.double(3) # Error: double is private
我认为私密性是一个有用的方面,因为它可以防止进行某些没有意义的方法调用,而问题中所示的代码缺少这些方法调用

不过,在模块中定义方法有一个优点,但RSpec代码似乎没有利用它:使用
module\u函数
,不仅可以保留实例方法的私有性,还可以获得一个公共类方法。这意味着,如果您有一个同名的实例方法,您仍然可以使用类方法版本引用模块定义的实例方法

module\u function
的一个常见示例是
Kernel
模块,它包含大多数类似函数的核心方法,如
put
(另一个是
Math
)。如果您所在的类重新定义了
put
,如果需要,您仍然可以显式使用
Kernel\put

class LikeAnIO
  def puts(string)
    @output << string
  end

  def do_work
    puts "foo" # inserts "foo" in @output
    Kernel.puts "foo" # inserts "foo" in $stdout
  end
end
class-LikeAnIO
def puts(字符串)

@输出事实上,如果这就是代码中的全部内容,我真的不相信它会更好——如果有的话。一个常见的参数是,通过检查方法所有者,可以轻松地检查RSpec是否负责在全局命名空间中添加此方法。不知何故,它从未觉得有必要这样做,因为方法的位置已经存储了这些信息

在任何范围之外定义方法将等同于在对象中定义私有实例方法:

class Object
  private
  def double(arg)
    arg * 2
  end
end

double(3)      # OK
3.double(3)    # Error: double is private
self.double(3) # Error: double is private
我认为私密性是一个有用的方面,因为它可以防止进行某些没有意义的方法调用,而问题中所示的代码缺少这些方法调用

不过,在模块中定义方法有一个优点,但RSpec代码似乎没有利用它:使用
module\u函数
,不仅可以保留实例方法的私有性,还可以获得一个公共类方法。这意味着,如果您有一个同名的实例方法,您仍然可以使用类方法版本引用模块定义的实例方法

module\u function
的一个常见示例是
Kernel
模块,它包含大多数类似函数的核心方法,如
put
(另一个是
Math
)。如果您所在的类重新定义了
put
,如果需要,您仍然可以显式使用
Kernel\put

class LikeAnIO
  def puts(string)
    @output << string
  end

  def do_work
    puts "foo" # inserts "foo" in @output
    Kernel.puts "foo" # inserts "foo" in $stdout
  end
end
class-LikeAnIO
def puts(字符串)
@输出我做了此更改,因此
描述
不再添加到系统中的每个对象。如果您在顶层定义了它:

def describe(*args)
end
…然后系统中的每个对象都会有一个私有的
descripe
方法。RSpec并不拥有系统中的每个对象,也不应该随意向每个对象添加
description
。我们只希望descripe方法在两个范围内可用:

describe MyClass do
end
(位于顶层,远离主对象)

(脱离任何模块,因此将描述嵌套在模块范围中)

将它放在一个模块中可以很容易地扩展到主对象上(只将它添加到该对象,而不是每个对象),并将它包含在
模块中(将它添加到所有模块中)。

我做了此更改,以便
描述
不再添加到系统中的每个对象中。如果您在顶层定义了它:

def describe(*args)
end
…然后系统中的每个对象都会有一个私有的
descripe
方法。RSpec并不拥有系统中的每个对象,也不应该随意向每个对象添加
description
。我们只希望descripe方法在两个范围内可用:

describe MyClass do
end
(位于顶层,远离主对象)

(脱离任何模块,因此将描述嵌套在模块范围中)

将它放在模块中可以很容易地扩展到主对象上(仅将其添加到该对象,而不是每个对象),并将其包含在
模块中(将其添加到所有模块中)