ruby中的适配器模式:访问您的实例变量

我正在研究ruby中的适配器模式实现。 我想访问适配器模块定义中的实例变量。 看一下下面的代码:

module Adapter module Dog def self.speak # I want to access the #name instance variable from my Animal instance puts "#{name} says: woof!" end end module Cat def self.speak # I want to access the #name instance variable from my Animal instance puts "#{name} says: meow!" end end end class Animal attr_accessor :name def initialize(name) @name = name end def speak self.adapter.speak end def adapter return @adapter if @adapter self.adapter = :dog @adapter end def adapter=(adapter) @adapter = Adapter.const_get(adapter.to_s.capitalize) end end 

为了测试它,我做了以下事情:

 animal = Animal.new("catdog") animal.adapter = :cat animal.speak 

我希望它返回以下内容:

 catdog says: meow! 

相反它说:

 Adapter::Cat says: meow! 

有关如何从适配器模块访问Animal#name实例方法的任何提示? 我认为问题是我的适配器方法是类级方法。

谢谢!

您需要将模块用作mixin并提供一种方法来跟踪哪个模块处于活动状态,这些方法似乎不会被重新包含或重新扩展覆盖,因此我采用了我在此处找到的扩展和删除方法。

 module Adapter module Dog def speak puts "#{name} says: woof!" end end module Cat def speak puts "#{name} says: meow!" end end def extend mod @ancestors ||= {} return if @ancestors[mod] mod_clone = mod.clone @ancestors[mod] = mod_clone super mod_clone end def remove mod mod_clone = @ancestors[mod] mod_clone.instance_methods.each {|m| mod_clone.module_eval {remove_method m } } @ancestors[mod] = nil end end class Animal include Adapter attr_accessor :name, :adapter def initialize(name) @name = name @adapter = Adapter::Dog extend Adapter::Dog end def adapter=(adapter) remove @adapter extend Adapter::const_get(adapter.capitalize) @adapter = Adapter.const_get(adapter.capitalize) end end animal = Animal.new("catdog") animal.speak # catdog says: woof! animal.adapter = :cat animal.speak # catdog says: meow! animal.adapter = :dog animal.speak # catdog says: woof! 

这是因为module上下文中的name指的是与您期望的name完全不同的内容。 Animal类和Cat模块不共享数据,它们没有任何关系。 巧合的是,你正在调用Module#name ,它恰好返回了Adapter::Cat因为它是模块的名称。

为了解决这个问题,你需要做两件事之一。 要么让你的module混合(删除self ,然后根据需要include它),或者通过将其作为参数speak出来共享必要的数据。

第一种方法如下所示:

 module Adapter module Dog def self.speak(name) puts "#{name} says: woof!" end end end class Animal attr_accessor :name attr_reader :adapter def initialize(name) @name = name self.adapter = :dog end def speak self.adapter.speak(@name) end def adapter=(adapter) @adapter = Adapter.const_get(adapter.to_s.capitalize) end end 

这看起来并不简单,因为它们基本上生活在两个不同的世界中。 更像Ruby的方式是这样的:

 module Adapter module Dog def speak puts "#{name} says: woof!" end end end class Animal attr_accessor :name attr_reader :adapter def initialize(name) @name = name self.adapter = :dog end def adapter=(adapter) @adapter = Adapter.const_get(adapter.to_s.capitalize) extend(@adapter) end end