创建类模式(五):单例(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.单例具有几种模式,最简单的两种分别是 ...
随机推荐
- Android custom View AirConditionerView hacking
package com.example.arc.view; import android.content.Context; import android.graphics.Canvas; import ...
- 文件IO
在unix世界中视一切为文件,无论最基本的文本文件还是网络设备或是u盘,在内核看来它们的本质都是一样的.大多数文件IO操作只需要用到5个函数:open . read . write . lseek 以 ...
- ORACLE 临时表空间清理
Oracle临时表空间主要用来做查询和存放一些缓冲区数据.临时表空间消耗的主要原因是需要对查询的中间结果进行排序.临时表空间的主要作用: 索引create或rebuildOrder by 或 grou ...
- JSP的九个隐式(内置)对象
1.out 转译后对应JspWriter对象,其内部关联一个PrintWriter对象.是向客户端输出内容常用的对象. 2.request 转译后对应HttpServletRequest对象.客户端的 ...
- html中的特殊符号
html中的特殊符号 符号 说明 编码 符号 说明 编码 符号 说明 编码 " 双引号 " × 乘号 × ← 向左箭头 ← & AND符号 & ÷ 除号 ÷ ...
- 【ASP.NET Web API教程】1.1 第一个ASP.NET Web API
Your First ASP.NET Web API (C#)第一个ASP.NET Web API(C#) By Mike Wasson|January 21, 2012作者:Mike Wasson ...
- linux中ulimit作用
一.作用 Linux对于每个用户,系统限制其最大进程数.为提高性能,可以根据设备资源情况,设置各linux 用户的最大进程数. ulimit主要是用来限制进程对资源的使用情况的,它支持各种类型的限制, ...
- cocoa中获取时间
头文件#import "Foundation/NSCalendarDate.h" + (id)calendarDate; - (int)yearOfCommonEra;- (int ...
- JS操作文件
) ; ; fso ); f1.Close(); // 读取文件的内容. // Response.Write("Reading file <br>") ...
- (转)android中利用 ViewPage 实现滑动屏
最近实现了这样的一个效果:滑动界面出现拖拽效果,可翻动3屏,也可点击按钮翻动页面. 主要利用android.support.v4.view.ViewPager控件来实现. 第一个界面: 滑动屏幕: 换 ...