Вызов метода Ruby Singleton без ссылки на «экземпляр» - PullRequest
3 голосов
/ 04 марта 2011

Я хотел бы вызвать метод объекта Singleton без ссылки на его экземпляр

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 ]

12 голосов
/ 15 марта 2011

Использование 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
2 голосов
/ 15 марта 2011

Проблема с вашим решением 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
1 голос
/ 17 марта 2011

Вот альтернатива, которая на самом деле не попадает в рамки моего первого ответа. Вы можете создать смешанный модуль, который отменяет определение всех методов базового класса, а затем использует технику 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_eval и Module#define_method. Тогда нам не нужно ссылаться на него с помощью base_class.instance, поэтому мы можем очистить все методы, включая этот (метод, известный как чистый лист). Затем мы определяем систему перенаправления методов, которая использует преимущества плоской области видимости для ссылки на instance как переменную и вызываем только те методы, которые общедоступны через Object#public_send.

0 голосов
/ 29 марта 2018

Вместо SingletonKlass я буду использовать имя UrlHelper в качестве более конкретного примера.

require 'singleton'    

class UrlHelperSingleton
  include Singleton

  def my_method
     ...
  end
end

UrlHelper = UrlHelperSingleton.instance

# then elsewhere
UrlHelper.my_method
0 голосов
/ 25 марта 2011

Почему вы не используете "статический" класс? e.g.:

class StaticKlass

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

end

StaticKlass.method1
...