设计模式(2)-单例模式(Singleton)

来源:岁月联盟 编辑:exp 时间:2012-07-10

(3) static与单例模式

由于static数据成员存储的位置是固定不变的,实质上,利用指针访问同一静态数据成员的不同实例,是访问同一内存地址。也就是说,访问同一静态数据成员的不同实例,实际上,访问的是同一实例(单例)。

main.cpp下述代码示例,s1、s2、s3,s4,s5是不同实例,分别new了不同的内存。但修改s1->test、s4->test,同时修改了s3,s4,s5三个实例中静态test值。实质上,s1->test、s2->test、s3->test、s4->test、s5->test是“同一”实例。

[html]
//Test2 
    qDebug()<<s1->test;//s1->test = 1 
    s2->test++; 
    qDebug()<<s1->test;//s1->test = 2 
    qDebug()<<s2->test;//s2->test = 2 
    Singleton *s4 = new Singleton; 
    qDebug()<<s4->test;//s4->test = 1 
    Singleton *s5 = new Singleton; 
    qDebug()<<s5->test;//s5->test = 1 
 
    s1->test++; 
    s4->test++; 
    qDebug()<<s1->test;//s1->test = 3 
    qDebug()<<s2->test;//s2->test = 3 
    qDebug()<<s3->test;//s3->test = 3 
    qDebug()<<s4->test;//s4->test = 3 
    qDebug()<<s5->test;//s5->test = 3 
//Test2
    qDebug()<<s1->test;//s1->test = 1
    s2->test++;
    qDebug()<<s1->test;//s1->test = 2
    qDebug()<<s2->test;//s2->test = 2
    Singleton *s4 = new Singleton;
    qDebug()<<s4->test;//s4->test = 1
    Singleton *s5 = new Singleton;
    qDebug()<<s5->test;//s5->test = 1

    s1->test++;
    s4->test++;
    qDebug()<<s1->test;//s1->test = 3
    qDebug()<<s2->test;//s2->test = 3
    qDebug()<<s3->test;//s3->test = 3
    qDebug()<<s4->test;//s4->test = 3
    qDebug()<<s5->test;//s5->test = 3
 

【UML图】

 /

图1 单例模式UML

1 Singleton应用了单例模式,定义了一个类型为Singleton的私有静态的_instance,一个类型为int的公有的_test,以及一个类型为QString的_name对象。

2 运用静态方法createInstance创建实例。

3 定义了两个公有方法setName()、getName()。

 

【示例代码】

singleton.h

[html] view plaincopyprint?#ifndef SINGLETON_H 
#define SINGLETON_H 
 
#include <QString> 
 
class Singleton 

public: 
    Singleton(); 
 
private: 
    static Singleton* _instance; 
    QString _name; 
 
public: 
    static int test; 
 
public: 
    static Singleton *createInstance(); 
    void setName(QString name); 
    QString getName(); 
}; 
 
#endif // SINGLETON_H 
#ifndef SINGLETON_H
#define SINGLETON_H

#include <QString>

class Singleton
{
public:
    Singleton();

private:
    static Singleton* _instance;
    QString _name;

public:
    static int test;

public:
    static Singleton *createInstance();
    void setName(QString name);
    QString getName();
};

#endif // SINGLETON_H
 

singleton.cpp

[html] view plaincopyprint?#include <QDebug> 
#include "singleton.h" 
 
Singleton* Singleton::_instance=NULL; 
int Singleton::test = 0; 
 
Singleton::Singleton() 

    qDebug()<<"construct"; 
    test = 1; 

 
Singleton* Singleton::createInstance() 

    if(_instance == NULL) 
    { 
        _instance = new Singleton; 
    } 
 
    return _instance; 

 
void Singleton::setName(QString name) 

    _name = name; 

 
 
QString Singleton::getName() 

    return _name; 

#include <QDebug>
#include "singleton.h"

Singleton* Singleton::_instance=NULL;
int Singleton::test = 0;

Singleton::Singleton()
{
    qDebug()<<"construct";
    test = 1;
}

Singleton* Singleton::createInstance()
{
    if(_instance == NULL)
    {
        _instance = new Singleton;
    }

    return _instance;
}

void Singleton::setName(QString name)
{
    _name = name;
}


QString Singleton::getName()
{
    return _name;
}
 

main.cpp

[html] view plaincopyprint?#include <QDebug> 
#include "singleton.h" 
 
int main(void) 

    Singleton *s1 = Singleton::createInstance(); 
    Singleton *s2 = Singleton::createInstance(); 
 
    if(s1 == s2) 
    { 
        qDebug()<<"s1 , s2 are the same instance"; 
    } 
 
 
    s1->setName("zhangsan"); 
    qDebug()<<s1->getName(); 
    qDebug()<<s2->getName(); 
    s2->setName("lisi"); 
    qDebug()<<s1->getName(); 
    qDebug()<<s2->getName(); 
 
    //Test1 
    int i; 
    i = s1->test; 
    i = Singleton::test; 
    s1->createInstance(); 
 
    Singleton *s3 = new Singleton; 
    i = s3->test; 
 
    //Test2 
    qDebug()<<s1->test;//s1->test = 1 
    s2->test++; 
    qDebug()<<s1->test;//s1->test = 2 
    qDebug()<<s2->test;//s2->test = 2 
    Singleton *s4 = new Singleton; 
    qDebug()<<s4->test;//s4->test = 1 
    Singleton *s5 = new Singleton; 
    qDebug()<<s5->test;//s5->test = 1 
 
    s1->test++; 
    s4->test++; 
    qDebug()<<s1->test;//s1->test = 3 
    qDebug()<<s2->test;//s2->test = 3 
    qDebug()<<s3->test;//s3->test = 3 
    qDebug()<<s4->test;//s4->test = 3 
    qDebug()<<s5->test;//s5->test = 3 
     
    return 0; 

