创建类模式(五):单例(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.单例具有几种模式,最简单的两种分别是 ...
随机推荐
- 添加第三方类库造成的linker command failed with exit code 1 (use -v to see invocation)的错误调试
linker command failed with exit code 1 (use -v to see invocation)这个错误貌似遇见并不止一次,当我想用某个第三方类库的时候(如SBJso ...
- shell 括号学习
http://blog.csdn.net/tttyd/article/details/11742241 http://tldp.org/LDP/abs/html/loops1.html
- 【英语】Bingo口语笔记(9) - 表示“不相信”
- 【转】XCode环境变量及路径设置 -- 待学习
原文网址:http://www.cnblogs.com/oc-bowen/p/5140541.html 一般我们在xcode里面配置包含工程目录下头文件的时候,都要关联着相对路径和绝对路径,如果只是自 ...
- 别人的的MYSQL学习心得(十五) 日志
我的MYSQL学习心得(十五) 日志 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
- Python学习2-列表和元组
Python学习2-列表和元组 标签(空格分隔): 列表 元组 在Python中,最基本的数据结构是序列(sequence).序列中的每个元素被分配一个序号--即元素的位置,也称为索引.索引从0开始. ...
- 18个jQuery Mobile开发贴士和教程
jQuery Mobile 是 jQuery 在手机上和平板设备上的版本.jQuery Mobile 不仅会给主流移动平台带来jQuery核心库,而且会发布一个完整统一的jQuery移动UI框架.支持 ...
- 嵌入式 Linux下curl库API简单介绍
1:CURLcode curl_global_init(long flags); 这个函数全局需要调用一次(多次调用也可以,不过没有必要), 所以这也是把Curlplus设计成单体类的原因,curl_ ...
- GitHub的代码托管和使用方法
原文 GitHub托管 借助GitHub托管你的项目代码 PS:话说自己注册了GitHub都很久了,却没有怎么去弄,现在系统学习一下,也把自己的学习经历总结下来share给大家,希望大家都能把Git ...
- Go 语言做的几个验证码
1.http://www.oschina.net/code/snippet_173630_12006 : 效果: 源代码: 1: package main 2: 3: import ( 4: cra ...