设计模式学习手册(一)(单例模式)

写在前面

  • 设计模式最初是被 GoF 于 1995 年提出的,GoF(Gang of Four,四人帮)即 Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides。他们四人于 1995 年出版了一本书《Design Patterns:Elements of Reusable Object-Oriented Software》(翻译成中文是《设计模式 可复用面向对象软件的基础》),第一次将设计模式提升到理论高度,并将之规范化,该书提出了 23 种经典的设计模式。
  • 市面上关于设计模式的资料大部分都是 Java、C++的案例,我自己学习时,主要写python,边学边写,这个坑也不知道什么时候填完
  • Python版本:3.8.10
  • 学习来源:https://learn.lianglianglee.com/
  • 按照主流的顺序来写,先从创建型模式开始
    • 单例模式
    • 工厂方法模式
    • 抽象工厂模式
    • 建造者模式
    • 原型模式

单例模式

  • 单例模式是一种创建型设计模式,它确保一个类仅有一个实例,并提供一个全局访问点来访问该实例

  • 通常有两种写法,懒汉式和饿汉式

    • 懒汉式单例模式在类被加载时不会立即创建实例,而是在首次需要使用该实例时才进行创建。这种方式也称为“延迟加载”或“懒加载”。
    • 饿汉式单例模式在类被加载时就会立即创建实例。这种方式在类加载时就完成了实例化,因此是线程安全的,无需额外的同步机制。
  • 吐槽下,这玩意面试经常问到,有次面试就让我手写线程安全的懒汉式

饿汉式单例模式

  • python不太好写饿汉式,不符合Python的惯用做法,我让GPT写了几次都会报错,大致意思到了就行
  • 饿汉式单例模式会在类定义时就创建实例
  • _instance里面存放对象
class SingleTest:
    _instance = None

    # 在类定义时直接创建实例
    _instance = SingleTest()

    def __new__(cls):
        # 返回已经创建的实例
        return cls._instance

# 使用
singleton_instance = SingleTest()

懒汉式单例模式

  • 在使用时才会创建,我在代码中还使用了 _isFirstInit 类变量来确保 __init__ 方法只被调用一次
  • __new__是我们通过类名进行实例化对象时自动调用的, __init__ 是在每一次实例化对象之后调用的, __new__方法创建一个实例之后返回这个实例对象,并将其传递给 __init__ 方法的 self 参数。
  • 这段代码有一个潜在的问题:它不是线程安全的。如果在多线程环境中,两个线程同时调用 __new__ 方法并发现 _instanceNone,那么它们都可能创建一个新的实例,从而违反单例模式的规则。
class SingletonPattern:
    _instance = None
    _isFirstInit = False

    def __new__(cls, name):
        if not cls._instance:
            cls._instance = super(SingletonPattern, cls).__new__(cls)
        return cls._instance

    def __init__(self, name):
        if not self._isFirstInit:
            self._name = name
            print(name)
            SingletonPattern._isFirstInit = True
        else:
            print(name)

    def show_name(self):
        print(self._name)


if __name__ == '__main__':
    single_a_instance = SingletonPattern("jack")
    single_a_instance.show_name()
    single_b_instance = SingletonPattern("tom")
    single_b_instance.show_name()
    print(single_a_instance is single_b_instance)
# 输出依次为:
# jack
# jack
# tom
# jack
# True
  • 我让GPT改进了下上面的代码,使其在多线程中更安全
  • 大致的思路是使用线程锁(如 threading.Lock)来确保在多线程环境中的原子性
import threading

class SingletonPattern:
    _instance = None
    _isFirstInit = False
    _lock = threading.Lock()  # 添加锁

    def __new__(cls, name):
        with cls._lock:  # 锁定
            if not cls._instance:
                cls._instance = super(SingletonPattern, cls).__new__(cls)
        return cls._instance

    def __init__(self, name):
        with SingletonPattern._lock:  # 锁定__init__
            if not self._isFirstInit:
                self._name = name
                SingletonPattern._isFirstInit = True

    def show_name(self):
        print(self._name)



