Подтвердить что ты не робот

Ruby - создать синглтон с параметрами?

Я видел, как определить класс как singleton (как создать singleton в ruby ​​):

require 'singleton'

class Example
  include Singleton
end

Но что, если я хочу дать ему некоторые параметры для этого единственного экземпляра, то есть в примере всегда должны быть инициализированы определенные свойства. Например, скажем, у меня был класс, единственной целью которого является запись в файл (это всего лишь пример), но для этого требуется имя файла для входа в систему, прежде чем он сможет работать.

class MyLogger
  def initialize(file_name)
    @file_name = file_name
  end
end

Как я могу сделать MyLogger одиночным, но убедитесь, что он получает имя_файла?

4b9b3361

Ответ 1

Singleton не предоставляет эту функциональность, но вместо использования singleton вы можете написать его самостоятельно

class MyLogger
  @@singleton__instance__ = nil
  @@singleton__mutex__ = Mutex.new
  def self.instance file_name
    return @@singleton__instance__ if @@singleton__instance__
    @@singleton__mutex__.synchronize {
      return @@singleton__instance__ if @@singleton__instance__
      @@singleton__instance__ = new(file_name)
    }
    @@singleton__instance__
  end
  private
  def initialize file_name
    @file_name = file_name
  end
  private_class_method :new
end

Он должен работать, но я не тестировал код.

Этот код заставляет использовать MyLogger.instance <file_name> или, по крайней мере, при первом вызове, если вы знаете, что он будет звонить в первый раз.

Ответ 2

Здесь другой способ сделать это - поместить имя файла журнала в переменную класса:

require 'singleton'
class MyLogger
  include Singleton
  @@file_name = ""
  def self.file_name= fn
    @@file_name = fn
  end
  def initialize
    @file_name = @@file_name
  end
end

Теперь вы можете использовать его следующим образом:

MyLogger.file_name = "path/to/log/file"
log = MyLogger.instance  # => #<MyLogger:0x000.... @file_name="path/to/log/file">

Последующие вызовы instance возвращают тот же объект с неизменным именем пути, даже если вы позже измените значение переменной класса. Другим приятным прикосновением было бы использовать другую переменную класса, чтобы отслеживать, был ли экземпляр уже создан, и в этом случае метод file_name= вызывает исключение. Вы также можете initialize вызвать исключение, если @@file_name еще не установлено.

Ответ 3

Это слишком долго, чтобы добавить комментарий (например, stackoverflow сказал, что он слишком длинный)

Хорошо, вот что я придумал:

class MyLogger
  @@singleton__instance__ = nil
  @@singleton__mutex__ = Mutex.new
  def self.config_instance file_name
    return @@singleton__instance__ if @@singleton__instance__
    @@singleton__mutex__.synchronize {
      return @@singleton__instance__ if @@singleton__instance__
      @@singleton__instance__ = new(file_name)
      def self.instance
        @@singleton__instance__
      end
      private_class_method :new
    }
    @@singleton__instance__
  end
  def self.instance
    raise "must call MyLogger.config_instance at least once"
  end
  private
  def initialize file_name
    @file_name = file_name
  end
end

Для создания и настройки экземпляра singleton используется 'config_instance'. Он переопределяет метод self.instance, когда экземпляр готов.

Он также делает метод "нового" класса закрытым после создания первого экземпляра.

Ответ 4

Простой синглтон, который не зависит от модуля Singleton

class MyLogger
  def self.instance(filepath = File.join('some', 'default', 'path'))
    @@instance ||= new(filepath).send(:configure)
  end

  def initialize(filepath)
    @filepath = filepath
  end
  private_class_method :new

  def info(msg)
    puts msg
  end

  private

  def configure
    # do stuff
    self
  end
end

Пример использования

logger_a = MyLogger.instance
# => #<MyLogger:0x007f8ec4833060 @filepath="some/default/path">

logger_b = MyLogger.instance
# => #<MyLogger:0x007f8ec4833060 @filepath="some/default/path">

logger_a.info logger_a.object_id
# 70125579507760
# => nil

logger_b.info logger_b.object_id
# 70125579507760
# => nil

logger_c = MyLogger.new('file/path')
# NoMethodError: private method `new' called for MyLogger:Class