#include <QDebug>
#include "singleton.h"

int main(void)
{
    Singleton *s1 = Singleton::createInstance();
    Singleton *s2 = Singleton::createInstance();

    if(s1 == s2)
    {
        qDebug()<<"s1 , s2 are the same instance";
    }


    s1->setName("zhangsan");
    qDebug()<<s1->getName();
    qDebug()<<s2->getName();
    s2->setName("lisi");
    qDebug()<<s1->getName();
    qDebug()<<s2->getName();

    //Test1
    int i;
    i = s1->test;
    i = Singleton::test;
    s1->createInstance();

    Singleton *s3 = new Singleton;
    i = s3->test;

    //Test2
    qDebug()<<s1->test;//s1->test = 1
    s2->test++;
    qDebug()<<s1->test;//s1->test = 2
    qDebug()<<s2->test;//s2->test = 2
    Singleton *s4 = new Singleton;
    qDebug()<<s4->test;//s4->test = 1
    Singleton *s5 = new Singleton;
    qDebug()<<s5->test;//s5->test = 1

    s1->test++;
    s4->test++;
    qDebug()<<s1->test;//s1->test = 3
    qDebug()<<s2->test;//s2->test = 3
    qDebug()<<s3->test;//s3->test = 3
    qDebug()<<s4->test;//s4->test = 3
    qDebug()<<s5->test;//s5->test = 3
   
    return 0;
}
 
 

【运行结果】

[html] view plaincopyprint?construct  
s1 , s2 are the same instance  
"zhangsan"  
"zhangsan"  
"lisi"  
"lisi"  
construct  
1  
2  
2  
construct  
1  
construct  
1  
3  
3  
3  
3  

construct
s1 , s2 are the same instance
"zhangsan"
"zhangsan"
"lisi"
"lisi"
construct
1
2
2
construct
1
construct
1
3
3
3
3
3
 
s1、s2是同一实例,调用s2->setName修改s2的同时,修改了s1

 

【实例剖析】

实例1

QT中对SQL进行相关操作,就是采用单例模式。以sqlite3数据库进行说明。

1 建立连接时,调用类似下述代码:

[html] view plaincopyprint?bool initSql::createConnection() 

    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); 
    db.setDatabaseName(dbName); 
    if (!db.open()) 
    { 
        QMessageBox::warning(0, QObject::tr("Database Error"),db.lastError().text()); 
        return false; 
    } 
    return true; 

bool initSql::createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(dbName);
    if (!db.open())
    {
        QMessageBox::warning(0, QObject::tr("Database Error"),db.lastError().text());
        return false;
    }
    return true;
}
 

2 断开连接时,调用:

[html] view plaincopyprint?void initSql::closeConnection() 

    QString name; 
    { 
        name = QSqlDatabase::database().connectionName(); 
    } 
 
    QSqlDatabase::database().close(); 
    QSqlDatabase::removeDatabase(name); 

void initSql::closeConnection()
{
    QString name;
    {
        name = QSqlDatabase::database().connectionName();
    }

    QSqlDatabase::database().close();
    QSqlDatabase::removeDatabase(name);
}
 
 

这样做,好处是,只需要连接数据库一次,得到静态的实例,就可以在工程任何地方,通过该实例,对数据库进行操作,而不必连接第二次。缺陷是,不能同时创建一个以上的实例。

 

其实,单例模式,优点和缺陷都是由于只能创建一个实例引起的。

优点容易理解,讲讲缺点。

运用sqlite3建立了一个数据库,现在编程对数据库进行访问。现在的问题是,有可能会出现这样的情形,在同一时刻,网页cgi程序和QT编写的程序要同时对数据库访问。cgi没有采用单例模式,而Qt采用单例模式。CGI操作sqlite3请参考CGI如何用C控制sqlite3?一文。

假设,QT程序已经与数据库建立了连接。此时,实例是静态的,断开连接后,静态对象并没有撤销,而是继续留在内存中,直到程序结束为止。www.2cto.com

QT在断开连接时,一直会占用该数据库。这样的后果是造成cgi程序无法与数据库建立连接。我们想的办法是,先将数据库与Qt断开连接。让cgi程序建立连接,使用完毕,断开连接。再建立Qt与数据库的连接。更加理想的方式是,每执行一次操作像cgi程序一样,执行类似建立连接->操作->断开连接 的过程。理想总是美好的,现实并非如此。

由于,Qt与数据的连接是单例的,只能创建一个实例。在断开连接后,实例并未被撤销。实际上,在退出程序前,都无法再与数据库建立连接了。连接被关闭的效果是,不能再利用该连接对数据库进行任何操作,并且无法再次连接,直到程序结束后重新开始。

 

*这段解释,给Qt 数据库编程敲响了一个警钟,在应用了单例模式的情况下,不要视图关闭数据库连接,“让道”给其他程序,然后重新进行连接。

 

实例2

在JavaMe 编程连载(9) - 重构之数据永久存储一文中,阐述了模拟一个通用数据库的方法。在文章最后,分析了单例模式的影响。如果程序中要建立多个实例,那么不要采用单例模式,编码实例是static的。

 

 作者:tandesir