设计模式:单例(Singleton Pattern)

使用场景

单例确保一个类只有一个实例存在。比如在服务器的配置信息存放一个文件,客户端通过一个 AppConfig 的类来读取配置文件的信息,如果在程序运行期间,很多地方都需要使用配置文件的内容,也就是说很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,会浪费内存。这时就应该使用单例,确保只有一个实例。

Python 实现单例

  1. 使用模块
  2. 使用 new
  3. 使用装饰器
  4. 使用元类

使用模块

原理:Python 的模块就是天然的单例。模块第一次导入时会生成 .pyc,第二次导入就会直接加载 .pyc,不会执行模块代码。

# singleton.py
class Singleton:
  def foo(self):
      pass
# 注意这行
my_singleton = Singleton()

调用

from singleton import my_singleton
my_singleton.foo()

使用 __new__

# __new__ 是拦截器,会在 __init__ 之前被调用
class Singleton:
    _instance = None

    def __new__(cls, *args, **kw):
        if not cls._instance:
            # cls._instance = object.__new__(cls, *args, **kw)
            cls._instance = super().__new__(cls, *args, **kw)
        return cls._instance


if __name__ == '__main__':
    s = Singleton()
    print(s)

使用装饰器

from functools import wraps


def single_dec(cls):
    _instance = {}
    
    @wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in _instance:
            _instance[cls] = cls()
        return _instance[cls]
    return get_instance


@single_dec
class Single:
    pass 


if __name__ == '__main__':
    s = Single()

使用元类

注意使用元类和使用 __new__ 的不同。

class Singleton(type):
    _instance = None

    def __call__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance


class SingleIns(metaclass=Singleton):
    pass


if __name__ == '__main__':
    s = SingleIns()
    ss = SingleIns()
    print(id(s), id(ss))

Java 中使用单例

class Soup2{
  private Soup2(){}
  private static Soup2 ps1 = new Soup2();
  public static Soup2 access(){
      return ps1;
  }
  public void f() {}
}

把构造器声明为 private 可以阻止直接创建某个类的实例;

如果没有创建构造器的话,编译器会默认创建一个不带任何参数的构造器。

所以,为了阻止编译器创建默认的构造器,需要手动创建一个构造器。

你可能感兴趣的:(设计模式:单例(Singleton Pattern))