Я пытаюсь написать самый безопасный синглтон в Ruby, который я могу. Я новичок в языке, который настолько эластичен, что у меня нет сильного чувства, что мой одноэлементный класс будет успешным при создании только одного экземпляра. В качестве бонуса, я бы хотел, чтобы объект только стал экземплярным, если он действительно использовался.
Каков правильный способ написания шаблона singleton в Ruby?
Ответ 1
# require singleton lib
require 'singleton'
class AppConfig
# mixin the singleton module
include Singleton
# do the actual app configuration
def load_config(file)
# do your work here
puts "Application configuration file was loaded from file: #{file}"
end
end
conf1 = AppConfig.instance
conf1.load_config "/home/khelll/conf.yml"
#=>Application configuration file was loaded from file: /home/khelll/conf.yml
conf2 = AppConfig.instance
puts conf1 == conf2
#=>true
# notice the following 2 lines won’t work
AppConfig.new rescue(puts $!)
#=> new method is private
# dup won’t work
conf1.dup rescue(puts $!)
#=>private method `new’ called for AppConfig:Class
#=>can’t dup instance of singleton AppConfig
Итак, что делает ruby, когда вы включаете модуль singleton внутри своего класса?
- Это делает метод
new
закрытым, поэтому вы не можете его использовать. - Он добавляет метод класса, называемый экземпляром, который создает экземпляр только одного экземпляра класса.
Итак, для использования модуля ruby singleton вам нужны две вещи:
- Требовать lib
singleton
, затем включить его в желаемый класс. - Используйте метод
instance
, чтобы получить необходимый экземпляр.
Ответ 2
Если вы хотите создать синглтон, зачем беспокоиться о создании класса? Просто создайте объект и добавьте нужные ему методы и переменные экземпляра.
>> MySingleton = Object.new
=> #<Object:0x100390318>
>> MySingleton.instance_eval do
?> @count = 0
>> def next
>> @count += 1
>> end
>> end
=> nil
>> MySingleton.next
=> 1
>> MySingleton.next
=> 2
>> MySingleton.next
=> 3
Более стандартный способ реализации этого шаблона - использовать Module
как объект singleton (а не более общий Object
):
>> module OtherSingleton
>> @index = -1
>> @colors = %w{ red green blue }
>> def self.change
>> @colors[(@index += 1) % @colors.size]
>> end
>> end
=> nil
>> OtherSingleton.change
=> "red"
>> OtherSingleton.change
=> "green"
>> OtherSingleton.change
=> "blue"
>> OtherSingleton.change
=> "red"
Если вы хотите, чтобы ваш объект singleton наследовался от какого-либо класса, просто сделайте его экземпляром этого класса. Чтобы наследовать из mixin, просто используйте #extend
. Если вам нужен одноэлементный объект, рубин делает его очень легким, и в отличие от других языков его не нужно определять в классе.
Ad-hoc singletons (мой первый пример) повсюду и охватывают большинство случаев, с которыми я столкнулся. Трюк модуля обычно охватывает остальные (когда я хочу что-то более формальное).
Ruby-код должен (imho) использовать утиную печать (через #respond_to?
) вместо явной проверки класса объекта, поэтому я обычно не забочусь о уникальности моего класса объектов singleton, так как это не его класс, который делает он уникален, но все, что я добавил после.
Ответ 3
require 'singleton'
class Klass
include Singleton
# ...
end
См. стандартную библиотеку Ruby синглтонкласса для объяснения.