您的位置:首页 > 编程语言 > C语言/C++

设计模式解析和实现(C++, java)之十-singleton模式

2010-10-18 10:27 831 查看
 单例模式可以保证一个类有且只有一个实例,并提供一个访问它的全局访问点.在程序设计中,有很多情况需要确保一个类只能有一个实例.从这句话可以
看出,Singleton模式的核心:如何控制用户使用new对一个类的实例构造器的任意调用。如何绕过常规的构造器,提供一种机制来保证一个类只有一个
实例?这应该是类设计者的责任,而不是使用者的责任。

 一、单例模式意图

  保证一个类有且只有一个实例,并提供一个访问它的全局访问点。

 二、单例模式UML图

图1: Singleton 模式结构



实现 Singleton 模式的办法通常有三种:

  1.用静态方法实现 Singleton;

  2.以静态变量为标志实现 Singleton;

  3.用注册器机制来创建 Singleton。

三 实现Java Singleton的方案

Java Singleton是指在特定系统范围内只能实例化一次的Java类,如何理解特定系统范围?按照需求和环境定义系统范围,关注在特定系统范围内单一实例的需求:

系统范围和环境定义
方案编号
框架容器内 A
单一JVM中、单一类加载器加载类 B、C、D
单一JVM、不同类加载器加载相同类
系统跨多个JVM
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实现方式,可以尽量减少实例的数量。

当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();


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: