2011-03-10 68 views
17

我已经知道如何定义一个类为一单(how to create a singleton in ruby):红宝石 - 用参数创建单例?

require 'singleton' 

class Example 
    include Singleton 
end 

但是如果我想给它一些参数对于单个实例,这意味着,该实例应该始终有一定的性能初始化。例如,假设我有一个课程,其唯一目的是登录到一个文件(这仅仅是一个例子),但它需要一个文件的名称才能登录才能工作。

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

我该如何让MyLogger成为一个singleton,但确保它获得file_name?

+1

,你确定你想使用单? – 2011-03-10 12:22:49

+2

是的。我相信有些情况下单身人士是有道理的,但它应该有一些初始配置。 – codecraig 2011-03-10 12:30:49

回答

4

辛格尔顿不提供此功能,但使用的单,你可以自己

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>或至少在第一次通话时,如果您知道它将首次通话。

+0

谢谢,这个工程。 – codecraig 2011-03-10 12:31:08

+0

所以我想我必须在创建@@ __ singleton_instance__之后重写self.new,否则您仍然可以执行MyLogger。新 – codecraig 2011-03-10 12:51:33

+0

好吧,这就是我想出来的: – codecraig 2011-03-10 13:01:24

1

这是太长,投入评论(如计算器说,这是太长)

好了,所以这里就是我想出了:

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 

这将使用“config_instance”来创建和配置单例实例。一旦实例准备就绪,它会重新定义self.instance方法。

它也使'new'类方法在创建第一个实例后保密。

+0

固定在我的答案中,您可以在类级别设置新的女贞,新的将仍然可见def self.instance – mpapis 2011-03-10 13:18:06

13

这里是另一种方式来做到这一点 - 在一个类变量将日志文件名:

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=方法引发异常。如果尚未设置@@file_name,您还可以让initialize发生异常。

+2

为什么麻烦复制@@ file_name到@file_name而不是只使用@@ file_name如果它是单身呢? – Gavriel 2015-10-28 10:12:05

0

简单单如果你想传递参数不依赖于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