设计模式并不只是一种方法和技术,更是一种思想、一个方法论。设计模式与具体编程语言无关,其主要目的是使人们可以更加简单方便地复用成功的设计和体系结构、建立面向对象的设计思想、面向接口编程、编写的程序高内聚、低耦合。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
单例模式是创建型模式之一,也是设计模式中最简单的形式之一。有懒汉式、饿汉式、注册式这三种形式。我将先用懒汉式举例。
目的:在系统中,使该类对象的实例只有一个。
动机:从业务角度来分析,在某些情况下,设计者或用户会要求核心对象在系统中只有该类的一个实例。例如多个任务需要调用打印机类的打印方法,而实际情况是计算机连接的打印机只有一个,此时必须限制打印机类的实例个数。
再例如从技术角度来分析,某个类被多个用户共用,但该类包含的数据量非常庞大,相应的方法实现起来也十分复杂。如果在系统中创建该类的多个实例,不仅会严重消耗系统的资源,还有可能使用户接受的数据不一致。此时只需创建该类的一个实例就可以满足用户需求。
实现原理:为了使得系统中只有该类的一个实例,我们不能让客户端可以随意创建该类的对象。那么我就将该类的构造方法私有化(private),使得客户端(即类外环境)无法直接创建类的实例。例如下列语句是错误的
//client
int main(void)
{
Singleton s=new Singleton();//错误,构造方法为私有方法,无法在客户端调用
return 0;
}
Singleton* Singleton::GetInstance(){
if(singleton==NULL) //如果未创建过类实例,
singleton=new Singleton();
return singleton;
}
必要条件:
1.将该类的构造方法私有化(private),使得客户端(即类外环境)无法直接创建类的实例。
2.设置一个公有的静态方法,在静态方法中调用构造方法。
3.类中建立一个私有的静态指针,用来存放创建的唯一实例。
懒汉式代码:在客户端需要使用的时候才创建对象。
//Singleton.h
#ifndef _Singleton_h
#define _Singleton_h
#include
using namespace std;
class Singleton{
private:
static Singleton* singleton; //静态实例指针
Singleton(); //私有构造函数
public:
static Singleton* GetInstance();//静态公有获得实例函数
};
#endif
//Singleton.cpp
#include "Singleton.h"
Singleton* Singleton::singleton=NULL; //用户在创建对象前,值为null
Singleton::Singleton(){
cout<<"Create a singleton instance!"<
运行结果:
饿汉式代码:在类初始化时就已经创建了对象。
//Singleton.h
#ifndef _Singleton_h
#define _Singleton_h
#include
using namespace std;
class Singleton{
private:
static Singleton* singleton; //静态实例指针
Singleton(); //私有构造函数
public:
static Singleton* GetInstance();//静态公有获得实例函数
};
#endif
//Singleton.cpp
#include "Singleton.h"
Singleton* Singleton::singleton=new Singleton(); //创建对象
Singleton::Singleton(){
cout<<"Create a singleton instance!"<
运行结果:
注册式代码:父类实例指针只有一个实例,可以是父类,也可以是子类。父类、子类被调用时创建实例。
#ifndef _Singleton_h
#define _Singleton_h
#include
using namespace std;
/*** 父类Singleton ***/
class Singleton{
private:
static Singleton* singleton; //静态实例指针,子类无法访问
protected:
Singleton(); //此时为保护构造函数,否则创建子类时子类构造无法访问父类构造
public:
static Singleton* GetInstance();//静态公有获得实例函数,只返回Singleton类
static Singleton* GetInstance(const char);//静态公有获得实例函数,可返回Singleton类、A类、B类
};
/*******************************/
/*** 子类A ***/
class A:public Singleton{
friend class Singleton; //将父类设为友元类,让父类可以直接访问子类构造
private:
A();
public:
static Singleton* GetInstance();
};
/*******************************/
/*** 子类B ***/
class B:public Singleton{
friend class Singleton; //将父类设为友元类,让父类可以直接访问子类构造
private:
B();
public:
static Singleton* GetInstance();
};
/*******************************/
#endif
#include "Singleton.h"
/*** Singelton父类 ***/
Singleton* Singleton::singleton=NULL; //实例指针初始为空
Singleton::Singleton(){
cout<<"Create one Singleton"; //构造父类时自动显示
}
Singleton* Singleton::GetInstance(){ //Singleton父类无参静态函数,用于直接获取Singleton实例
if(singleton==NULL)
singleton=new Singleton();
return singleton;
}
Singleton* Singleton::GetInstance(const char choice){ //Singleton父类有参静态函数,用于获取子类实例
if(singleton==NULL){
if(choice=='A') //判断输入的参数来决定创建哪个子类实例
singleton=new A();
if(choice=='B')
singleton=new B();
}
return singleton;
}
/**************************/
/*** A子类 ***/
A::A(){ //创建子类A时自动显示
cout<<"->A"<
B"<
运行结果:
如果将main函数中第一行注释掉,运行结果:
在我看一些设计模式的书时,有些书并没有讲到注册式代码。我觉得原因有二:
1.使用注册式代码时,父类构造函数不能再为private而是protected,因为子类在构造时会访问父类构造,如果父类构造为private,子类是无法访问父类构造的,链接时会出错。
2.在子类中需要将父类添加为友元类,否则父类在构造子类实例时无法访问子类构造。
这两点原因都在破坏类的封装性,而标准的单例模式对类的封装性要求极高。而且在描写注册式代码时,我发现我渐渐将构思的重点转向了如何更方便的构造子类,这是工厂模式要考虑的事情。所以这么做这并不是纯正的单例模式思想。而且由于C++中友元类不可以被继承,假如子类还要向下泛化子类,那么这个新的子类需要手动添加代码
friend class Singleton,父类也要添加创建子类的判断,类间耦合度太高,这是很麻烦的事情。也许我们会在需要单例模式和工厂模式混合使用的情况下,才探讨这个方法吧。
我也看到很多单例模式中涉及到线程间互斥的问题,这个问题我会继续研究,在我觉得我的想法足够切实可靠的时候,我会回来修改这篇文章。