繁体   English   中英

模块扩展Ruby中的其他模块

[英]module extending other modules in Ruby

在这方面,我已经阅读了很多答案,但是我仍然无法弄清楚以下原因为何不起作用

module A
  def a
    puts 'hi'
  end
end

module B
  extend A
end

class C
  extend B
  def b
    a
  end
end
C.new.b # undefined local variable or method `a' for #<C:...

我也尝试过:

module B
  def self.included(recipient)
    recipient.extend A
  end
end

希望C将得到扩展(但是我想层次结构是错误的)

重要提示 :问题在于我有一个需要extended memoist模块,并且我想为其添加一些功能。

我该如何实现?为什么第一个示例不起作用?

extends将作为参数传递的模块中的方法添加为“类方法”。 你期待看到的东西include ,它增加了从方法(在这种情况下) A模块实例方法,就像你希望它:

module A
  def a
    puts 'hi'
  end
end

module B
  include A
end

class C
  include B
  def b
    a
  end
end
C.new.b
# hi

首先,您的示例并不完全有效。 默认情况下,扩展的模块不会添加实例方法。 您的A模块应如下所示:

module Memoist
  def self.extended(mod)
    mod.include InstanceMethods
  end

  module InstanceMethods
    def a
      puts 'hi'
    end
  end
end

您可以在extendedincludedexcluded回调中extend Memoist 只要您要添加到界面中,它就可以工作。 在扩展,包含或添加模块之后,将触发回调。 这意味着,如果要使用super扩展现有方法,则该方法将无效,因为在您的版本之前会调用原始方法。

module YourMemoist
  # Depending on how you want to incorporate your module into the class
  # you can use one of the following: 
  def self.extended(mod)
    mod.extend Memoist
  end

  # def self.included(mod)
  #   mod.extend Memoist
  # end

  # def self.prepended(mod)
  #   mod.extend Memoist
  # end

  # ...
end

class C
  extend MemoistHelper # or include, prepend
end

C.new.a #=> prints hi
C.singleton_class.ancesotrs #=> [#<Class:C>, Memoist, YourMemoist, ...]

如果您确实想使用super关键字,事情将会变得更加困难。 由于Memoist模块需要首先扩展。 一种实现方法是将模块包含在自己的模块中,以便您可以覆盖它们。 到目前为止非常简单。 但是, Memoist可能定义了extended回调(以添加实例方法),当我们将模块包括在自己的模块中时不会触发该回调。 因此,我们需要手动调用它。

module YourMemoist
  include Memoist

  def self.extended(mod)
    Memoist.send(:extended, mod) # using #send to call private method
    # If you want to add your own instance methods add them after the
    # above call (as shown in the Memoist module).
  end

  # ...
end

class C
  extend MemoistHelper
end

C.new.a #=> prints hi
C.singleton_class.ancesotrs #=> [#<Class:C>, YourMemoist, Memoist, ...]

回想一下,如果一个模块M1includedextended (或prepended由模块) M2M1的实例方法被带入M2 ,无论是作为实例方法(如果包括)或作为模块的方法(如果延伸)。 如果M1包含模块方法(在M1调用的方法),则它们都将被includeextend跳过。 由于类是模块,因此在M2是类时适用。

module M1
  def i() end
  def self.m() end
end

M1.instance_methods
  #=> [:i] 
M1.methods(false)
  #=> [:m] 

module M2; end

M2.extend M1
M2.instance_methods
  #=> []
M2.methods & [:i, :m]
  #=> [:i]

module M3; end

M3.include M1
M3.instance_methods
  #=> [:i]
M3.methods & [:i, :m]
  #=> []

我们看到M2M3都不包含方法:m 现在考虑以下内容。

module A
  def a
    puts 'hi'
  end
end

module B
  extend A
end

B.instance_methods
  #=> []
B.methods & [:a]
  #=> [:a]

不出所料, B包含任何实例方法,其模块方法包括:a 从前面的讨论可以看出,将B包含或扩展到另一个模块(或类) C 不会将实例方法或方法带入C

为了增加A的功能, B必须include (或在prependA

module B
  include A
  def b
    puts 'ho'
  end
end

B.instance_methods
  #=> [:b, :a] 
B.methods & [:a, :b]
  #=> []

然后, C可以根据需要includeextend B

有人可能会问,当模块被另一个模块包含或扩展时,如果忽略它们,是否有任何理由来定义模块方法。 答案是它们只是辅助方法,在非OOP语言中充当函数。 一个示例是Math模块,该模块仅包含模块方法。 因此,它们在模块上被调用; 例如,

Math.sqrt(2)
  #=> 1.4142135623730951  

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM