vlambda博客
学习文章列表

设计模式第四谈:单例模式

这篇文章将会介绍23种设计模式的单例模式

1、什么是单例模式

“四人帮”编写的《设计模式-可复用面相对象软件的基础》一书中给出定义是:保证一个类只有一个实例,并且提供一个访问它的全局访问点

对于一些类来讲,只有一个实例是非常重要的,比如:在访问文件时,只有一个写实例。通过使用单例模式,让类自身创建、保存它的唯一的实例。

2、Python实现

由于Python语言的特性,可以有多种方式实现单例模式。接下来的Python实现方式会涉及一些Python的基础知识,对这方面不太了解的,可查看接下来几篇的文章。

2.1 基于.pyc文件实现

.pyc文件是.py文件编译后的二进制文件,.pyc相比于.py文件,加载速度会有所提升。在执行.py文件代码时,会在磁盘上(Linux系统会存放在Package下的pycache文件夹)寻找对应的.pyc文件,如果.pyc的文件修改时间晚于.py文件的修改时间,表示.py文件没有修改过,可直接加载,反之会重新生成.pyc文件。

基于以上特性,可以在一个.py文件中定义一个类并实例化一个类,其他的.py文件中通过import方式导入实例化的类对象,如下:

  • Car.py

# coding: utf-8


class Car(object):
   def driver(self):
       pass


car = Car()
  • singleton_example.py

# coding: utf-8

from Singleton.pyc.Car import car


if __name__ == '__main__':
   car.driver()

2.2 基于装饰器实现

装饰器本质上就是一个函数,这个函数接受其装饰的函数作为参数,并将其以一个新的修改后的函数进行替换。以装饰器实现单例模式代码如下:

  • Car.py

# coding: utf-8

from functools import wraps


def singleton(cls):
   """装饰器函数"""

   _cls_instance = []

   @wraps(cls)
   def _create_instance():
       if len(_cls_instance) == 0:
           _cls_instance.append(cls())

       return _cls_instance[0]

   return _create_instance


@singleton
class Car(object):
   def drive(self):
       pass
  • singleton_example.py

# coding: utf-8

from Singleton.decorator.Car import Car


if __name__ == '__main__':
   car_bmw = Car()
   car_benz = Car()

   print(car_bmw)
   print(car_benz)

2.3  基于静态方法实现

  • Car.py

# coding: utf-8


class Car(object):
   def __init__(self, *args, **kwargs):
       pass

   @classmethod
   def instance(cls, *args, **kwargs):
       if not hasattr(Car, "_instance"):
           Car._instance = Car(*args, **kwargs)

       return Car._instance

   def drive(self):
       pass
  • singleton_example.py

# coding: utf-8

from Singleton.InstanceMethod.Car import Car


if __name__ == '__main__':
   car_bmw = Car.instance()
   car_benz = Car.instance()

   print(car_bmw)
   print(car_benz)

2.4 基于__new__实现

  • Car.py

# coding: utf-8


class Car(object):
   def __new__(cls, *args, **kwargs):
       if not hasattr(Car, "_instance"):
           Car._instance = object.__new__(cls)

       return Car._instance

   def __init__(self, *args, **kwargs):
       pass

   def drive(self):
       pass
  • singleton_example.py

# coding: utf-8

from Singleton.NewAndInit.Car import Car


if __name__ == '__main__':
   car_bmw = Car()
   car_benz = Car()

   print(car_bmw)
   print(car_benz)

2.5 基于metaclass实现

  • Car.py

# coding: utf-8


class CarMetaClass(type):
   _car_instance = None

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

       return cls._car_instance


class Car(metaclass=CarMetaClass):
   def __init__(self, *args, **kwargs):
       pass

   def drive(self):
       pass
  • singleton_example.py

# coding: utf-8

from Singleton.MetaClass.Car import Car


if __name__ == '__main__':
   car_bmw = Car()
   car_benz = Car()

   print(car_bmw)
   print(car_benz)

2.6 单例模式的多线程安全

以上5种实现单例模式的方法中,使用类实现的后三种模式在使用多线程时,会存在创建多个实例的现象,这时需要在创建类实例是添加一把进程锁。

  • 基于静态方法的实现:Car.py

import threading

class Car(object):
   _instance_lock = threading.Lock()

   def __init__(self, *args, **kwargs):
       pass

   @classmethod
   def instance(cls, *args, **kwargs):
       if not hasattr(Car, "_instance"):
           with Car._instance_lock:
               if not hasattr(Car, "_instance"):
                   Car._instance = Car(*args, **kwargs)

       return Car._instance

   def drive(self):
       pass
  • 基于__new__的实现:Car.py

# coding: utf-8

import threading


class Car(object):
   _instance_lock = threading.Lock()

   def __new__(cls, *args, **kwargs):
       if not hasattr(Car, "_instance"):
           while cls._instance_lock:
               if not hasattr(Car, "_instance"):
                   Car._instance = object.__new__(cls)

       return Car._instance

   def __init__(self, *args, **kwargs):
       pass

   def drive(self):
       pass
  • 基于metaclass的实现:Car.py

# coding: utf-8

import threading


class CarMetaClass(type):
   _car_instance = None
   _instance_lock = threading.Lock()

   def __call__(cls, *args, **kwargs):
       if not cls._car_instance:
           with cls._instance_lock:
               if not cls._car_instance:
                   cls._car_instance = super().__call__(cls, *args, **kwargs)

       return cls._car_instance


class Car(metaclass=CarMetaClass):
   def __init__(self, *args, **kwargs):
       pass

   def drive(self):
       pass

3、单例模式UML类图

基于以上的Python代码来看,单例模式只有一个类参与,其UML类图如下图所示。


4、单例模式优缺点

  • 优点:

  1. 单例模式只有一个类实例,这样可以减少内存开销和性能开销,特别是当某个对象需要频繁的创建和销毁时

  2. 减少对资源的重复使用

  3. 单例模式可以在系统设置全局的访问点,优化和共享资源访问

  • 缺点

  1. 单例模式一般没有接口,很难扩展,如果需要扩展,只能修改源代码

  2. 在多线程开发中,单例模式需要进程锁,会损耗一部分性能,同时对于测试来说也是不利的

5、单例模式适用场景

在如下情景可使用单例模式:

  • 当一个类只能有一个实例,并且可以从全局访问到它

  • 当要严格控制全局变量个数时,可以使用单例模式