Ruby 中的 Singleton 模块 ( 上 )

yufei       6 年, 1 月 前       1786

在其它语言中,我们要千方百计的实现单例模式,比如先要把构造方法修饰为私有。然后把 clone() 方法也设置为私有,然后再创建一个静态方法 getInstance() 来获得单例。

Ruby 呢,直接给我们实现了一个单例模块 Singleton,我们只要把这个模块往类里一放,成了,自动就可以获得一个 instance 方法,这个方法会返回 Singleton 模块为我们创建的单例模块。

本章节呢,我们主要讲解三个方面的内容

  1. 单例模式
  2. Singleton 模块
  3. 延迟加载

单例模式

不知道你有没有听说过这句话 : 类只是蓝图( blueprint ),程序员更偏爱对象

类只是一个蓝图,它就像建筑图纸对于实际的高楼大厦,就像我们写程序时大脑的构思对应于实际上线的工程代码。

从某些方面说,对象,就是类的内存表示。

对象封装了数据和行为,就像类中定义的那样。

千万要注意,面向对象的三大特性,类只是负责定义,实际上的三大特性,是由对象来完成的。

对于某个类,我们可以创建无穷多个对象,且不会受到任何限制。这看起来非常酷。

但是,有时候,系统只需要类的一个实例来协调整个程序的操作,并不需要第二个实例。

对于这种场景,我们希望确保正在运行的程序只存在给定类的一个实例。

这种时刻,单例模式 就派上用场了。

单例模式 是一种设计模式,它将给定类的实例化限制为一个对象。

现在,我们来看看 Ruby 如何实现单例模式。

Ruby 中的 Singleton 模块

因为单例模式如此的常见和重要,Ruby 特意为此提供了一个模块 Singleton

singleton 模块包含了在类中实现单例模式的所有逻辑。

Ruby 中,为一个类实现单例模式只需要简单的两步

  1. 加载 singleton 模块文件

    require 'singleton'
    

    由于 Singleton 模块不是 Ruby 核心库的一部分,因此必须使用 require 'singleton' 来加载库。 该库包含 Singleton 模块定义

  2. 创建一类,并在类中包含 Singleton 模块

    require 'singleton'
    
    class Demo
      include Singleton
    
      attr_accessor :version, :state
    end
    

这样,类 Demo 就自动拥有了单例模式。要得到这个类的单例,可以通过 instance 方法。

Demo.instance # => #<Demo:0x00007fc25a1b8d10>

不管我们调用多少次 instance 方法,返回的都是同一个实例

Demo.instance # => #<Demo:0x00007fc25a1b8d10>
Demo.instance # => #<Demo:0x00007fc25a1b8d10>
Demo.instance # => #<Demo:0x00007fc25a1b8d10>
Demo.instance # => #<Demo:0x00007fc25a1b8d10>

如果我们访问单例模式下的类的 new 方法,会被告知这个方法不存在

Demo.new      # => NoMethodError .

那,是不是包含了 Singleton 模块,只能通过 instance() 方法来获得这个单例呢?

在大部分情况下,是正确的,但是,少数几种情况,则不是,比如

Demo.send(:new) # => #<Demo:0x00007ffca20fd998>
Demo.send(:new) # => #<Demo:0x00007ffca2119df0>
Demo.send(:new) # => #<Demo:0x00007ffca20e8c78>

结果让然大跌眼镜,原来消息的方式可以创建多个实例。

这怎么可能?

当然可能了!

当包含 Singleton 模块时,只是将 Demo.new 方法变为私有 ( private )。

有关更多 private 方面的知识,可以访问我们的 Ruby 中 private 和 protected 的消息 ( Message ) 机制

这一步,会给人一种错觉,认为 Demo 类不可实例化。

而问题则出现 Object#send 方法能够调用给定类的私有方法。

因此,在使用 Singleton 模块时请注意这一事实。

延迟加载

这个话题,有点无厘头。

算了,我们先来看看使用了 Singleton 模块的一个范例

demo.rb

require 'singleton'

class Demo
  include Singleton
end

p ObjectSpace.each_object(Demo){}

p Demo.instance                  
p ObjectSpace.each_object(Demo){}

p Demo.instance                   
p ObjectSpace.each_object(Demo){}

运行结果如下

[root@www.twle.cn ruby]# ruby demo.rb
0
#<Demo:0x00007f865f0a33a8>
1
#<Demo:0x00007f865f0a33a8>
1

是不是发现了什么不得了的事情?

Singleton 模块生成的单例,竟然是在首次调用 instance() 方法时才创建,并不是在加载类时就创建了。

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.