单件(Singleton)

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
  • 当这个唯一的实例应该是通过子类化可扩展的,并且客户应该无须更改代码就能使用一个扩展的实例时
/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%8D%95%E4%BB%B6/%E5%8D%95%E4%BB%B6%E7%BB%93%E6%9E%84%E5%9B%BE.png
单件结构图
  • Singleton:定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作
  • 可能负责创建它自己的唯一实例
  • 对唯一实例的受控访问:严格控制客户怎样以及如何访问实例
  • 缩小命名空间:Singleton模式是对全局变量的一种改进,避免了污染命名空间
  • 允许对操作和表示的精化:可以有子类,用扩展子类的实例配置一个应用是很容易的
  • 允许可变数目的实例:可以控制实例的数目(不只是一个,而是多个)
  • 比类操作更灵活

在使用抽象工厂模式时,通常希望每种工厂只有一个实例,这样就用到了单件模式:

1
2
3
4
5
6
7
8
9
class Factory {
public:
    static Factory* Instance();
protected:
    // 构造函数是保护型的,确保仅有一个实例被创建
    Factory();
private:
    static Factory* _instance;
};
1
2
3
4
5
6
7
8
Factory* Factory::_instance = nullptr;

Factory* Factory::Instance() {
    if _instance == nullptr {
        _instance = new Factory;
    }
    return _instance;
}

如果Factory存在子类,必须决定使用哪个子类时,Instance()的实现需要修改:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Factory* Factory::Instance() {
    if _instance == nullptr {
        const char* style = getenv("STYLE");
        
        if (strcmp(style, "A") == 0) {
            _instance = new FactoryA;
        } else if (strcmp(style, "B") == 0) {
            _instance = new FactoryB;
            
        // 其他可能情况
            
        } else {
            // 默认情况
            _instance = new Factory;
        }
    }
    return _instance;
}

上述方法在定义新的Factory子类时,都必须修改Instance(),一个解决办法是使用注册表办法

  • 保证一个唯一的实例:将创建实例的操作隐藏在类操作后面,由它保证只有一个实例被创建

  • 创建Singleton类的子类:使用一个单件注册表Instance()函数在注册表中查找单件并返回:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    class Singleton {
    public:
        static Singleton* Instance();
        static void Register(const char* name, Singleton*);
    protected:
        static Singleton* Lookup(const char* name);
    private:
        static Singleton* _instance;
        static List<NameSingletonPair>* _register;
    };
    

    Register以给定的名字注册Singleton实例,Instance()调用Lookup()函数根据给定单件的名称进行查找:

    1
    2
    3
    4
    5
    6
    7
    
    Singleton* Singleton::Instance() {
        if _instance == nullptr {
            const char* name = getenv("SINGLETON");
            _instance = Lookup(name);
        }
        return _instance;
    }
    

    Singleton在何处注册自己?一种方法是在构造函数中:

    1
    2
    3
    4
    
    Singleton::Singleton() {
        //...
        Singleton::Register("SingletonA", this);
    }
    

    用这种方法Singleton类不再负责创建单件,而是确保选择的单件对象可以在系统中被访问。创建由定义一个静态实例完成:

    1
    
    static SingletonA theSingletonA
    

    Register()函数仅在Singleton构造时被调用一次,缺点是所有可能的Singleton都必须被创建才能注册。