2009-09-27 17 views
25

Estoy tratando de escribir el singleton más seguro en Ruby que pueda. Soy nuevo en el lenguaje, que es tan elástico que no tengo una fuerte sensación de que mi clase Singleton tenga éxito al crear solo una instancia. Como beneficio adicional, me gustaría que el objeto solo se convierta en instancia si realmente se usa.¿Cuál es la forma correcta de escribir un patrón Singleton en Ruby?

Respuesta

43
# 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 

Entonces, ¿qué hace ruby ​​cuando incluye el módulo singleton dentro de su clase?

  1. Hace que el método new sea privado y, por lo tanto, no lo puede usar.
  2. Agrega un método de clase llamado instancia que ejemplifica solo una instancia de la clase.

Así que para utilizar el módulo de rubí Singleton se necesitan dos cosas:

  1. requieren el lib singleton luego incluirlo dentro de la clase deseada.
  2. Utilice el método instance para obtener la instancia que necesita.
16

Si desea crear un singleton, ¿por qué molestarse en crear una clase? Simplemente cree un objeto y agregue los métodos y las variables de instancia que desee.

>> 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 

Una forma más normal que la gente implementar este patrón es utilizar un Module como el objeto Singleton (en lugar de la más genérica 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" 

Si quería que su objeto singleton a heredar de alguna clase, solo haz que sea una instancia de esa clase. Para heredar de un mixin, simplemente use #extend. Si desea un objeto singleton, ruby ​​lo hace realmente fácil, y a diferencia de otros idiomas, no tiene que definirse en una clase.

Los singletons ad-hoc (mi primer ejemplo) están por todas partes, y cubren la mayoría de los casos que he encontrado. El truco del módulo normalmente cubre el resto (cuando quiero algo un poco más formal).

código Ruby debe (en mi humilde opinión) utilizar la tipificación de pato (a través de #respond_to?) en lugar de comprobar de forma explícita la clase de un objeto, por lo que normalmente no se preocupan por la singularidad de la clase mis objetos únicos, ya que no es su clase que lo hace único, pero todo lo que agregué después.

+6

+1 para señalar que no se necesita realmente singleton. Puedes implementar la intención sin copiar servilmente el patrón. –

+0

He estado pensando mucho sobre este punto, ya que pone en tela de juicio la existencia misma del módulo Singleton en Ruby Core. La utilidad potencial de la clase w/singleton mixin me parece doble: 1) si quieres métodos de clase e instancia y 2) si tienes una inicialización complicada que quieres ejecutar perezosamente sin requerir que los métodos del módulo lo activen de alguna manera de manera explícita la primera vez que se accede a ellos ¿Alguien más tiene alguna ventaja del módulo Singleton real? – gtd

Cuestiones relacionadas