为什么Ruby模块中定义的类无法和普通类享有相同的作用域?
原因说明
Ruby的方法查找是基于当前self的继承链,嵌套类的实例方法执行时,self是类的实例,继承链默认不包含外层模块,所以无法直接访问外层模块的方法,module_function只会为模块本身生成单例方法,不会同步到嵌套类的方法链中。
最简解决方案
方案1:捕获外层模块上下文(适配匿名模块/无固定名称模块场景)
适合无固定名称脚本模块的场景,不需要硬编码模块名,定义类时直接捕获外层模块的self即可:
module MyMod # 先捕获当前模块的引用,匿名模块也能正常生效 outer_mod = self def meaning 42 end class Foo # 批量把需要的方法委托给外层模块 %i[meaning other_method another_method].each do |meth| define_method(meth) { outer_mod.send(meth) } end def initialize puts "New Foo" puts "I can call #{meaning} directly now" end end def go puts "I can get to #{meaning} from here" bar = Foo.new end module_function :go, :meaning go end
方案2:抽取公共混入模块
如果需要共享的方法较多,推荐抽成独立的子模块,外层模块和内部类都混入该模块即可:
module MyMod # 所有要共享的方法统一放到这个子模块 module SharedMethods def meaning 42 end def other_shared_method "test" end end # 外层模块引入,实例和单例侧都能调用 include SharedMethods extend SharedMethods class Foo # 内部类直接引入,直接调用所有共享方法 include SharedMethods def initialize puts meaning puts other_shared_method end end def go puts meaning Foo.new end module_function :go go end
方案3:使用module_parent(Ruby 2.7+)
如果模块有固定名称,也可以用Ruby自带的module_parent方法获取外层模块,配合方法委托使用:
module MyMod def meaning 42 end class Foo # 直接委托给外层模块,不需要硬编码模块名 def meaning self.class.module_parent.meaning end def initialize puts meaning end end module_function :meaning end
内容的提问来源于stack exchange,提问作者David Ljung Madison Stellar
相关产品推荐
相关产品推荐

