如何覆盖模块中的类常量和方法?

Posted

技术标签:

【中文标题】如何覆盖模块中的类常量和方法?【英文标题】:How to override class constants and methods from a module? 【发布时间】:2022-01-24 03:31:33 【问题描述】:

我正在尝试创建一个模块来为某些类定义一些方法和常量。

这意味着可以在模块和包含该模块的类中定义相同的方法或常量。

在那种情况下,我想要控制(最好是在模块定义中,或者可能来自调用者,而不更改类代码)哪个优先。

通常,我希望使用模块中定义的方法或常量。

但是,除非我从类中删除它们,否则类中定义的那些似乎总是被使用。

module A
  def self.included(clazz)
    const_set(:CONST, clazz.name == "B" ? "constant from module A for B" : "constant from module A for C")

    define_method(:a) do
      "from module A"
    end
  end
end

class B
  include A
  CONST = "constant from class B"
  def a
    "from class B"
  end
end

class C < B
  include A
  CONST = "constant from class C"
  def a
    "from class C"
  end
end

b = B.new
c = C.new

p b.a, c.a
p B::CONST, C::CONST

在this REPL 打印上运行它

main.rb:3: warning: already initialized constant A::CONST
main.rb:3: warning: previous definition of CONST was here
"from class B"
"from class C"
"constant from class B"
"constant from class C"

有什么办法可以输出这个

"from class A"
"from class A"
"constant from module A for B"
"constant from module A for C"

不从 B 类或 C 类中删除常量 CONST 或方法 a

【问题讨论】:

【参考方案1】:

首先,Ruby从上到下逐行解释代码,所以在你的例子中,Ruby在定义CONST =之前读取module A,结果,在类B或C中定义的CONST将始终输出。

-> 将include A 移动到CONST= 下方

其次,在def self.included(clazz) 内部,self 是module A 而不是B 类或C 类,因此const_set 方法由module A 本身调用,因此没有B::CONSTC::CONST在这里定义。

-> 在def self.included(clazz) 中使用clazz.const_set

试试这个

module A
  def self.included(base)
    base.send :remove_const, :CONST if base.const_defined?(:CONST)
    base.const_set(:CONST, base.name == "B" ? "constant from module A for B" : "constant from module A for C")
  end
end

class B
  CONST = "constant from class B"
  include A
end

【讨论】:

以上是关于如何覆盖模块中的类常量和方法?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby模块中的动态覆盖类方法

在 PHP 中使用类常量和覆盖

Java接口内部类包装类装箱拆箱详解!

如何将另一个文件中的变量分配给类常量?

如何覆盖(覆盖)material-ui(React)中的类和样式

在angularJS中覆盖模块值/常量的最佳方法