调用Ruby Singleton的方法而不引用'instance'

时间:2011-03-04 12:51:05

标签: ruby singleton metaprogramming

我想调用Singleton Object的方法而不引用它的实例

SingletonKlass.my_method

而不是

SingletonKlass.instance.my_method

我想出了这个解决方案(在课堂上使用method_missing):

require 'singleton'    

class SingletonKlass
  include Singleton

  def self.method_missing(method, *args, &block)
    self.instance.send(method, *args)
  end

  def my_method
     puts "hi there!!"
  end
end

这有什么缺点吗? 还有更好的解决方案吗? 你的任何推荐?

谢谢。

更新:

我的目标是让一个模块与单身类混合:

module NoInstanceSingleton
   def method_missing(method, *args)
      self.instance.send(method, *args)
   end
end

然后在课堂上使用它:

class SingletonKlass
   include Singleton
   extend NoInstanceSingleton

  def method1; end
  def method2; end
  ...
  def methodN; end
end

我希望能够直接致电:

SingletonKlass.method1

5 个答案:

答案 0 :(得分:12)

使用forwardable和def_delegators:

require 'singleton'    
require 'forwardable'

class SingletonKlass
  include Singleton
  class << self
    extend Forwardable
    def_delegators :instance, :my_method
  end

  def my_method
     puts "hi there!!"
  end
end

SingletonKlass.my_method

修改:如果您想要包含自己定义的所有方法,可以

require 'singleton'    
require 'forwardable'

class SingletonKlass
  include Singleton

  def my_method
     puts "hi there!!"
  end

  class << self
    extend Forwardable
    def_delegators :instance, *SingletonKlass.instance_methods(false)
  end
end

SingletonKlass.my_method

答案 1 :(得分:2)

method_missing解决方案存在的问题是,如果instance上没有该名称的方法,则只会将调用重定向到SingletonKlass,如果人们想要这会导致问题例如,通过您提供的此接口访问instance.__id__。以正常方式访问SingletonKlass.instance没有太大问题,但如果你真的想要创建一个快捷方式,那么最安全的就是常量:

KlassInstance = SingletonKlass.instance

如果要动态定义常量,请使用Module#const_set

const_set :KlassInstance, SingletonKlass.instance

你也可以延伸。例如,您可以创建一个方法来为您创建这样的常量:

def singleton_constant(singleton_class)
  const_set singleton_class.name, singleton_class.instance
end

当然,因为Module#const_set是模块的方法,所以这种特定技术只能在模块或类的上下文中执行。另一种可能性是使用带有重载钩子方法的mixin模块:

module SingletonInstance
  def included(base_class)
    const_set base_class.name, base_class.instance
    super
  end
end

答案 2 :(得分:1)

这是一个替代方案,并不属于我的第一个答案范围。您可以创建一个mixin模块,该模块取消定义基类的所有方法,然后使用method_missing技术:

module SingletonRedirect
  def included(base_class)
    instance = base_class.instance
    base_class.class_eval do
      methods.each &undef_method

      define_method :method_missing do |name, *arguments|
        instance.public_send name, *arguments
      end
    end
  end
end

为了实现这个想法,instance是一个局部变量,通过闭包传递给调用Class#class_evalModule#define_method的块。然后,我们不需要通过base_class.instance来引用它,因此我们可以清除所有方法,包括那个方法(一种称为空白平板的技术)。然后,我们定义一个方法重定向系统,该系统利用平坦范围将instance称为变量,并仅调用通过Object#public_send公开提供的方法。

答案 3 :(得分:0)

而不是SingletonKlass,我将使用名称UrlHelper作为更具体的示例。

require 'singleton'    

class UrlHelperSingleton
  include Singleton

  def my_method
     ...
  end
end

UrlHelper = UrlHelperSingleton.instance

# then elsewhere
UrlHelper.my_method

答案 4 :(得分:-1)

为什么不使用“静态”课程? e.g:

class StaticKlass

  class << self
    def method1; end
    def method2; end
    ...
    def methodN; end
  end

end

StaticKlass.method1