设计模式解析和实现(C++, java)之十-singleton模式
2010-10-18 10:27
831 查看
单例模式可以保证一个类有且只有一个实例,并提供一个访问它的全局访问点.在程序设计中,有很多情况需要确保一个类只能有一个实例.从这句话可以
看出,Singleton模式的核心:如何控制用户使用new对一个类的实例构造器的任意调用。如何绕过常规的构造器,提供一种机制来保证一个类只有一个
实例?这应该是类设计者的责任,而不是使用者的责任。
一、单例模式意图
保证一个类有且只有一个实例,并提供一个访问它的全局访问点。
二、单例模式UML图
图1: Singleton 模式结构
实现 Singleton 模式的办法通常有三种:
1.用静态方法实现 Singleton;
2.以静态变量为标志实现 Singleton;
3.用注册器机制来创建 Singleton。
A
提供实例管理容器的第三方框架,例如Spring
IOC容器,可以通过配置保证实例在容器内的唯一性。这些被外部容器管理的类,能在某个容器范围内达到Singleton的效果,不一定禁止自身在容器外
部范围生成多个实例。 这种方案可以看作是一种局部的单例解决方案。
B
代码示范如下
C
D
有时候类的实例化开销较为昂贵,有时候类的实例化要用到系统运行时的动态数据做参数等等,在上述情形,静态成员变量需要延迟初始化。程序员中间流传较广的一种形式是:
这种写法最大的问题是getInstance()方法被多个线程竞争使用的时候,可能会产生多个实例,违反了单例设计的初衷。如果多个实例的风险(Singleton失败)不会引起您的系统异常,比如实例存放的是无状态的数据、实例是轻量级的,您可以坚持这种写法。
当然还有改进的方法,一般是在该方法前加上“synchronized”关键字:
这种作法的副作用就是影响性能。
双检查锁(Double-Checked Locking)是在多线程环境下实现延迟初始化的有效方式(如C++),不幸的是,对大多数JVM是无效的。有一篇文章解释的很深入:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
其他方案
单一JVM、不同类加载器加载相同类或跨多个JVM的情形,要保证Java Singleton不失败,我还没找到恰当的方法。
有些人认为单例的需求不仅仅是来自某些组件自身的唯一特性,还来自对创建并维护多个对象产生消耗的无法忍受、来自对性能的追求。如果出于降低消耗和
提高性能的目的,很多无状态的类(类的所有实例天然是始终相同的),即便面临单一JVM、不同类加载器加载相同类或跨多个JVM的情形,也可以采用上述的
Singleton实现方式,可以尽量减少实例的数量。
我们看看一个例子:
进行序列化测试
public static void main(String[] args) throws Exception{
//序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E://kingBedroom.obj"));
King king_1 = King.INSTANCE;
objectOutputStream.writeObject(king_1);
objectOutputStream.close();
//反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E://kingBedroom.obj"));
King king_2 = (King)objectInputStream.readObject();
objectInputStream.close();
//比较是否原来的实例
System.out.println(king_1==king_2);
}
结果是false
解决方法是为King类增加readResolve()方法:
private Object readResolve(){
return INSTANCE;
}
反序列化之后新创建的对象会先调用此方法,该方法返回的对象引用被返回,取代了新创建的对象。本质上,该方法忽略了新建对象,仍然返回类初始化时创建的那个实例。
五:C++的设计与实现:
C++完美实现Singleton模式
Singleton
模式是常用的设计模式之一,但是要实现一个真正实用的设计模式却也不是件容易的事情。
1.
标准的实现
class
Singleton
{
public:
static
Singleton *
Instance()
{
if(
0
==
_instance)
{
_instance =
new
Singleton;
}
return
_instance;
}
protected:
Singleton(void)
{
}
virtual
~
Singleton(void)
{
}
static
Singleton* _instance;
};
这是教科书上使用的方法。看起来没有什么问题,其实包含很多的问题。下面我们一个一个的解决。
2.
自动垃圾回收
上面的程序必须记住在程序结束的时候,释放内存。为了让它自动的释放内存,我们引入
auto_ptr
改变它。
#include
<memory>
#include
<iostream>
using
namespace
std;
class
Singleton
{
public:
static
Singleton *
Instance()
{
if(
0
==
_instance.
get())
{
_instance.
reset(
new
Singleton);
}
return
_instance.
get();
}
protected:
Singleton(void)
{
cout <<
"Create Singleton"
<<
endl;
}
virtual
~
Singleton(void)
{
cout <<
"Destroy Singleton"
<<
endl;
}
friend
class
auto_ptr<
Singleton>;
static
auto_ptr<
Singleton>
_instance;
};
//Singleton.cpp
auto_ptr<
Singleton>
Singleton::
_instance;
3.
增加模板
在我的一个工程中,有多个的
Singleton
类,对
Singleton
类,我都要实现上面这一切,这让我觉得烦死了。于是我想到了模板来完成这些重复的工作。
现在我们要添加本文中最吸引人单件实现:
/********************************************************************
(c) 2003-2005 C2217 Studio
Purpose:
Implement singleton pattern
History:
*********************************************************************/
#pragma
once
#include
<memory>
using
namespace
std;
using
namespace
C2217::
Win32;
namespace
C2217
{
namespace
Pattern
{
template
<class
T>
class
Singleton
{
public:
static
inline
T*
instance();
private:
Singleton(void){}
~
Singleton(void){}
Singleton(const
Singleton&){}
Singleton &
operator=
(const
Singleton &){}
static
auto_ptr<
T>
_instance;
};
template
<class
T>
auto_ptr<
T>
Singleton<
T>::
_instance;
template
<class
T>
inline
T*
Singleton<
T>::
instance()
{
if(
0
==
_instance.
get())
{
_instance.
reset (
new
T);
}
return
_instance.
get();
}
//Class that will implement the singleton mode,
//must use the macro in it's delare file
#define
DECLARE_SINGLETON_CLASS(
type )
/
friend
class
auto_ptr<
type >;
/
friend
class
Singleton<
type >;
}
}
4.
线程安全
上面的程序可以适应单线程的程序。但是如果把它用到多线程的程序就会发生问题。主要的问题在于同时执行
_instance.
reset (
new
T);
就会同时产生两个新的对象,然后马上释放一个,这跟
Singleton
模式的本意不符。所以,你需要更加安全的版本:
/********************************************************************
(c) 2003-2005 C2217 Studio
Purpose:
Implement singleton pattern
History:
*********************************************************************/
#pragma
once
#include
<memory>
using
namespace
std;
#include
"Interlocked.h"
using
namespace
C2217::
Win32;
namespace
C2217
{
namespace
Pattern
{
template
<class
T>
class
Singleton
{
public:
static
inline
T*
instance();
private:
Singleton(void){}
~
Singleton(void){}
Singleton(const
Singleton&){}
Singleton &
operator=
(const
Singleton &){}
static
auto_ptr<
T>
_instance;
static
CResGuard _rs;
};
template
<class
T>
auto_ptr<
T>
Singleton<
T>::
_instance;
template
<class
T>
CResGuard Singleton<
T>::
_rs;
template
<class
T>
inline
T*
Singleton<
T>::
instance()
{
if(
0
==
_instance.
get()
)
{
CResGuard::
CGuard gd(
_rs);
if(
0
==
_instance.
get())
{
_instance.
reset (
new
T);
}
}
return
_instance.
get();
}
//Class that will implement the singleton mode,
//must use the macro in it's delare file
#define
DECLARE_SINGLETON_CLASS(
type )
/
friend
class
auto_ptr<
type >;
/
friend
class
Singleton<
type >;
}
}
CresGuard
类主要的功能是线程访问同步
,
代码如下:
/******************************************************************************
Module:
Interlocked.h
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/
#pragma
once
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
class
CResGuard {
public:
CResGuard()
{
m_lGrdCnt =
0
;
InitializeCriticalSection(&
m_cs);
}
~
CResGuard()
{
DeleteCriticalSection(&
m_cs);
}
// IsGuarded is used for debugging
BOOL IsGuarded()
const
{
return(
m_lGrdCnt >
0
);
}
public:
class
CGuard {
public:
CGuard(
CResGuard&
rg)
:
m_rg(
rg)
{
m_rg.
Guard();
};
~
CGuard()
{
m_rg.
Unguard();
}
private:
CResGuard&
m_rg;
};
private:
void
Guard()
{
EnterCriticalSection(&
m_cs);
m_lGrdCnt++;
}
void
Unguard()
{
m_lGrdCnt--;
LeaveCriticalSection(&
m_cs);
}
// Guard/Unguard can only be accessed by the nested CGuard class.
friend
class
CResGuard::
CGuard;
private:
CRITICAL_SECTION m_cs;
long
m_lGrdCnt;
// # of EnterCriticalSection calls
};
///////////////////////////////////////////////////////////////////////////////
5.
实用方法
比如你有一个需要实现单件模式的类,就应该这样实现:
#pragma
once
#include
"singleton.h"
using
namespace
C2217::
Pattern;
class
ServiceManger
{
public:
void
Run()
{
}
private:
ServiceManger(void)
{
}
virtual
~
ServiceManger(void)
{
}
DECLARE_SINGLETON_CLASS(
ServiceManger);
};
typedef
Singleton<
ServiceManger>
SSManger;
在使用的时候很简单,跟一般的
Singleton
实现的方法没有什么不同。
int
_tmain(int
argc,
_TCHAR*
argv[])
{
SSManger::
instance()->
Run();
}
看出,Singleton模式的核心:如何控制用户使用new对一个类的实例构造器的任意调用。如何绕过常规的构造器,提供一种机制来保证一个类只有一个
实例?这应该是类设计者的责任,而不是使用者的责任。
一、单例模式意图
保证一个类有且只有一个实例,并提供一个访问它的全局访问点。
二、单例模式UML图
图1: Singleton 模式结构
实现 Singleton 模式的办法通常有三种:
1.用静态方法实现 Singleton;
2.以静态变量为标志实现 Singleton;
3.用注册器机制来创建 Singleton。
三 实现Java Singleton的方案
Java Singleton是指在特定系统范围内只能实例化一次的Java类,如何理解特定系统范围?按照需求和环境定义系统范围,关注在特定系统范围内单一实例的需求:系统范围和环境定义 | 方案编号 |
框架容器内 | A |
单一JVM中、单一类加载器加载类 | B、C、D |
单一JVM、不同类加载器加载相同类 | |
系统跨多个JVM |
提供实例管理容器的第三方框架,例如Spring
IOC容器,可以通过配置保证实例在容器内的唯一性。这些被外部容器管理的类,能在某个容器范围内达到Singleton的效果,不一定禁止自身在容器外
部范围生成多个实例。 这种方案可以看作是一种局部的单例解决方案。
B
代码示范如下
C
D
有时候类的实例化开销较为昂贵,有时候类的实例化要用到系统运行时的动态数据做参数等等,在上述情形,静态成员变量需要延迟初始化。程序员中间流传较广的一种形式是:
这种写法最大的问题是getInstance()方法被多个线程竞争使用的时候,可能会产生多个实例,违反了单例设计的初衷。如果多个实例的风险(Singleton失败)不会引起您的系统异常,比如实例存放的是无状态的数据、实例是轻量级的,您可以坚持这种写法。
当然还有改进的方法,一般是在该方法前加上“synchronized”关键字:
这种作法的副作用就是影响性能。
双检查锁(Double-Checked Locking)是在多线程环境下实现延迟初始化的有效方式(如C++),不幸的是,对大多数JVM是无效的。有一篇文章解释的很深入:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
其他方案
单一JVM、不同类加载器加载相同类或跨多个JVM的情形,要保证Java Singleton不失败,我还没找到恰当的方法。
有些人认为单例的需求不仅仅是来自某些组件自身的唯一特性,还来自对创建并维护多个对象产生消耗的无法忍受、来自对性能的追求。如果出于降低消耗和
提高性能的目的,很多无状态的类(类的所有实例天然是始终相同的),即便面临单一JVM、不同类加载器加载相同类或跨多个JVM的情形,也可以采用上述的
Singleton实现方式,可以尽量减少实例的数量。
当Java Singleton遇到反序列化
一个序列化的实例,每次反序列化的时候都会产生一个新的实例。Singleton也不例外。我们看看一个例子:
进行序列化测试
public static void main(String[] args) throws Exception{
//序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E://kingBedroom.obj"));
King king_1 = King.INSTANCE;
objectOutputStream.writeObject(king_1);
objectOutputStream.close();
//反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E://kingBedroom.obj"));
King king_2 = (King)objectInputStream.readObject();
objectInputStream.close();
//比较是否原来的实例
System.out.println(king_1==king_2);
}
结果是false
解决方法是为King类增加readResolve()方法:
private Object readResolve(){
return INSTANCE;
}
反序列化之后新创建的对象会先调用此方法,该方法返回的对象引用被返回,取代了新创建的对象。本质上,该方法忽略了新建对象,仍然返回类初始化时创建的那个实例。
五:C++的设计与实现:
C++完美实现Singleton模式
Singleton
模式是常用的设计模式之一,但是要实现一个真正实用的设计模式却也不是件容易的事情。
1.
标准的实现
class
Singleton
{
public:
static
Singleton *
Instance()
{
if(
0
==
_instance)
{
_instance =
new
Singleton;
}
return
_instance;
}
protected:
Singleton(void)
{
}
virtual
~
Singleton(void)
{
}
static
Singleton* _instance;
};
这是教科书上使用的方法。看起来没有什么问题,其实包含很多的问题。下面我们一个一个的解决。
2.
自动垃圾回收
上面的程序必须记住在程序结束的时候,释放内存。为了让它自动的释放内存,我们引入
auto_ptr
改变它。
#include
<memory>
#include
<iostream>
using
namespace
std;
class
Singleton
{
public:
static
Singleton *
Instance()
{
if(
0
==
_instance.
get())
{
_instance.
reset(
new
Singleton);
}
return
_instance.
get();
}
protected:
Singleton(void)
{
cout <<
"Create Singleton"
<<
endl;
}
virtual
~
Singleton(void)
{
cout <<
"Destroy Singleton"
<<
endl;
}
friend
class
auto_ptr<
Singleton>;
static
auto_ptr<
Singleton>
_instance;
};
//Singleton.cpp
auto_ptr<
Singleton>
Singleton::
_instance;
3.
增加模板
在我的一个工程中,有多个的
Singleton
类,对
Singleton
类,我都要实现上面这一切,这让我觉得烦死了。于是我想到了模板来完成这些重复的工作。
现在我们要添加本文中最吸引人单件实现:
/********************************************************************
(c) 2003-2005 C2217 Studio
Purpose:
Implement singleton pattern
History:
*********************************************************************/
#pragma
once
#include
<memory>
using
namespace
std;
using
namespace
C2217::
Win32;
namespace
C2217
{
namespace
Pattern
{
template
<class
T>
class
Singleton
{
public:
static
inline
T*
instance();
private:
Singleton(void){}
~
Singleton(void){}
Singleton(const
Singleton&){}
Singleton &
operator=
(const
Singleton &){}
static
auto_ptr<
T>
_instance;
};
template
<class
T>
auto_ptr<
T>
Singleton<
T>::
_instance;
template
<class
T>
inline
T*
Singleton<
T>::
instance()
{
if(
0
==
_instance.
get())
{
_instance.
reset (
new
T);
}
return
_instance.
get();
}
//Class that will implement the singleton mode,
//must use the macro in it's delare file
#define
DECLARE_SINGLETON_CLASS(
type )
/
friend
class
auto_ptr<
type >;
/
friend
class
Singleton<
type >;
}
}
4.
线程安全
上面的程序可以适应单线程的程序。但是如果把它用到多线程的程序就会发生问题。主要的问题在于同时执行
_instance.
reset (
new
T);
就会同时产生两个新的对象,然后马上释放一个,这跟
Singleton
模式的本意不符。所以,你需要更加安全的版本:
/********************************************************************
(c) 2003-2005 C2217 Studio
Purpose:
Implement singleton pattern
History:
*********************************************************************/
#pragma
once
#include
<memory>
using
namespace
std;
#include
"Interlocked.h"
using
namespace
C2217::
Win32;
namespace
C2217
{
namespace
Pattern
{
template
<class
T>
class
Singleton
{
public:
static
inline
T*
instance();
private:
Singleton(void){}
~
Singleton(void){}
Singleton(const
Singleton&){}
Singleton &
operator=
(const
Singleton &){}
static
auto_ptr<
T>
_instance;
static
CResGuard _rs;
};
template
<class
T>
auto_ptr<
T>
Singleton<
T>::
_instance;
template
<class
T>
CResGuard Singleton<
T>::
_rs;
template
<class
T>
inline
T*
Singleton<
T>::
instance()
{
if(
0
==
_instance.
get()
)
{
CResGuard::
CGuard gd(
_rs);
if(
0
==
_instance.
get())
{
_instance.
reset (
new
T);
}
}
return
_instance.
get();
}
//Class that will implement the singleton mode,
//must use the macro in it's delare file
#define
DECLARE_SINGLETON_CLASS(
type )
/
friend
class
auto_ptr<
type >;
/
friend
class
Singleton<
type >;
}
}
CresGuard
类主要的功能是线程访问同步
,
代码如下:
/******************************************************************************
Module:
Interlocked.h
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/
#pragma
once
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
class
CResGuard {
public:
CResGuard()
{
m_lGrdCnt =
0
;
InitializeCriticalSection(&
m_cs);
}
~
CResGuard()
{
DeleteCriticalSection(&
m_cs);
}
// IsGuarded is used for debugging
BOOL IsGuarded()
const
{
return(
m_lGrdCnt >
0
);
}
public:
class
CGuard {
public:
CGuard(
CResGuard&
rg)
:
m_rg(
rg)
{
m_rg.
Guard();
};
~
CGuard()
{
m_rg.
Unguard();
}
private:
CResGuard&
m_rg;
};
private:
void
Guard()
{
EnterCriticalSection(&
m_cs);
m_lGrdCnt++;
}
void
Unguard()
{
m_lGrdCnt--;
LeaveCriticalSection(&
m_cs);
}
// Guard/Unguard can only be accessed by the nested CGuard class.
friend
class
CResGuard::
CGuard;
private:
CRITICAL_SECTION m_cs;
long
m_lGrdCnt;
// # of EnterCriticalSection calls
};
///////////////////////////////////////////////////////////////////////////////
5.
实用方法
比如你有一个需要实现单件模式的类,就应该这样实现:
#pragma
once
#include
"singleton.h"
using
namespace
C2217::
Pattern;
class
ServiceManger
{
public:
void
Run()
{
}
private:
ServiceManger(void)
{
}
virtual
~
ServiceManger(void)
{
}
DECLARE_SINGLETON_CLASS(
ServiceManger);
};
typedef
Singleton<
ServiceManger>
SSManger;
在使用的时候很简单,跟一般的
Singleton
实现的方法没有什么不同。
int
_tmain(int
argc,
_TCHAR*
argv[])
{
SSManger::
instance()->
Run();
}
相关文章推荐
- 设计模式解析和实现(C++, java)之二十--状态(state)模式
- 设计模式的解析和实现(C++)之五-Singleton模式
- 设计模式的解析和实现之五-Singleton模式 单例模式
- 常见设计模式的解析和实现(C++)之五-Singleton模式
- 常见设计模式的解析和实现(C++)之五-Singleton模式
- 常见设计模式的解析和实现(C++)之五-Singleton模式
- 设计模式--工厂模式--Java实现
- JAVA单例设计模式简单实现(懒汉与恶汉),多例设计模式的简单实现
- Java设计模式_结构型_享元模式_实现单元的共享
- Java实现设计模式之策略模式
- Java设计模式菜鸟系列(十)模板方法模式建模与实现
- 设计模式:策略模式的实现 c++ 与 java
- 六种常用的设计模式java实现(三)责任链模式
- 程序基础设计模式的解析和实现(C++)之二十-Visitor模式
- 常见设计模式的解析和实现(C++)之十四-Command模式
- 设计模式-生产者与消费者Java实现
- 六种常用的设计模式java实现(五)代理模式
- 设计模式的解析和实现(C++)之三-Builder模式
- Java设计模式菜鸟系列(十四)代理模式建模与实现
- 设计模式--状态模式(分布式中间件熔断器Java实现)