【设计模式】创建型模式之单例Singleton

在设计模式中,单例Singleton是一种简单、常用的创建型模式,它的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点,下面以C++为例说明单例的使用。

1、一个简单的单例

一个最一般的单例类图如下所示:

这里写图片描述

如下Singleton类,使用了单例模式,仅仅对外部提供了一个访问接口,即public权限的静态成员函数Instance,与之相关联的是一个private权限的静态成员变量,用来保存唯一的类实例。为了保证外部无法创建一个类对象,这里的构造函数为private,同时也意味着阻止了类的子类化。另外还声明了拷贝构造函数和赋值操作符为private,并没有真正去定义实现它们,进一步阻止了外部的可操作性。

/*
 * @class Singleton
 */
class Singleton
{
public:
    static Singleton* Instance();

private:
    Singleton();
    Singleton(const Singleton&);
    Singleton& operator =(const Singleton&);

private:
    static Singleton *s_self;
};

下面是Singleton类的实现,初始化时并没有创建对象,而是在外部第一次通过Instance函数访问时才进行了实例化,这便是所谓的懒汉模式。

/* Singleton - lazy*/
Singleton* Singleton::s_self = 0;

Singleton* Singleton::Instance()
{
    if (0 == s_self) {
        s_self = new Singleton;
    }
    return s_self;
}

Singleton::Singleton()
{
}

2、单例的子类化问题

考虑到一个单例类作为基类,有可能被继承,上面的代码情形就不适用了,至少其构造函数应该为protected,这样外部无法实例化,派生类也获得了构造能力,但是实例化工作交给谁来做呢,显然不是外部对象,可以尝试在父类中完成,由父类统一管理,如下例子的SingletonBase类通过环境变量来判断需要真正实例化的对象是哪一个。

/*
 * @class SingletonBase
 */
class SingletonBase
{
public:
    static SingletonBase* Instance();

protected:
    SingletonBase();

private:
    static SingletonBase *s_self;
};

/*
 * @class SingletonA
 */
class SingletonA : public SingletonBase
{
private:
    friend class SingletonBase;

    SingletonA();
};

下面是SingletonBase类和SingletonA类的实现部分。

/* SingletonBase */
SingletonBase* SingletonBase::s_self = 0;

/* uses env var */
SingletonBase* SingletonBase::Instance()
{
    if (0 == s_self) {
        const char *singletonName = getenv(ENV_SINGLETON);
        if (0 == singletonName) {
            s_self = new SingletonBase;
        }
        else {
            if (strcmp(singletonName, "SingletonA") == 0) {
                s_self = new SingletonA;
            }
            else {
                s_self = new SingletonBase;
            }
        }
    }
    return s_self;
}

SingletonBase::SingletonBase()
{
}

/* SingletonA */
SingletonA::SingletonA()
{
}

3、使用注册表

上面例子的实例化工作交给了父类,但一个缺点也暴露了出来,那就是父类硬编码了好多东西,这并不是一种好的编程风格,不过还有一种更灵活的方式,即注册表。如下例子的SingletonRegistry类,提供了Register接口给子类使用,也就是说子类在实例化时调用Register接口向父类注册信息,信息是一个map对象,map的key为子类的唯一标识符,map的value为子类的实例对象,父类将这些信息保存下来,然后在给外部使用的Instance中通过环境变量来Lookup查找所需对象是否已经注册并返回相应的结果。这个地方的实例化工作并不是由父类完成的,父类只是负责保存注册信息并查找返回注册结果,对象创建可以在外部或者子类中完成,例子中把实例化工作交给了子类,初始化时已经完成,这就是所谓的饥饿模式。实例化工作还可以在其它合适的地方创建一个static变量,保证注册工作先与Instance调用。

/*
 * @class SingletonRegistry
 */
class SingletonRegistry
{
public:
    static SingletonRegistry* Instance();

protected:
    static void Register(const std::string &name, SingletonRegistry *singleton);

    SingletonRegistry();

private:
    static SingletonRegistry* Lookup(const std::string &name);

private:
    static SingletonRegistry* s_self;
    static std::map<std::string, SingletonRegistry*> s_registry;
};

/*
 * @class SingletonB
 */
class SingletonB : public SingletonRegistry
{
public:
    SingletonB();

private:
    static SingletonB s_self;
};

下面是实现部分,因为子类只能有一个实例,所以Register注册时作了判断,如果之前已经注册,程序将直接abort退出。

/* SingletonRegistry */
SingletonRegistry* SingletonRegistry::s_self = 0;
std::map<std::string, SingletonRegistry*> SingletonRegistry::s_registry;

SingletonRegistry* SingletonRegistry::Instance()
{
    if (0 == s_self) {
        const char *singletonName = getenv(ENV_SINGLETON);
        if (0 != singletonName) {
            s_self = Lookup(std::string(singletonName));
        }
    }
    return s_self;
}

void SingletonRegistry::Register(const std::string &name, SingletonRegistry *singleton)
{
    if (s_registry.find(name) != s_registry.end()) {
        std::cout << name << " has been instantiated!" << std::endl;
        abort();
    }
    s_registry[name] = singleton;
}

SingletonRegistry::SingletonRegistry()
{
}

SingletonRegistry* SingletonRegistry::Lookup(const std::string &name)
{
    std::map<std::string, SingletonRegistry*>::iterator it = s_registry.find(name);
    if (s_registry.end() != it) {
        return it->second;
    }
    return 0;
}

/* SingletonB - hungry */
SingletonB SingletonB::s_self;

SingletonB::SingletonB()
{
    SingletonRegistry::Register(std::string("SingletonB"), this);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值