创建类模式(五):单例(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.单例具有几种模式,最简单的两种分别是 ...
随机推荐
- 850 USB 烧录模式
/************************************************************************* * 850 USB 烧录模式 * 说明: * 本文 ...
- 20160126.CCPP体系详解(0005天)
程序片段(01):eatmem.c 内容概要:语句和逻辑结构 #include <stdio.h> #include <stdlib.h> #include <Windo ...
- MAC自动备份数据到服务器
需求:公司内部使用自己电脑,回家需要使用另一台电脑,所以想时时备份公司用的电脑中文件.代码到服务器上,回家就可以用啦 1 无密码使用scp (1)第一步:生成密匙对,我用的是rsa的密钥.使用命令 & ...
- ecshop 广告位固定
不知道ECSHOP用户们发现没有,如果在一个广告位中添加多个广告图片, 在前台显示的时候,每刷新一次,图片的显示顺序就会随机变化一次. 注:如果给广告位只添加一个图片是没有这种问题的. 现在的问题是: ...
- PHP实现站点pv,uv统计(三)
数据分析脚本如下: //error_reporting(0);date_default_timezone_set("PRC");$environment = get_cfg_var ...
- SQLServer如何快速生成100万条不重复的随机8位数字
最近在论坛看到有人问,如何快速生成100万不重复的8位编号,对于这个问题,有几点是需要注意的: 1. 如何生成8位随机数,生成的数越随机,重复的可能性当然越小 2. 控制不重复 3. ...
- Asp.net 身份验证方式?
[Forms 身份验证] 通过其可将没有通过身份验证的请求重定向到使用 HTTP 客户端重定向的 HTML 窗体的系统.用户提供凭据并提交该窗体.如果应用程序验证该请求,系统就会发出包含凭据或密钥的 ...
- 13、NFC技术:读写非NDEF格式的数据
MifareUltralight数据格式 将NFC标签的存储区域分为16个页,每一个页可以存储4个字节,一个可存储64个字节(512位).页码从0开始(0至15).前4页(0至3)存储了NFC标签相关 ...
- C++读取二进制文件(某特定格式)
该格式也不复杂,就是一个二进制文件,格式为:8个通道,每个通道2字节,都为整数,最后两个通道都是0x03FF == 1023d,文件中中若干个8通道. 有个小细节就是:下面代码中 infile.rea ...
- git初步使用
git初步使用 主要目的:使用代码控制工具,练习使用git 1.创建新项目 网址如下: https://github.com/kellyseeme?tab=repositories 注意每个人使用的名 ...