新手问题 如何用 Ruby 实现单例模式?

BadTudou · 2018年05月21日 · 最后由 teddyinfi 回复于 2018年05月22日 · 2778 次阅读

Singleton.rb

class Singleton
  @@uniqueInstance = nil
  @count = 0

  def self.instance
    unless @@uniqueInstance
      @@uniqueInstance = new
    end
    @@uniqueInstance
  end

  def plus
    @count += 1
  end

  private_class_method :new
end

test/Counter.rb

require '../Singleton.rb'

singleton_one = Singleton.instance
puts singleton_one.object_id

singleton_two = Singleton.instance
puts singleton_two.object_id

singleton_three = Singleton.instance
puts singleton_three.object_id

p singleton_one.plus
p singleton_two.plus
p singleton_three.plus

运行结果: Singleton.rb:13:in plus': undefined method+' for nil:NilClass (NoMethodError)

我的想法是:单例模式保证Singleton.instance获取到的是同一个实例,而count是实例变量,每次执行xxx.plus,都会让实例变量count的值加 1。

可以把@count改为@@count,即将实例变量改为类变量,我感觉这样修改并不太好 (只是感觉,也不知道具体是哪里不好)。

问这个问题的本质是想不用内置的 Singleton 来实现单例模式

class Singleton
  @@uniqueInstance = nil

  def self.instance
    unless @@uniqueInstance
      @@uniqueInstance = new
    end
    @@uniqueInstance
  end

  def initialize
    @count = 0
  end

  def plus
    @count += 1
  end

  private_class_method :new
end

为啥不用内置的... 就是为了把简单问题复杂化?

Module 就是单例

luikore 回复

因为想自己实现单例模式,尽可能少用 Ruby 本身提供的特性

IChou 回复

可以解释一下,为什么

@@uniqueInstance = nil
@count = 0

不行,但是

def initialize
    @count = 0
end

就 OK? 非常感谢

楼主 java 转过来的吗?

lithium4010 回复

不是,代码是从 Java 转过来的,《Head First 设计模式》

BadTudou 回复

感觉日常开发单例用的很少

BadTudou 回复

不推荐在 Ruby 用这些东西,会让你的代码变得低效而多 bug

BadTudou 回复

关于 5 楼的问题

class C
  @a 
end

以及

class C
  def self.m
    @a 
  end
end

中的实例变量 @a 是类单例类(C 的 singleton_class)的实例变量,不是 C 的实例变量,与 C 实例变量拥有不同的作用域,可以搜索 ruby singleton class。

下面才是 C 这个类的实例变量

class C
  def m
    @a 
  end
end
BadTudou 回复
class Singleton
  # 这个 count 准确的说是『类的实例变量』,因为 Singleton 这个类本身也是 Class 这个的父类的一个实例
  # 它的行为更接近类变量,用来储存类的一些状态
  # 区别是它不能在类的实例里面直接被调用(当然你可以再给它定义一个 getter/setter)
  @count = 0

  def initialize
    # 这才是我们通常意识里面的实例变量
    # 并不存在于类中,也不能在类里面声明或调用
    # 只在类的实例里面使用
    # 当然,你也可以在任何一个实例方法里面去声明或这修改它
    # attr_read、attr_write、attr_accessor 声明的也是这种的
    @count = 0
  end
end

这两个 count 其实不是同一个东西

pinewong 回复

多谢您的讲解

IChou 回复

谢谢,您的讲解非常详细,非常有帮助

记得 matz 书里开玩笑说最简单的单例模式就是程序员自己约定好

class Klass
   include Singleton
   # ...
end
a = Klass.instance
BadTudou 关闭了讨论。 05月23日 11:11
需要 登录 后方可回复, 如果你还没有账号请 注册新账号