zoukankan      html  css  js  c++  java
  • 单例模式

    前言

    单例模式应该是所有接触的设计模式初学者第一个听过的设计模式,这个模式应该是所有设计模式中最简单的一个模式了。值得注意的是,许多开发者将单例模式视为一种反模式,因此单例模式在 Python 中的使用频率现在越来越少了。

    反模式(英文:Anti-patterns或pitfalls), 是指用来解决问题的带有共同性的不良方法。它们已经经过研究并分类,以防止日后重蹈覆辙,并能在研发尚未投产时辨认出来。软件开发中公认的反模式

    意图

    保证一个类仅有一个实例,并提供一个访问它的全剧访问点。

    UML 图

    简单看一下单例模式的 UML 图

    应用场景

    单例模式最常使用的场景就是连接数据库以及日志等等,单例模式使用的场景通常有以下几个特点

    • 需要频繁实例化然后销毁的对象。
    • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
    • 有状态的工具类对象。
    • 频繁访问数据库或文件的对象。 

    代码

    接下来让我们通过代码实现一个单例模式

    简单单例模式

    class SingletonMeta(type):
        _instances = {}
    
        def __call__(cls, *args, **kwargs):
            if cls not in cls._instances:
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
            return cls._instances[cls]
    
    
    class Singleton(metaclass=SingletonMeta):
        def some_action(self):
            pass
    
    
    if __name__ == "__main__":
        s1 = Singleton()
        s2 = Singleton()
    
        if id(s1) == id(s2):
            print("Singleton works")
        else:
            print("Singleton failed")
    

    看一下输出:

    Singleton works
    

    线程安全单例模式

    上面的单例模式可能会在多线程环境中出错,所以为了解决这个问题,我们将单例模式再升级一下

    from threading import Lock, Thread
    
    
    class SingletonMeta(type):
    
        _instances = {}
        _lock: Lock = Lock()
    
    
        def __call__(cls, *args, **kwargs):
            with cls._lock:
                if cls not in cls._instances:
                    instance = super().__call__(*args, **kwargs)
                    cls._instances[cls] = instance
            return cls._instances[cls]
    
    
    class Singleton(metaclass=SingletonMeta):
        value: str = None
    
        def __init__(self, value: str) -> None:
            self.value = value
    
        def some_action(self):
            pass
    
    def test_singleton(value: str) -> None:
        singleton = Singleton(value)
        print(singleton.value)
    
    
    if __name__ == "__main__":
        # 由于是单例模式,预期两个线程输出的结果应该一样,都是第一个 FOO
        process1 = Thread(target=test_singleton, args=("FOO",))
        process2 = Thread(target=test_singleton, args=("BAR",))
        process1.start()
        process2.start()
    

    看一下输出结果:

    FOO
    FOO
    

    总结

    单例模式优缺点

    优点

    • 你可以保证一个类只有一个实例。
    • 你获得了一个指向该实例的全局访问节点。
    • 仅在首次请求单例对象时对其进行初始化。

    缺点

    • 违反了单一职责原则
    • 单例模式可能掩盖不良设计, 可能会隐藏类之间的依赖关系。
    • 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
    • 单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法。
    • 单例不支持有参数的构造函数,比如我们创建一个连接池的单例对象,我们没法通过参数来指定连接池的大小。
  • 相关阅读:
    如何理解对象、属性、方法?
    添加超链接,请始终将正斜杠添加到子文件夹
    什么导致table不稳固?
    HTML css和js浏览器兼容问题
    理解前端数据双向绑定原理——Object.defineProperty()
    Js事件传播流程
    移动端开发项目注意事项
    get与post的区别
    Web 页面性能优化与SEO优化
    我的WCF项目系列之二WCF初级应用
  • 原文地址:https://www.cnblogs.com/leetao94/p/13320769.html
Copyright © 2011-2022 走看看