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

这篇文章将会介绍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、单例模式适用场景

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

  • 当一个类只能有一个实例,并且可以从全局访问到它
  • 当要严格控制全局变量个数时,可以使用单例模式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值