创建类模式(五):单例(Singleton)
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式一般情况下通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化。
和静态变量的区别
虽然都是在任意地方可以访问到,但是静态变量或全局变量不能限制一个应用中只存在指定类的一个实例,而单例可以。
线程安全
如果是多线程应用,需要在创建时进行加锁操作,否则可能会产生多个实例。
UML
优点
- 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
- 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
- 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
缺点
- 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。
- 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
- 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。
应用场景
- 要求生成唯一序列号的环境;
- 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
- 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
- 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。
示例
实现一个单例并调用其一个方法。
C++
线程不安全模式:
Main.cpp
#include "stdafx.h"
#include "stdlib.h"
#include "Mgr.h" int _tmain(int argc, _TCHAR* argv[])
{
Mgr::GetInstance()->doSomething(); system("pause");
return ;
}
Mgr.h
#pragma once
//单例, 线程不安全.
class Mgr
{
public:
//获取唯一实例.
static Mgr* GetInstance();
//方法.
void doSomething();
private:
static Mgr* _instance;
//私有构造函数, 保证不能在外部被实例化.
Mgr();
}
Mgr.cpp
#include "stdafx.h"
#include <iostream>
#include "Mgr.h" using namespace std; Mgr* Mgr::_instance = ; Mgr* Mgr::GetInstance()
{
if(_instance == )
{
_instance = new Mgr();
}
return _instance;
} Mgr::Mgr()
{
} void Mgr::doSomething()
{
cout << "单例 Mgr 干了某事。" << endl;
}
线程安全模式:
C++的多线程需要借助一些外部库实现,所以线程安全的单例也要根据不同的类库来看其不同的实现:
Boost(这篇文章同时也包括了类库无关的通用线程安全模式):http://www.cnblogs.com/ccdev/archive/2012/12/19/2825355.html
pThread:http://blog.csdn.net/joanlynnlove/article/details/7462254
C#
using System; namespace DesignPattern
{
class Program
{
static void Main(string[] args)
{
Mgr.GetInstance().DoSomething(); Console.Read();
}
} /// <summary>
/// 单例, 线程安全, 不能继承所以作为密封类存在.
/// </summary>
public sealed class Mgr
{
private static Mgr _instance;
private static readonly object lockObj = new object(); /// <summary>
/// 获取唯一实例.
/// </summary>
/// <returns>唯一实例.</returns>
public static Mgr GetInstance()
{
if(_instance == null)
{
lock(lockObj)
{
if(_instance == null)
{
_instance = new Mgr();
}
}
}
return _instance;
} /// <summary>
/// 私有构造函数, 保证不能在外部被实例化.
/// </summary>
private Mgr()
{
} /// <summary>
/// 方法.
/// </summary>
public void DoSomething()
{
Console.WriteLine("单例 Mgr 干了某事。");
}
}
}
Java
Main.java
public class Main
{
public static void main(String[] args)
{
Mgr.getInstance().doSomething();
}
}
Mgr.java
/**
* 单例, 线程安全, 不能继承所以作为最终类存在.
*/
public final class Mgr
{
private static Mgr _instance; /**
* 获取唯一实例.
* @return 唯一实例.
*/
public static Mgr getInstance()
{
if(_instance == null)
{
synchronized(Mgr.class)
{
if(_instance == null)
{
_instance = new Mgr();
}
}
}
return _instance;
} /**
* 私有构造函数, 保证不能在外部被实例化.
*/
private Mgr()
{
} /**
* 方法.
*/
public void doSomething()
{
System.out.println("单例 Mgr 干了某事。");
}
}
AS3
Main.as
package
{
import flash.display.Sprite; public class Main extends Sprite
{
public function Main()
{
Mgr.getInstance().doSomething();
}
}
}
Mgr.as
package
{
import flash.concurrent.Mutex; /**
* 单例, 线程安全, 不能继承所以作为最终类存在.
*/
public final class Mgr
{
private static var _instance:Mgr;
private static var _mutex:Mutex = new Mutex(); /**
* 获取唯一实例.
* @return 唯一实例.
*/
public static function getInstance():Mgr
{
if(_instance == null)
{
_mutex.lock();
try
{
if(_instance == null)
{
_instance = new Mgr(new SingletonEnforcer());
}
}
finally
{
_mutex.unlock();
}
}
return _instance;
} /**
* AS3 中没有私有构造函数, 所以使用包外类来保证其不能在外部被实例化.
* @param singletonEnforcer 单例类实现对象.
*/
public function Mgr(singletonEnforcer:SingletonEnforcer)
{
if(singletonEnforcer == null)
{
throw new Error("单例类不能进行实例化!");
}
} /**
* 方法.
*/
public function doSomething():void
{
trace("单例 Mgr 干了某事。");
}
}
} class SingletonEnforcer{}
我的经验总结
单例模式是很常见的模式,同时其存在比较多的变种,下面说说我遇到过的比较优秀的单例模式的变种模式。
扩展:多例模式
单例模式在Flex框架中的变种
Adobe的Flex框架中的Manager使用的模式是在单例的基础上进行的扩展的一种模式,其目的是为了方便替换实现类,大家可以自己下载Flex SDK来查看源码,或者来看我仿写的一个简单的例子:
1.管理类的接口
首先我们需要一个接口来规定这个管理类需要实现的方法:
package
{
public interface IMgr
{
function doSomething():void;
}
}
2.管理类的实现类
实现管理类接口的实现类,我们可以根据需求自己添加多个新的管理实现类:
实现一:
package
{
public class MgrImpl implements IMgr
{
public function doSomething():void
{
trace("调用了管理类的实现类的方法。");
}
}
}
实现二:
package
{
public class MyMgrImpl implements IMgr
{
public function doSomething():void
{
trace("调用了我自定义的管理类的实现类的方法。");
}
}
}
3.注入类
我们需要一个类来指定我们的管理类实际上使用的实现类是谁:
package
{
import flash.utils.Dictionary; public class Injector
{
private static var _classMap:Dictionary = new Dictionary(); public static function setClass(key:Class, value:Class)
{
_classMap[key] = value;
} public static function getClass(key:Class):Object
{
return new _classMap[key]();
}
}
}
4.管理类
这个管理类是一个静态类,我们可以看做一个壳子,实际上是专门给外部调用的:
package
{
public final class Mgr
{
private static var _impl:IMgr; private static function get impl():IMgr
{
if(_impl == null)
{
_impl = IMgr(Injector.getClass(Mgr));
}
return _impl;
} public static function doSomething():void
{
impl.doSomething();
}
}
}
测试
使用第一个实现类:
package
{
import flash.display.Sprite; public class Main extends Sprite
{
public function Main()
{
Injector.setClass(Mgr, MgrImpl);
Mgr.doSomething();//调用了管理类的实现类的方法。
}
}
}
使用第二个实现类:
package
{
import flash.display.Sprite; public class Main extends Sprite
{
public function Main()
{
Injector.setClass(Mgr, MyMgrImpl);
Mgr.doSomething();//调用了我自定义的管理类的实现类的方法。
}
}
}
创建类模式(五):单例(Singleton)的更多相关文章
- OpenJDK源码研究笔记(十三):Javac编译过程中的上下文容器(Context)、单例(Singleton)和延迟创建(LazyCreation)3种模式
在阅读Javac源码的过程中,发现一个上下文对象Context. 这个对象用来确保一次编译过程中的用到的类都只有一个实例,即实现我们经常提到的"单例模式". 今天,特意对这个上下文 ...
- spring mvc 的Controller类默认Scope是单例(singleton)的
使用Spring MVC有一段时间了,之前一直使用Struts2,在struts2中action都是原型(prototype)的, 说是因为线程安全问题,对于Spring MVC中bean默认都是(s ...
- 跨应用程序域(AppDomain)的单例(Singleton)实现
转载自: 跨应用程序域(AppDomain)的单例(Singleton)实现 - CorePlex代码库 - CorePlex官方网站,Visual Studio插件,代码大全,代码仓库,代码整理,分 ...
- 如何使用双重检查锁定在 Java 中创建线程安全的单例?
这个 Java 问题也常被问: 什么是线程安全的单例,你怎么创建它.好吧,在Java 5之前的版本, 使用双重检查锁定创建单例 Singleton 时,如果多个线程试图同时创建 Singleton 实 ...
- 设计模式之创建类模式大PK
创建类模式大PK 创建类模式包括工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,他们能够提供对象的创建和管理职责.其 ...
- 设计模式之创建类模式PK
创建类模式包括: 工厂方法模式 建造者模式 抽象工厂模式 单例模式 原型模式 创建类模式能够提供对象的创建和管理职责. 其中单例模式和原型模式非常容易理解, 单例模式是要保持在内存中只有一个对象,原型 ...
- ZT 创建类模式总结篇
创建类模式总结篇 分类: 设计模式 2012-03-26 09:03 7320人阅读 评论(11) 收藏 举报 编程优化设计模式任务 创建类模式主要关注对象的创建过程,将对象的创建过程进行封装,使客户 ...
- 创建类模式大PK(总结)
创建类模式包含工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,它们都可以提供对象的创建和管理职责.当中的单例模式和原型模式很easy理解,单例模式是要保持在内存中仅仅有一个对象,原型模式是 ...
- [Android面试题-7] 写出一个Java的Singleton类(即单例类)
1.首先明确单例的概念和特点: a>单例类只能有一个实例 b>单例类必须自己创建一个自己的唯一实例 c>单例类必须为其他所有对象提供这个实例 2.单例具有几种模式,最简单的两种分别是 ...
随机推荐
- mysql 数据库常用命令总结
(1)查看数据库可以支持的存储引擎 命令:show engines; (2)查看表结构命令:desc table_name:(3)显示表的创建语句 show create table ta ...
- ecshop 商品详情页显示同类别下的推荐商品
1.打开goods.php文件找到下面代码 $smarty->assign('goods_rank', get_goods_rank($goods_id)); // 商品的销售排名 在上面的代码 ...
- ecshop 在首页每个商品下显示已销售数量
1.在includes/lib_goods.php文件末尾加入以下代码 function get_buy_sum($goods_id) { $sql = "select sum(goods_ ...
- HDU5400 Arithmetic Sequence
解题思路:这题看懂题目是很关键的,这个区间是等差数列,且公差为d1或d2, 特别注意单个数字也为等差数列.每次求出等差数列序列长度,然后 求出对应这种长度对应有多少种组合方式,累加起来就是结果. ...
- Hdu 1521 排列组合
a1 n1 a2 n2 ... ak nkn=n1+n2+...+nk从n个数中选r个排列(不是组合噢)// 指数型母函数// 模板#include <iostream> #include ...
- java-swing在组件中显示信息
package com.http; import java.awt.*; import javax.swing.*; public class TestSwing2 { //创建了一个能够绘制的组件 ...
- 【进阶——最小费用最大流】hdu 1533 Going Home (费用流)Pacific Northwest 2004
题意: 给一个n*m的矩阵,其中由k个人和k个房子,给每个人匹配一个不同的房子,要求所有人走过的曼哈顿距离之和最短. 输入: 多组输入数据. 每组输入数据第一行是两个整型n, m,表示矩阵的长和宽. ...
- T-SQL:SQL Server-数据开发(经典)
ylbtech-SQL Server-Doc-Help:SQL Server-数据开发(经典) SQL Server 数据开发(经典). 1,数据开发(经典) 返回顶部 1.按姓氏笔画排序: Sele ...
- [转]SqlPlus安装配置
本文转载自http://blog.csdn.net/wuxiaoyan_home/article/details/4826440 一.下载oracle 10g sqlplus软件 http://www ...
- python的元组和列表使用之一
Python的列表和元组 1. 概述 列表是用方括号[]包围的数据集合,不同的成员之间用逗号进行分隔,列表可以通过序号来进行访问其中的成员,可以对列表进行排序.添加.删除操作,改变列表中某 ...