Ruby实现单例模式

来源:互联网 发布:c 数组的长度 编辑:程序博客网 时间:2024/06/12 00:59

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

require 'Singleton'class Testinclude Singletonenda, b = Test.instance, Test.instanceputs a==b    #=>true

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

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

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

String was inherited by T

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

module Mdef self.included(base)puts "#{self} included by #{base}"endendclass Test include Mend# => M included by Test

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

module Singletondef self.included(base)base_eigenclass = class << base; self; endbase_eigenclass.class_eval doinstance_variable_set("@instance", base.new) define_method :instance doinstance_variable_get "@instance"endendendend

使用如下代码测试:

# test for singletonclass Testinclude Singletonenda, b = Test.instance, Test.instanceputs a==b    #=>true

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

module Singletondef self.included(base)base_eigenclass = class << base; self; endbase_eigenclass.class_eval doinstance_variable_set("@instance", base.new) define_method :instance doinstance_variable_get "@instance"enddefine_method :new doraise "can't new in singleton class"endendendend

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

class Testinclude Singletonenda = Test.instance

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

module Singletondef self.included(base)base.instance_variable_set("@instance", base.new)base.extend(self)enddef newraise "can't new in singleton class"enddef instanceinstance_variable_get("@instance")endend

使用和测试如下:

# test for singletonclass Testinclude Singletonenda, b = Test.instance, Test.instanceputs a==b    #=>true



原创粉丝点击