Ruby实现单例模式

单例模式是一种常用的设计模式,在ruby的标准库中有singleton模块,可以直接像下面这样使用:

require 'Singleton'

class Test
	include Singleton
end

a, b = Test.instance, Test.instance
puts a==b    #=>true

如果不使用标准库,该如何实现呢?在看完《ruby元编程》第五章后,似乎有了一些思路决定自己实现一个(备注:和标准库的实现方式可能不一致,还没有看过标准库)。首先需要一些准备知识:钩子函数Hook Method(回调函数),Ruby在做一些事情的时候,会回调一些函数。如继承。例子代码如下:

class String 
	def self.inherited(subclass)
		puts "#{self} was inherited by #{subclass}"
	end
end

class T < String; end

类在继承时会调用Class#inherited方法。所以上述代码会输出:

String was inherited by T

还有其它很多的钩子函数,这里用到的一个就是included方法,当模块被包含的时候,会调用该方法。例子代码如下:

module M
	def self.included(base)
		puts "#{self} included by #{base}"
	end
end

class Test 
	include M
end

# => M included by Test

有了以上的工具就可以实现Singleton了。如下代码实现了instance函数:

module Singleton
	def self.included(base)
		base_eigenclass = class << base; self; end
		base_eigenclass.class_eval do
			instance_variable_set("@instance", base.new) 
			define_method :instance do
				instance_variable_get "@instance"
			end
		end
	end
end

使用如下代码测试:

# test for singleton
class Test
	include Singleton
end

a, b = Test.instance, Test.instance
puts a==b    #=>true

单例模式为了保证只有一个方式来得到类实例,不能使用new函数来生成实例,在类定义中,我们可以使用private:new 来讲new方法设置为私有,这样就阻止了对new方法的调用。但是在这里如何阻止呢?我们知道new方法是Class类的实例方法,那么通过覆写该方法,使其抛出一个异常,就可以达到目的了。重新修改后,代码如下:

module Singleton
	def self.included(base)
		base_eigenclass = class << base; self; end
		base_eigenclass.class_eval do
			instance_variable_set("@instance", base.new) 
			define_method :instance do
				instance_variable_get "@instance"
			end

			define_method :new do
				raise "can't new in singleton class"
			end
		end
	end
end

通过调用test.new可以看到抛出异常,这样就实现了一个简单的singleton模块。以后通过如下方法即可使用了:

class Test
	include Singleton
end
a = Test.instance

观察以上代码,通过使用Eigenclass来定义base的类方法,有点不够简洁,class中可以通过extend方法直接将Module中的方法加载到Eigenclass,那么何不在Module中直接使用extend呢,修改后,第二个版本的Singleton Module代码如下:

module Singleton
	def self.included(base)
		base.instance_variable_set("@instance", base.new)
		base.extend(self)
	end

	def new
		raise "can't new in singleton class"
	end

	def instance
		instance_variable_get("@instance")
	end
end

使用和测试如下:

# test for singleton
class Test
	include Singleton
end

a, b = Test.instance, Test.instance
puts a==b    #=>true



你可能感兴趣的:(Ruby)