python:水果与设计模式-单例模式

单例模式(Singleton Pattern):属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。(注意:1、单例类只能有一个实例。2、单例类必须自己创建自己的唯一实例。3、单例类必须给所有其他对象提供这一实例。)

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

主要解决 一个全局使用的类频繁地创建与销毁。

何时使用 当您想控制实例数目,节省系统资源的时候。

如何解决 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码 构造函数是私有的。

优点 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例;避免对资源的多重占用。

缺点 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

注意事项 getInstance() 方法中需要使用同步锁,防止多线程同时进入造成 instance 被多次实例化。

应用实例:水果工厂最近弄到了一个牛批的火龙果,它是一个极为牛批的火龙果,会动,你敢信?!!!∑(゚Д゚ノ)ノ,如下图所示。为了更好的管理这个牛批的火龙果,专门为该火龙果设计了一个单例模式类,对它的重量、售价、产地、保质日期、等数据信息进行单独的管理

python:水果与设计模式-单例模式_第1张图片

那我们来一起使用单例模式,实现一个牛批的火龙果的管理类吧!(づ。◕ᴗᴗ◕。)づ

实现的思路

  • 主要包括两个部分:“管理者”和“火龙果”,管理者获取 唯一的火龙果对象,并对其进行信息的访问和修改等操作,而火龙果类返回其相关信息。

项目的UML用例图如下:

python:水果与设计模式-单例模式_第2张图片

实现的代码如下:

class Pitaya:

    def __init__(self):
        # 成员属性为内置,不对外开放对象创建的接口
        pass

    """
    classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,
    但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,
    类的方法,实例化对象等。
    """

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Pitaya, "_instance"):
            # hasattr() 函数用于判断对象是否包含对应的属性。
            Pitaya._instance = Pitaya(*args, **kwargs)
            Pitaya.weight = 21
            Pitaya.place = "Canaveria"
            Pitaya.price = 20000
            Pitaya.date = "2021-12-12"
        return Pitaya._instance


def show(obj):
    print("重量:%d(kg)\n产地:%s\n售价:%d(元)\n保质日期:%s\n创建信息:%s\n" % (
        obj.weight, obj.place, obj.price, obj.date, obj._instance))


if __name__ == '__main__':
    # 创建火龙果对象
    obj = Pitaya().get_instance()
    show(obj)

使用上述方法的原理为:当创建第一个火龙果对象的时候,设定属性_instance,后面再创建新的对象的时候,已经存在 _instance属性就不会再创建新的火龙果对象。

但是,按照以上方式创建的单例,当存在多个管理者同时访问该对象的时候,可能会出现并发异常

于是,为了 解决支持多线程访问的问题,对创建对象的部分加锁,优化后的代码如下:

import threading


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

    def __init__(self):
        # 成员属性为内置,不对外开放对象创建的接口
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Pitaya, "_instance"):
            with Pitaya._instance_lock:
                if not hasattr(Pitaya, "_instance"):
                    # hasattr() 函数用于判断对象是否包含对应的属性。
                    Pitaya._instance = Pitaya(*args, **kwargs)
                    Pitaya.weight = 21
                    Pitaya.place = "Canaveria"
                    Pitaya.price = 20000
                    Pitaya.date = "2021-12-12"
        return Pitaya._instance


def show(obj):
    print("重量:%d(kg)\n产地:%s\n售价:%d(元)\n保质日期:%s\n创建信息:%s\n" % (
        obj.weight, obj.place, obj.price, obj.date, obj._instance))


def task(arg):
    obj = Pitaya.instance()
    show(obj)


if __name__ == '__main__':
    for i in range(5):
        t = threading.Thread(target=task, args=[i, ])
        t.start()

相关的测试用例:

python:水果与设计模式-单例模式_第3张图片

可以通过上述的创建信息发现,我们访问的这些火龙果对象均为同一个对象,所以该单例模式的实现成功!

本文关于设计模式的讲解思想,参考链接:单例模式

关于@classmethod的用法,参考链接:python中的@classmethod的作用

更多的单例模式的实现方式,参考链接:Python中的单例模式的几种实现方式的及优化

如果有对装饰器的用法还不熟悉的小伙伴,参考这里的讲解:Python小技巧:装饰器(Decorator)

往期推荐: 点这里->Python:水果与设计模式-工厂模式

往期推荐: 点这里->Python:水果与设计模式-抽象工厂模式

你可能感兴趣的:(python:水果与设计模式,python,设计模式,python,多线程)