分享
 
 
 

大卫的Design Patterns学习笔记05:Singleton

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一、概述

在很多情况下,我们的系统只允许某个类有一个或指定个数的实例,如一般的应用系统往往有且仅有一个log文件操作类实例,或者,整个系统仅有一个等待事务队列等(注意:Singleton不是用来解决整个应用程序仅有一个实例这样的问题的),在这些情况下可以考虑使用Singleton模式。

Singleton(单件)模式用于保证一个类仅有一个实例,并提供一个访问该实例的全局访问点。(GoF: Ensure a class only has one instance, and provide a global point of access to it.)

二、结构

Singleton典型的结构如下图所示:

图1:Singleton模式的类图示意

三、应用

Singleton模式从概念上讲应该是所有模式中最简单的,它的目的很简单:限制我们创建实例的个数,Only one is permited。不过,该模式也可以扩展到允许指定数量个实例的情况。

一眼看去,Singleton似乎有些像全局对象。但是实际上,并不能用全局对象代替Singleton模式,这是因为:其一,有些编程语言例如Java、C#等,根本就不支持全局变量。其二,全局对象的方法并不能阻止人们将一个类实例化多次:除了类的全局实例外,开发人员仍然可以通过类的构造函数创建类的多个局部实例。而Singleton模式则通过从根本上控制类的创建,将“保证只有一个实例”这个任务交给了类本身,开发人员不可能再有其它途径得到类的多个实例。这一点是全局对象方法与Singleton模式的根本区别。

四、优缺点

Singleton模式是作为"全局变量"的替代品出现的,所以它具有全局变量的特点:全局可见;它也具有全局变量不具备的性质:同类型的对象实例只可能有一个。对于全局变量“贯穿应用程序的整个生命期”的特性,对于Singleton,视具体的实现方法,并不一定成立。

五、举例

实现该模式最简便的方法就是:采用static变量,并将类的构造函数、拷贝构造函数、赋值函数声明为private或者protected,同时提供public的实例创建/访问函数。

示例代码如下:

#include <assert.h>

#include <assert.h>

#include <iostream>

using namespace std;

class Singleton

{

public:

static Singleton* Instance() {

if (pInstance == NULL)

pInstance = new Singleton();

return pInstance;

}

protected:

Singleton() {}

private:

static Singleton* pInstance;

Singleton(const Singleton&) ;

Singleton& operator=(const Singleton&) ;

};

Singleton* Singleton::pInstance = NULL;

int main()

{

//Singleton instance; // Error

//Singleton* pInstance = new Singleton; // Error

//Singleton instance = Singleton::Instance(); // Error

Singleton* pInstance1 = Singleton::Instance(); // Good

Singleton* pInstance2 = Singleton::Instance();

cout.setf(ios::hex, ios::basefield);

cout << "pInstance1 = " << pInstance1 << endl;

cout << "pInstance2 = " << pInstance2 << endl;

cout.setf(ios::dec, ios::basefield);

assert(pInstance1 == pInstance2);

return 0;

}

从结果我们可以我们两次获取到的是同一实例。上述实现方式只是众多实现方式中的一种,要将其改为多个实例的情况也十分简单。

但是,上述Singleton实现在多线程环境下不能正常工作,假定第一个线程完成:

if (pInstance == NULL)

检测后,CPU将控制权交给第二个(甚至是第三个、第四个...)线程,则最终我们可能获得n个而不是1个实例。以下实现方式同样无法保证线程安全:

static Singleton* Instance() {

static Singleton _instance;

return &_instance;

}

下面的代码模拟了该问题(由于涉及多线程问题,为了简化问题,以下代码用Java编写):

class Singleton {

private static Singleton singleton = null;

protected Singleton() {

try {

Thread.currentThread().sleep(50);

} catch(InterruptedException ex) {

}

}

public static Singleton getInstance() {

if(singleton == null) {

singleton = new Singleton();

}

return singleton;

}

}

