定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式一般情况下通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化。

和静态变量的区别

虽然都是在任意地方可以访问到,但是静态变量或全局变量不能限制一个应用中只存在指定类的一个实例,而单例可以。

线程安全

如果是多线程应用,需要在创建时进行加锁操作,否则可能会产生多个实例。

UML

优点

  1. 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
  2. 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
  3. 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
  4. 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

缺点

  1. 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。
  2. 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
  3. 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。

应用场景

  1. 要求生成唯一序列号的环境;
  2. 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
  3. 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
  4. 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为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)的更多相关文章

  1. OpenJDK源码研究笔记(十三):Javac编译过程中的上下文容器(Context)、单例(Singleton)和延迟创建(LazyCreation)3种模式

    在阅读Javac源码的过程中,发现一个上下文对象Context. 这个对象用来确保一次编译过程中的用到的类都只有一个实例,即实现我们经常提到的"单例模式". 今天,特意对这个上下文 ...

  2. spring mvc 的Controller类默认Scope是单例(singleton)的

    使用Spring MVC有一段时间了,之前一直使用Struts2,在struts2中action都是原型(prototype)的, 说是因为线程安全问题,对于Spring MVC中bean默认都是(s ...

  3. 跨应用程序域(AppDomain)的单例(Singleton)实现

    转载自: 跨应用程序域(AppDomain)的单例(Singleton)实现 - CorePlex代码库 - CorePlex官方网站,Visual Studio插件,代码大全,代码仓库,代码整理,分 ...

  4. 如何使用双重检查锁定在 Java 中创建线程安全的单例?

    这个 Java 问题也常被问: 什么是线程安全的单例,你怎么创建它.好吧,在Java 5之前的版本, 使用双重检查锁定创建单例 Singleton 时,如果多个线程试图同时创建 Singleton 实 ...

  5. 设计模式之创建类模式大PK

                                        创建类模式大PK 创建类模式包括工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,他们能够提供对象的创建和管理职责.其 ...

  6. 设计模式之创建类模式PK

    创建类模式包括: 工厂方法模式 建造者模式 抽象工厂模式 单例模式 原型模式 创建类模式能够提供对象的创建和管理职责. 其中单例模式和原型模式非常容易理解, 单例模式是要保持在内存中只有一个对象,原型 ...

  7. ZT 创建类模式总结篇

    创建类模式总结篇 分类: 设计模式 2012-03-26 09:03 7320人阅读 评论(11) 收藏 举报 编程优化设计模式任务 创建类模式主要关注对象的创建过程,将对象的创建过程进行封装,使客户 ...

  8. 创建类模式大PK(总结)

    创建类模式包含工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,它们都可以提供对象的创建和管理职责.当中的单例模式和原型模式很easy理解,单例模式是要保持在内存中仅仅有一个对象,原型模式是 ...

  9. [Android面试题-7] 写出一个Java的Singleton类(即单例类)

    1.首先明确单例的概念和特点: a>单例类只能有一个实例 b>单例类必须自己创建一个自己的唯一实例 c>单例类必须为其他所有对象提供这个实例 2.单例具有几种模式,最简单的两种分别是 ...

随机推荐

  1. toB的产品经理和toc产品经理区别

    腾讯产品经理现身说法 曾经在UC做过2年to c的app,现在在腾讯做to b的产品. 做to c产品的时候,我很瞧不起做to b产品的同学,认为他们不过是做支撑的. 后来,我参与了一个to b平台级 ...

  2. Mac 上安装MySQL

    http://blog.neten.de/posts/2014/01/27/install-mysql-using-homebrew/ http://www.wkii.org/mac-os-x-ins ...

  3. Spark快速入门(1)

    1 安装Spark 首先,到 https://spark.apache.org/downloads.html 选择最新的 Spark 版本和 Hadoop 版本(实际上我们暂时用不上 Hadoop,所 ...

  4. 【转】Android端与Android端利用WIFI进行FTP通信

    原文网址:http://www.cnblogs.com/zhangkai5157/p/4303188.html 一.客户端通信工具类: import java.io.File; import java ...

  5. Word2003使用VBA教程

    [正文] 注:本文中所有vba代码都是储存在doc中,而非normal.dot 1.打开一个.doc文档 2.按ALT+F11 3.左侧 Project-插入-模块 4.输入自己的代码,一定要是函数的 ...

  6. SQL Server数据类型与SDE库表sde_type对照表

    SDE_column_registry 表管理所有注册列. 警告:如果使用 SQL 界面更改列定义,SDE_column_registry 表中的记录将不会更新.这可能导致之后的任何数据导出失败. S ...

  7. php 采用fpdf乱码问题

    步骤1.首先下载fpdf http://www.fpdf.org/en/download.php(本人用的是1.7版本) 步骤2.下载中文包 http://www.fpdf.org/download/ ...

  8. hdu 1565(状态压缩基础题)

    题意:容易理解. 分析:这是我做的状态压缩第二题,一开始超内存了,因为数组开大了,后来超时了,因为能够成立的状态就那么多,所以你应该先把它抽出来!!总的来说还是比较简单的!! 代码实现: #inclu ...

  9. Yii 显示错误信息(Fatal Error,Warning)在页面上

    Yii由于设计上对于一些php奇怪问题的顾虑,并没有像cake,kohana一样把php错误信息打印在页面上. 遇到错误时,只是显示白页,这让没有经验的programmer会一头雾水. 实际上通常vh ...

  10. 怎样学法学?——民法学泰斗王利明教授的演讲 z

    今晚我讲“怎样学习法律”,但不是讲一般的学习法学的方法,而是主要从法学.法律的特征讲起.(因为)我们学习任何东西,都首先要搞清楚我们的学习对象有什么特征.性质. 我们要了解法律.法学本身的性质,要了解 ...