我有一个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
我对课程方法中的重复感到不满意。
我已尝试使用Forwardable
和SimpleDelegator
,但似乎无法将其清除干净。 (大多数示例都不是类方法,而是例如方法)
答案 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