def create_singleton(name):
    instance = SingletonPattern(name)
    instance.show_name()


if __name__ == '__main__':
    # 创建并运行多线程测试
    if __name__ == "__main__":
        threads = []

        # 创建多个线程,每个线程尝试创建单例
        for i in range(10):
            t = threading.Thread(target=create_singleton, args=(f"Thread-{i}",))
            threads.append(t)
            t.start()

        # 等待所有线程执行完毕
        for t in threads:
            t.join()
# 输出都是Thread-0
  • 其实还可以将单例的初始化和状态管理完全封装在 __new__ 方法中,避免在 __init__ 方法中进行任何与单例状态相关的逻辑,类似这样
import threading


class SingletonPattern:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, name=None):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super(SingletonPattern, cls).__new__(cls)
                cls._instance._name = name  # 直接在__new__中初始化状态  
                if name:
                    print(name)
        return cls._instance

        # 不再需要__init__方法,因为所有初始化都在__new__中完成了  

    def show_name(self):
        print(self._name)

使用metaclass 的方法

class Singleton(type):
    """单例实现方式二"""

    def __init__(cls, what, bases=None, dict=None):
        print("Singleton.__init__")
        super().__init__(what, bases, dict)
        cls._instance = None  # 初始化全局变量cls._instance为None

    def __call__(cls, *args, **kwargs):
        print("__call__")
        # 控制对象的创建过程,如果cls._instance为None则创建,否则直接返回
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance


class CustomClass(metaclass=Singleton):
    """用户自定义的类"""

    def __init__(self, name):
        print("CustomClass.__init__")
        self.__name = name

    def getName(self):
        return self.__name


tony = CustomClass("Tony")
print("-------")
karry = CustomClass("Karry")
print(tony.getName(), karry.getName())
print(tony == karry)
# 输出依次为
# Singleton.__init__
# __call__
# CustomClass.__init__
# -------
# __call__
# Tony Tony
# True
  • metaclass 在用于控制类的实例化过程,代码中 CustomClass 使用了 Singleton 作为元类,不在这里说太多metaclass的知识点了
  • 不难发现,第二次CustomClass只输出了__call__,原因是Singleton.__init__ 只在类 CustomClass 被定义时调用一次,而不是在每次实例化时调用。
  • 第一次调用 CustomClass("Tony")
    • Python 会调用 Singleton.__init__ 方法,并输出 "Singleton.__init__"
    • Python 会调用 Singleton.__call__ 方法,在 __call__ 方法中,检查 cls._instance 是否为 None。因为这是第一次调用,所以 _instance 确实是 None,因此会执行 super().__call__(*args, **kwargs) 创建实例。
    • 接着会调用 CustomClass.__init__,初始化实例属性,并输出 "CustomClass.__init__"
  • 第二次调用 CustomClass("Karry")
    • 再次调用 Singleton.__call__ 方法。
    • 此时,cls._instance 已经不再是 None(因为第一次调用时已经创建了实例并赋值给了 _instance),所以 __call__ 方法直接返回 cls._instance
    • 由于 cls._instance 已经存在,所以不会再次执行 super().__call__(*args, **kwargs),也不会再次调用 CustomClass.__init__

装饰器的方法

from functools import wraps


def singletonDecorator(func):
    """定义一个单例装饰器"""
    instance = {}

    @wraps(func)
    def wrapperSingleton(*args, **kwargs):
        if func not in instance:
            instance[func] = func(*args, **kwargs)

        print(instance)
        return instance[func]

    return wrapperSingleton


@singletonDecorator
class Singleton:
    """使用单例装饰器修饰一个类"""
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name


tony = Singleton("Tony")
print("----------")
karry = Singleton("Karry")
print(tony.getName(), karry.getName())
print(tony == karry)
# 输出依次为:
# {<class '__main__.Singleton'>: <__main__.Singleton object at 0x000001A3AD5B08B0>}
# ----------
# {<class '__main__.Singleton'>: <__main__.Singleton object at 0x000001A3AD5B08B0>}
# Tony Tony
# True
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值