k_eckelhttp://www.mscenter.edu.cn/blog/k_eckel & http://k-eckel.cnblogs.com

前几天因为某些事情,和在ATC工作的一位朋友S聊了些技术方面的问题。S在学校的时候就是很,那种传说中的N人,S Review我之前写的Visual CMCS的一些代码,就问到其中一个类为什么要使用Singleton模式?代码是几个月前写的,写的时候正是自己对设计模式有一些学习积累和思考的时候,因此里面用到了不少的设计模式。当时简单的考虑就是因为那是一个工厂类,我在程序生命周期内仅需要一个该工厂类的对象就可以了,因此很直观地使用了Singleton模式。当然我当时的回答就是因为仅需要提供一个对象就可以,S马上就问那为什么不用一个全局变量来实现?我知道,在纯面向对象支持的编程语言(例如C#/Java)中,这个问题可以很简单地理解为没有全局变量这样一个概念支持,但是对于向C++等这种支持面向过程的编程语言中这个回答就不是很合适了(至少像在找借口)。

       实际上这个问题的答案并不重要(虽然是这个文档的主题),更加重要的是我们在作设计的时候实际上要好好综合考虑,为什么要使用这种设计模式,也就是要防止滥用设计模式的情况Singleton模式可能可以说是最简单的设计模式了,其应用的场景和示例实在是没有太多可以再重复,但是以下的两个问题还是需要我们进一步的审视:

1)  Singleton模式 VS 全局变量。很多情况下,我们使用Singleton模式达到的效果和

全局变量达到的效果类似。但是,全局变量不能防止实例化多个对象GoF在《设计模式》中给出了Singleton模式的意图“保证一个类仅有一个对象,并提供一个访问它的全局访问点”,因此全局变量可以达到后面半句的效果,但是却不能保证仅有一个对象被实例化。另外,使用全局变量将使得对象在无论是否用到都要被创建,而Singleton模式则没有这个瑕疵。

2)  Singleton的子类化问题。一般来说Singleton的子类并不是Singleton,因此在保证

Singleton的正确子类化,在实现上要注意以下几点(C++实现):

     父类Singleton的构造函数为Protected,目的是为了要让Singleton子类访问,而

不让Client程序访问(防止被其他方式实例化类);Singleton子类构造函数声明为private或者protected,并且将父类Singleton声明为子类Singleton的友元,目的是在父类Singleton中可以实例化子类Singleton,而Client程序不可访问(防止被其他方式实例化类)。

     我们必须改写父类Singleton中的Instance方法(获得唯一实例方法)。因为Instance

static的成眼函数,不能以多态的方式实现之。因此我们必须在父类Singleton中就是提供真正实例化Singleton子类的信息。我们可以通过到某一个专门的地方获取Singleton子类的信息,例如提供一个获取函数,在Instance实例化Singleton子类之前获得这个信息,再根据这个信息去实例化具体的Singleton子类。我这里提供的示例程序中是,提供一个全局的GetSingletionType(),返回应该实例化的Singleton具体子类。具体的实现则是是通过随机数来确定的方式,详细请参看代码。以下就将整个代码给出:

//Singleton.h

 

#ifndef _SINGLETON_H_

#define _SINGLETON_H_

 

#include <iostream>

using namespace std;

 

class Singleton

{

public:

       static Singleton* Instance();

 

       virtual void PrintInfo();

 

protected:

       Singleton();

 

private:

       static Singleton* _instance;

 

};

 

class SingletonDeriveA:public Singleton

{

private:

       friend class Singleton;

       SingletonDeriveA();

public:

       virtual ~SingletonDeriveA();

 

       void PrintInfo();

      

};

 

class SingletonDeriveB:public Singleton

{

private:

       friend class Singleton;

       SingletonDeriveB();

 

public:

       virtual ~SingletonDeriveB();

 

       void PrintInfo();

      

};

 

char* GetSingletionType();

 

#endif //~_SINGLETON_H_

//Singleton.cpp

 

#include "Singleton.h"

 

#include <ctime>  //for time

 

#include <iostream>

using namespace std;

 

Singleton* Singleton::_instance = 0;

 

Singleton::Singleton()

{

       cout<<"Singleton...."<<endl;

}

 

Singleton* Singleton::Instance()

{

       const char* type = GetSingletionType();

 

       if (_instance == 0)

       {

              if (strcmp(type,"SingletonDeriveA") == 0)

              {

                     _instance = new SingletonDeriveA();

              }

              else if (strcmp(type,"SingletonDeriveB") == 0)

              {

                     _instance = new SingletonDeriveB();

              }

              else

              {

                     _instance = new Singleton();

              }

       }

 

       return _instance;

}

 

void Singleton::PrintInfo()

{

       cout<<"Singleton type:Singleton"<<endl;

}

 

SingletonDeriveA::SingletonDeriveA()

{

       cout<<"SingletonDeriveA...."<<endl;

}

 

SingletonDeriveA::~SingletonDeriveA()

{

 

}

 

void SingletonDeriveA::PrintInfo()

{

       cout<<"Singleton type:SingletonDeriveA"<<endl;

}

 

SingletonDeriveB::SingletonDeriveB()

{

       cout<<"SingletonDeriveB...."<<endl;

}

 

SingletonDeriveB::~SingletonDeriveB()

{

 

}

 

void SingletonDeriveB::PrintInfo()

{

       cout<<"Singleton type:SingletonDeriveB"<<endl;

}

 

 

char* GetSingletionType()

{

       //随机返回一个Singleton子类

       srand((unsigned int)time(NULL));

       int SINGLETON_TYPE = rand() % 3;

 

       switch (SINGLETON_TYPE)

       {

       case 0:

              return "SingletonDeriveA";

              break;

       case 1:

              return "SingletonDeriveB";

              break;

       case 2:

              return "Singleton";

              break;

       default :

              return "Singleton";

              break;

       }

}

//main.cpp

 

#include "Singleton.h"

 

#include <iostream>

using namespace std;

 

 

int main(int argc,char* argv[])

{

       for (int i = 0; i < 10; ++i)

       {

              Singleton* sgn = Singleton::Instance();

 

              sgn->PrintInfo();

       }

 

       //Singleton* sgn1 = new SingletonDeriveA(); //compile error,保证不能通过其他方式实例化类

 

       return 0;

}

测试程序运行的结果是,虽然我们请求了10Singleton对象,但是只实例化了一次(从调用构造函数的次数就可以知道)。比较遗憾也是要说明的是:由于GetSingletionType()实现策略不是很好,运行后10次返回的随机数取模后的结果是一样的,因此获得Singleton对象是一样的,但是不同时刻运行的结果可以不一样。调试发现,rand()返回结果不同,但是赋值后就变得一样了,由于这不是重点,故放弃,哪位有好的建议,请不吝赐教。

Posted on 2005-08-06 16:00  k_eckel's mindview  阅读(1837)  评论(0)    收藏  举报