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