Ruby - создать синглтон с параметрами?
Я видел, как определить класс как singleton (как создать singleton в ruby ):
require 'singleton'
class Example
include Singleton
end
Но что, если я хочу дать ему некоторые параметры для этого единственного экземпляра, то есть в примере всегда должны быть инициализированы определенные свойства. Например, скажем, у меня был класс, единственной целью которого является запись в файл (это всего лишь пример), но для этого требуется имя файла для входа в систему, прежде чем он сможет работать.
class MyLogger
def initialize(file_name)
@file_name = file_name
end
end
Как я могу сделать MyLogger одиночным, но убедитесь, что он получает имя_файла?
Ответы
Ответ 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