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

时间:2017-01-25 21:14:44

标签: ruby

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

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实例方法的任何提示?我认为问题是我的适配器方法是类级方法。

谢谢!

2 个答案:

答案 0 :(得分:2)

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

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!

答案 1 :(得分:1)

这是因为function requestToServer(lParamList: TStringList) : string; var Params: TStringList; Thread: TThread; begin Params := TStringList.Create; try Params.Assign(lParamList); except Params.Free; raise; end; TThread.CreateAnonymousThread( procedure var lHTTP: TIdHTTP; serverResponce : string; aObj: ISuperObject; begin try try lHTTP := TIdHTTP.Create(nil); try serverResponce := lHTTP.Post('http://domain.com/mjson.php', lParamList); aObj := SO(serverResponce); if aObj['result'].AsString = 'lr_102' then begin TThread.Queue(nil, procedure begin form2.Label3.Text := 'Saved token expired.'; form2.Rectangle2.Visible := true; end ); end else if aObj['result'].AsString = 'lr_103' then begin X := aObj['dta'].AsArray; TThread.Queue(nil, procedure begin form2.Label3.Text := 'Auto login.'; //load device data form2.allDeviceListData := X; form2.Hide; form1.show; end ); end; // globalReachedServer := true; finally lHTTP.Free; end; finally Params.Free; end; except TThread.Queue(nil, procedure begin form2.Memo1.Lines.Add('errr'); end ); end; end ).Start; end; 上下文中的name指的是与您期望的module完全不同的内容。 name类和Animal模块不共享数据,它们没有任何关系。巧合的是,你正在调用Module#name碰巧返回Cat,因为那是模块的名称。

为了解决这个问题,你需要做两件事之一。您可以将Adapter::Cat混合(删除module,然后根据需要self)或通过将其作为参数传递给include来共享必要的数据。< / p>

第一种方法如下:

speak

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

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