Ruby设计模式编程中单例模式的应用详解
时间:2026-08-17 | 作者:白桃企划师 | 阅读:0单例模式可以说是设计模式里最基础、也最常用的一种。它的核心目标很明确:让某个类在系统中只存在一个实例,并且这个实例能被全局访问。
这个概念听起来简单,但实现时有不少细节需要注意。
简介
说白了,单例模式就是要把类的实例化过程控制住,不让外部随意new对象。
通常的做法是让类自己管理自己的唯一实例,对外提供一个静态方法(类方法)来获取这个实例。
为什么必须是静态方法?因为如果用一个实例去生成另一个唯一实例,本身就是矛盾的。你还没拿到唯一的那个实例,自然也无法依赖它去创建对象。
要点
单例模式要满足三个条件:
- 类只能有一个实例
- 这个实例必须由类自己来创建
- 它必须自己把这个实例提供给整个系统
从实现角度来说,对应的就是:
- 私有化构造函数(让外部不能直接new)
- 在类内部维护一个静态的私有实例
- 提供一个公开的静态方法返回这个实例
singleton
先看一个简单的例子,用类变量来模拟单例行为:
class ClassVariableTester
@@class_count = 0
def initialize
@instance_count = 0
end
def increment
@@class_count = @@class_count + 1
@instance_count = @instance_count + 1
end
def to_s
"class count :#{@@class_count} -- instance count :#{@instance_count}"
end
end
cv1 = ClassVariableTester.new
cv1.increment
cv1.increment
puts("cv1:#{cv1}")
cv2 = ClassVariableTester.new
puts("cv2:#{cv2}")
#cv1:class count :2 -- instance count :2
#cv2:class count :2 -- instance count :0
这里有个关键点:类变量 @@class_count 被所有实例共享。
所以当cv1调用了两次increment后,@@class_count 变成2;创建cv2时,这个类变量仍然是2。
而实例变量 @instance_count 只属于当前对象,所以cv2的实例变量是0。
这种类变量的共享特性,其实就是单例模式的一种朴素体现。
但真正的单例模式需要更严格的控制。下面是一个经典实现:
class SimpleLogger @@instance = SimpleLogger.new def self.get_instance @@instance end private_class_method :new end sl1 = SimpleLogger.get_instance sl2 = SimpleLogger.get_instance puts sl1 == sl2
输出结果是 true。
这里用类变量 @@instance 保存唯一实例,通过类方法 get_instance 返回。
但注意,SimpleLogger.new 仍然能创建新实例。所以必须把 new 方法设为私有,这行代码 private_class_method :new 就是干这个的。
如果尝试直接调用 SimpleLogger.new,会得到:
private method `new' called for SimpleLogger:Class (NoMethodError)
Ruby 标准库提供了一个更简洁的 singleton 模块,几行代码就能搞定:
require 'singleton' class SimpleLogger include Singleton end #puts SimpleLogger.new sl1 = SimpleLogger.instance sl2 = SimpleLogger.instance puts sl1 == sl2
输出仍然是 true。
混入 Singleton 之后,类自动获得了类变量、初始化、 instance 方法,并且 new 被设为私有。
通过 SimpleLogger.instance 就能获取唯一的实例。
两种实现方式的区别
这两种方式有个关键区别:
- 第一种是“勤性单例”(eager instantiation),在类加载时就创建了实例
- 第二种是“惰性单例”(lazy instantiation),只有在第一次调用
instance时才创建
不过,Singleton 模块并不是绝对安全的。
你可以通过 public_class_method :new 把 new 重新设为公开,然后就能直接 SimpleLogger.new 创建新对象了:
class SimpleLogger public_class_method :new end puts SimpleLogger.new
所以,使用单例模式时,最好在团队中约定好使用方式,避免被轻易绕过。
替代思路
再来看看两种常见的替代思路:
使用全局变量
尽量别用。因为全局变量会让代码耦合得死死的。
其实单例模式本质上和全局变量作用一样,但更优雅。比如 $logger = SimpleLogger.new 这种写法,虽然简单,但破坏了封装。
使用类本身作为单例
把类方法直接当作实例方法使用,也能达到类似效果:
class SimpleLogger
WARNING = 1
INFO = 2
def initialize(file)
@@log = File.open(file, "w")
@@level = WARNING
end
def self.warning(msg)
puts @@level > WARNING
@@log.puts(msg) if @@level > WARNING
@@log.flush
end
def self.level
@@level
end
def self.level=(new_level)
@@level = new_level
end
end
SimpleLogger.new("test.txt")
puts SimpleLogger.level
SimpleLogger.level = SimpleLogger::INFO
puts SimpleLogger.level
SimpleLogger.warning("warning")
这种方式下,所有方法都是类方法,数据存储在类变量中,本质上也是单例。
实例
最后看一个实际应用场景:用单例模式管理浏览器自动化测试的实例。
require 'rubygems'
require 'watir'
require 'singleton'
class AutoTest
include Singleton
def OpenUrl(url)
@browser= Watir::Browser.new
@browser.goto(url)
@url=url
end
def set_textarea(text)
@browser.text_field(:id,'kw').set(text)
end
def click
@browser.button(:id,'su').click
end
end
test,test2 = AutoTest.instance
test.OpenUrl('http://www.baidu.com')
test.set_textarea('aslandhu')
test.click
注意这里虽然写了 test, test2 = AutoTest.instance,但第二个变量实际上接收的是 nil。
因为 instance 方法只会返回同一个实例,所以 test2 并没有成功创建。
再看另一种写法,试图用类方法直接操作单例:
require 'rubygems' require 'watir' require 'singleton' require 'thread' class TestOneObj end class <
这段代码试图创建两个 Browser 对象,但事实上 test 和 test2 是同一个对象。
虽然可能会打开两个浏览器窗口,因为每次调用 instance 都执行了 Watir::Browser.new,但 Ruby 对象的引用是同一个。
test 和 test2 指向同一个实例。
这提醒我们:单例模式控制的是对象的创建,而不是底层资源的重复创建。具体实现时,需要把边界考虑清楚。
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- 使用RVM切换Ruby与Rails版本的实现方法
- 时间:2026-08-18
-
- Ruby语言是什么及入门使用方法
- 时间:2026-08-18
-
- Ruby on Rails网站项目搭建入门指南
- 时间:2026-08-18
-
- Ruby插入排序算法实现与二路插入排序代码示例
- 时间:2026-08-18
-
- Ruby图片滤镜算法实现代码与核心原理
- 时间:2026-08-18
-
- Ruby中Hash哈希结构基本操作方法详解
- 时间:2026-08-18
-
- Ruby面向对象编程:类方法与类扩展详解
- 时间:2026-08-18
-
- Ruby正则表达式语法详解与常用示例代码
- 时间:2026-08-18
精选合集
更多大家都在玩
大家都在看
更多-
- 糖尿病完全不能吃糖吗
- 时间:2026-09-15
-
- 蚂蚁庄园小课堂2026年9月16日最新题目答案
- 时间:2026-09-15
-
- 小鸡答题今天的答案是什么2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园每日答题答案2026年9月16日
- 时间:2026-09-15
-
- 以下哪种粮食是酿造绍兴黄酒的主要原料 蚂蚁庄园今日答案9月16日
- 时间:2026-09-15
-
- 劝学名句“及时当勉励,岁月不待人”出自哪位诗人 蚂蚁庄园今日答案9.16
- 时间:2026-09-15
-
- 蚂蚁庄园今天答题答案2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园答题今日答案2026年9月16日
- 时间:2026-09-15
