首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Modules

模块在 Ruby 中有两个用途,命名空间和混合功能。

命名空间可以用于通过包或功能组织代码,以将常见名称与其他包的干扰区分开来。例如,IRB 命名空间提供了 irb 功能,可防止通用名称 “Context” 发生冲突。

混合功能允许跨多个类或模块共享常用方法。Ruby 带有 Enumerable 混合模块,它提供了许多基于each方法的枚举方法,而 Comparable 允许根据<=>比较方法比较对象。

请注意,模块和类之间有许多相似之处。除了混合模块的能力之外,以下模块的描述也适用于类。

Module Definition

模块使用module关键字创建:

代码语言:javascript
复制
module MyModule
  # ...
end

模块可以重新打开任意次数以添加,更改或删除功能:

代码语言:javascript
复制
module MyModule
  def my_method
  end
end

module MyModule
  alias my_alias my_method
end

module MyModule
  remove_method :my_method
end

重新打开类是 Ruby 的一个非常强大的功能,但最好只重新打开你自己的类。重新打开您不属于自己的课程可能会导致命名冲突或难以诊断错误。

Nesting

模块可以嵌套:

代码语言:javascript
复制
module Outer
  module Inner
  end
end

许多包创建一个最外层的模块(或类)来为其功能提供一个名称空间。

您也可以使用::提供的外部模块(或类)定义内部模块:

代码语言:javascript
复制
module Outer::Inner::GrandChild
end

请注意,这将引发NameError如果OuterOuter::Inner尚未确定。

这种风格的好处是允许作者减少缩进量。而不是3个级别的缩进,只有一个是必要的。但是,对于使用此语法创建名称空间而不是更详细的语法,常量查找的范围不同。

Scope

self

self指的是定义当前范围的对象。self在输入不同的方法或定义新的模块时会改变。

Constants

可访问的常量根据模块嵌套而不同(使用哪种语法来定义模块)。在下面的例子中,A::Z可以从 B 访问常量,因为 A 是嵌套的一部分:

代码语言:javascript
复制
module A
  Z = 1

  module B
    p Module.nesting #=> [A::B, A]
    p Z #=> 1
  end
end

但是,如果您使用::定义A::B而不嵌套它A,则会引发 NameError 异常,因为嵌套不包括A

代码语言:javascript
复制
module A
  Z = 1
end

module A::B
  p Module.nesting #=> [A::B]
  p Z #=> raises NameError
end

如果在顶层定义了一个常量,则可以在其之前::引用它:

代码语言:javascript
复制
Z = 0

module A
  Z = 1

  module B
    p ::Z #=> 0
  end
end

Methods

有关方法定义文档,请参阅方法的语法文档。

类方法可以直接调用。(这有点令人困惑,但模块上的方法通常被称为“类方法”,而不是“模块方法”。另请参阅模块 #module_function,它可以将实例方法转换为类方法。)

当一个类方法引用一个常量时,它使用与在该方法之外引用它相同的规则,因为范围相同。

在模块中定义的实例方法仅在包含时才可调用。这些方法可以访问通过祖先列表包含的常量:

代码语言:javascript
复制
module A
  Z = 1

  def z
    Z
  end
end

include A

p self.class.ancestors #=> [Object, A, Kernel, BasicObject]
p z #=> 1

Visibility

Ruby 有三种可见性。默认是public。公共方法可以从任何其他对象调用。

第二个可见性是protected。当调用受保护的方法时,发送者必须是接收者的子类,或者接收者必须是发送者的子类。否则会引发 NoMethodError。

受保护的可见性最常用于定义==和其他比较方法,其中作者不希望将任何对象的状态公开给任何调用者,并希望仅将其限制为继承的类。

这里是一个例子:

代码语言:javascript
复制
class A
  def n(other)
    other.m
  end
end

class B < A
  def m
    1
  end

  protected :m

end

class C < B
end

a = A.new
b = B.new
c = C.new

c.n b #=> 1 -- C is a subclass of B
b.n b #=> 1 -- m called on defining class
a.n b # raises NoMethodError A is not a subclass of B

第三个可见性是private。私人方法可能不会被接收方调用,甚至不会self。如果使用接收方调用私有方法,则会引发 NoMethodError。

aliasundef

您也可以使用别名或不定义方法,但这些操作不限于模块或类。查看文档的其他语法部分。

每个类也是一个模块,但与模块不同,一个类可能不会混入另一个模块(或类)。就像一个模块一样,一个类可以用作命名空间。一个类还从它的超类继承了方法和常量。

定义一个类

使用class关键字创建一个类:

代码语言:javascript
复制
class MyClass
  # ...
end

如果你不提供超类,你的新类将继承 Object。您可以继续使用不同的类,<然后使用类名称:

代码语言:javascript
复制
class MySubclass < MyClass
  # ...
end

有一个特殊的 BasicObject 类被设计成一个空白类,并且包含最少的内置方法。您可以使用 BasicObject 创建独立的继承结构。有关更多详细信息,请参阅 BasicObject 文档。

Inheritance

定义在类上的任何方法都可以从它的子类中调用:

代码语言:javascript
复制
class A
  Z = 1

  def z
    Z
  end
end

class B < A
end

p B.new.z #=> 1

常量同样如此:

代码语言:javascript
复制
class A
  Z = 1
end

class B < A
  def z
    Z
  end
end

p B.new.z #=> 1

您可以通过重新定义方法来覆盖超类方法的功能:

代码语言:javascript
复制
class A
  def m
    1
  end
end

class B < A
  def m
    2
  end
end

p B.new.m #=> 2

如果你想从一个方法调用超类功能,使用super

代码语言:javascript
复制
class A
  def m
    1
  end
end

class B < A
  def m
    2 + super
  end
end

p B.new.m #=> 3

在没有任何参数的情况下super使用给子类方法的参数。不发送参数给超类方法使用super()。要向超类方法发送特定参数,请手动提供它们super(2)

super 可以在子类方法中多次调用。

对象的单例类(也称为元类或特征类)是仅为该实例保存方法的类。你可以class << object像这样访问对象的单例类:

代码语言:javascript
复制
class C
end

class << C
  # self is the singleton class here
end

大多数情况下,你会看到像这样访问的单例类:

代码语言:javascript
复制
class C
  class << self
    # ...
  end
end

这允许在类(或模块)上定义方法和属性而无需编写def self.my_method

既然你可以打开任何对象的单例类,这意味着这个代码块:

代码语言:javascript
复制
o = Object.new

def o.my_method
  1 + 1
end

相当于这个代码块:

代码语言:javascript
复制
o = Object.new

class << o
  def my_method
    1 + 1
  end
end

两个对象都会由my_method返回2

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com