在其它语言中,我们要千方百计的实现单例模式,比如先要把构造方法修饰为私有。然后把 clone()
方法也设置为私有,然后再创建一个静态方法 getInstance()
来获得单例。
Ruby 呢,直接给我们实现了一个单例模块 Singleton
,我们只要把这个模块往类里一放,成了,自动就可以获得一个 instance
方法,这个方法会返回 Singleton
模块为我们创建的单例模块。
本章节呢,我们主要讲解三个方面的内容
- 单例模式
Singleton
模块- 延迟加载
单例模式
不知道你有没有听说过这句话 : 类只是蓝图( blueprint ),程序员更偏爱对象。
类只是一个蓝图,它就像建筑图纸对于实际的高楼大厦,就像我们写程序时大脑的构思对应于实际上线的工程代码。
从某些方面说,对象,就是类的内存表示。
对象封装了数据和行为,就像类中定义的那样。
千万要注意,面向对象的三大特性,类只是负责定义,实际上的三大特性,是由对象来完成的。
对于某个类,我们可以创建无穷多个对象,且不会受到任何限制。这看起来非常酷。
但是,有时候,系统只需要类的一个实例来协调整个程序的操作,并不需要第二个实例。
对于这种场景,我们希望确保正在运行的程序只存在给定类的一个实例。
这种时刻,单例模式 就派上用场了。
单例模式 是一种设计模式,它将给定类的实例化限制为一个对象。
现在,我们来看看 Ruby 如何实现单例模式。
Ruby 中的 Singleton
模块
因为单例模式如此的常见和重要,Ruby 特意为此提供了一个模块 Singleton
。
singleton
模块包含了在类中实现单例模式的所有逻辑。
Ruby 中,为一个类实现单例模式只需要简单的两步
-
加载
singleton
模块文件require 'singleton'
由于
Singleton
模块不是 Ruby 核心库的一部分,因此必须使用require 'singleton'
来加载库。 该库包含Singleton
模块定义 -
创建一类,并在类中包含
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()
方法时才创建,并不是在加载类时就创建了。