Ruby:调用Singleton实例方法的DRY类方法

时间:2016-01-16 15:48:49

标签: ruby delegates singleton

我有一个Singleton类ExchangeRegistry,它保存所有Exchange对象。

而不是需要打电话:    ExchangeRegistry.instance.exchanges

我希望能够使用: ExchangeRegistry.exchanges

这很有效,但我对重复不满意:

require 'singleton'

# Ensure an Exchange is only created once
class ExchangeRegistry
  include Singleton

  # Class Methods  ###### Here be duplication and dragons

  def self.exchanges
    instance.exchanges
  end

  def self.get(exchange)
    instance.get(exchange)
  end

  # Instance Methods

  attr_reader :exchanges

  def initialize
    @exchanges = {} # Stores every Exchange created
  end

  def get(exchange)
    @exchanges[Exchange.to_sym exchange] ||= Exchange.create(exchange)
  end
end

我对课程方法中的重复感到不满意。

我已尝试使用ForwardableSimpleDelegator,但似乎无法将其清除干净。 (大多数示例都不是类方法,而是例如方法)

3 个答案:

答案 0 :(得分:5)

可转发模块将执行此操作。由于您正在转发类方法,您必须打开本征类并在那里定义转发:

require 'forwardable'
require 'singleton'

class Foo

  include Singleton

  class << self
    extend Forwardable
    def_delegators :instance, :foo, :bar
  end

  def foo
    'foo'
  end

  def bar
    'bar'
  end

end

p Foo.foo    # => "foo"
p Foo.bar    # => "bar"

答案 1 :(得分:2)

接受的答案很聪明,但似乎不必要复杂(更不用说method_missing的性能损失了。

解决此问题的常用方法是将实例分配给常量。

class ExchangeRegistrySingleton
  include Singleton

  # ...
end

ExchangeRegistry = ExchangeRegistrySingleton.instance

答案 2 :(得分:0)

您可以利用method_missing挂钩并将方法调用委托给instance

require 'singleton'

class ExchangeRegistry
  include Singleton

  # Missing methods will be delegated to `instance` if an implementation is available.
  # Else `NoMethodError` will be raised via call to `super`
  def self.method_missing method_name, *args
    if instance.respond_to? method_name
        puts "** Defining new method: '#{method_name}'"
        (class << self; self; end).instance_eval do
            define_method(method_name) do |*args|
                instance.send(method_name, *args)
            end
        end
        instance.send(method_name, *args)
    else
        super
    end
  end

  attr_reader :exchanges

  def initialize
    @exchanges = {} # Stores every Exchange created
  end

  def get(exchange)
    @exchanges[Exchange.to_sym exchange] ||= Exchange.create(exchange)
  end
end

# By default, there is no class method - `exchanges`
p ExchangeRegistry.singleton_methods.grep(/exchanges/)
#=> []

p ExchangeRegistry.exchanges
#=> ** Defining new method: 'exchanges'
#=> {}

# After first call to `exchanges`, a new class method is now available
# Future calls will not hit `method_missing` again.
p ExchangeRegistry.singleton_methods.grep(/exchanges/)
#=> [:exchanges]
p ExchangeRegistry.exchanges
#=> {}
此问题的

Another answer表示处理method_missing会导致性能下降。因此,我在第一次报告method_missing时更新了定义类方法的答案。此更新基于文章:Dynamically create class methods in Ruby