public class SingletonTest {

private static Singleton singleton = null;

public static void main(String[] args) throws InterruptedException {

// Both threads call Singleton.getInstance().

Thread threadOne = new Thread(new SingletonTestRunnable()),

threadTwo = new Thread(new SingletonTestRunnable());

threadOne.start();

threadTwo.start();

threadOne.join();

threadTwo.join();

}

private static class SingletonTestRunnable implements Runnable {

public void run() {

// Get a reference to the singleton.

Singleton s = Singleton.getInstance();

// Protect singleton member variable from multithreaded access.

synchronized(SingletonTest.class) {

if(singleton == null) // If local reference is null set it to the singleton

singleton = s;

}

if (s == singleton)

System.out.println("Yes.");

else

System.out.println("No.");

}

}

}

要保证结果的正确性,必须对获取Singleton实例的方法进行同步控制,为此,有人提出了double-checked locking的解决方案,相应的getInstance实现如下所示:

public static Singleton getInstance() {

if(singleton == null) {

synchronized(Singleton.class) {

if(singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

但据说,由于Java中,在变量singleton完成初始化前,即

singleton = new Singleton();

执行完毕前,sigleton的值可能是任意的,因此,若此时某个线程进入第一个if判断,则可能判断结果为false,从而返回一个无效的引用。因此,在Java中,唯一安全的方法是synchronized整个getInstance。

六、扩展

当系统中存在多个不同类型的Singleton时,可以考虑将Abstract Factory与Singleton模式结合,实现SingletonFactory,以下是一个SingletonFactory的例子:

import java.util.HashMap;

public class SingletonFactory {

public static SingletonFactory singletonFactory = new SingletonFactory();

private static HashMap map = new HashMap();

protected SingletonFactory() {

// Exists only to defeat instantiation.

}

public static synchronized Singleton getInstance(String classname) {

if (classname == null) throw new IllegalArgumentException("Illegal classname");

Singleton singleton = (Singleton)map.get(classname);

if(singleton != null) {

return singleton;

}

if (classname.equals("SingeltonSubclass1"))

singleton = new SingletonSubclass1(); // SingletonSubclass1 derives from Singleton class or implements Singleton interface

else if(classname.equals("SingeltonSubclass2"))

singleton = new SingletonSubclass2(); // SingletonSubclass2 derives from Singleton or implements Singleton interface

map.put(classname, singleton);

return singleton;

}

}

最后,需要注意的是,在C++中,单件对象的释放是个比较麻烦的事情,在上面的第一个示例中,由于没有释放单件对象,实际上是存在内存泄漏的,在释放单件对象的问题上,我们有以下几种选择:

1、在程序退出时由上层应用负责delete,比如在MFC程序中,在ExitInstance中逐一检查SingletonFactory的静态map对象所保存的Singleton;

2、在Singleton内部采用静态auto_ptr来封装Singleton对象,按此方法修改后的示例1如下所示:

#include <assert.h>

#include <iostream>

#include <memory>

using namespace std;

class Singleton

{

public:

static Singleton* Instance() {

if (instance.get() == NULL)

{

instance.reset(new Singleton());

}

return instance.get();

}

protected:

Singleton() {}

private:

static auto_ptr<Singleton> instance;

Singleton(const Singleton&) ;

Singleton& operator=(const Singleton&) ;

};

auto_ptr<Singleton> Singleton::instance = auto_ptr<Singleton>(NULL);

int main()

{

Singleton* pInstance1 = Singleton::Instance(); // Good

Singleton* pInstance2 = Singleton::Instance();

cout.setf(ios::hex, ios::basefield);

cout << "pInstance1 = " << pInstance1 << endl;

cout << "pInstance2 = " << pInstance2 << endl;

cout.setf(ios::dec, ios::basefield);

assert(pInstance1 == pInstance2);

return 0;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有