2011-03-10 21 views
17

he visto cómo definir una clase como un conjunto unitario (how to create a singleton in ruby):ruby ​​- crea singleton con parámetros?

require 'singleton' 

class Example 
    include Singleton 
end 

Pero lo que si quiero darle algunos parámetros para esa única instancia, es decir, el Ejemplo siempre debe tener ciertas propiedades inicializado Por ejemplo, supongamos que tengo una clase cuyo único propósito es iniciar sesión en un archivo (esto es solo un ejemplo) pero requiere un nombre de archivo para iniciar sesión antes de que pueda funcionar.

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

¿Cómo puedo hacer de MyLogger un singleton pero me aseguro de que tenga un nombre de archivo?

+1

Si desea pasar parámetros en, ¿está seguro de que desea utilizar un producto único? –

+2

Sí. Creo que hay situaciones en las que un singleton tiene sentido, pero es uno que debería tener alguna configuración inicial. – codecraig

Respuesta

4

Singleton no proporciona esta funcionalidad, pero en lugar de utilizar Singleton se podría escribir por sí mismo

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 

Se debe trabajar, pero no probó el código.

Este código obliga a utilizar MyLogger.instance <file_name> o al menos en la primera llamada si sabe que será la primera vez que llama.

+0

Gracias, esto funciona. – codecraig

+0

Así que creo que tendré que reemplazar a self.new después de crear @@ __ singleton_instance__, de lo contrario, todavía puedes hacer MyLogger.nuevo – codecraig

+0

Ok, esto es lo que se me ocurrió: – codecraig

1

Esto era demasiado tiempo para poner en un comentario (por ejemplo stackoverflow dijo que era demasiado largo)

Ok, así que aquí es lo que ocurrió:

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 

Esto utiliza 'config_instance' a crea y configura la instancia singleton. Reduce el método self.instance una vez que una instancia está lista.

También hace que el método de clase 'nuevo' sea privado después de crear la primera instancia.

+0

corregido en mi respuesta, puede configurar el privet nuevo en el nivel de clase, nuevo será aún visible para def self.instance – mpapis

13

Ésta es otra manera de hacerlo - puso el nombre de archivo de registro en una variable de clase:

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 

Ahora puede utilizar de esta manera:

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

llamadas posteriores a instance volverá el mismo objeto con el nombre de ruta sin cambios, incluso si luego cambia el valor de la variable de clase. Un buen toque adicional sería usar otra variable de clase para realizar un seguimiento de si ya se ha creado una instancia y hacer que el método file_name= presente una excepción en ese caso. También podría hacer que initialize presente una excepción si todavía no se ha configurado @@file_name.

+2

¿Por qué molestarse en copiar @@ file_name en @file_name en lugar de simplemente usar @@ file_name si es un singleton de todos modos? – Gavriel

0

Singleton simple que no depende de Singleton módulo

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 

Ejemplo de uso

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