【笔记整理】23种设计模式——单例模式

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式分为懒汉式和饿汉式
饿汉式:当一个类在引入或加载时,实例就创建并初始化。
优点:

  • 提前创建好实例,避免在程序运行的时候,再去初始化导致的性能问题。
  • 按照 fail-fast 的设计原则(有问题及早暴露),有问题时初始化就能暴(比如内存溢出),不用等到线上运行时,影响系统可用性。

缺点:

  • 初始化资源耗时,如果需要初始化的对象很多,程序加载时间太久,体验不好。
  • 资源效率不高,实例初始化出来后可能一直没有使用到,造成浪费

懒汉式:延迟加载,只有在实例需要使用时,才初始化实例。

  • 优缺点与饿汉式相反

Python

1.饿汉模式

当一个类在引入或加载时,实例就创建并初始化。
在类被加载时就将自己实例化(静态初始化)。优点是躲避了多线程的安全性问题,缺点是提前占用系统资源
python模块就已经是单例模式了, 但这不是面向对象,而是模块的数据,变量或者全局对象

# 建立一个类
class Log:
    pass

# 再模块种实例化这个类,这样其他模块引入此模块中的实例时,就实现了单例模式
# 其他模块通过 import log 使用实例
log = Log()

2.懒汉模式

在第一次被引用时才将自己实例化。避免开始时占用系统资源,但是有多线程访问安全性问题

# 一般用new方法
# 基类
class Singleton:
    # 设置一个静态变量,用来保证实例的唯一性
    # _instance = None  # 添加此行会出错:AttributeError: 'NoneType' object has no attribute 'a'

    def __new__(cls, *args, **kwargs):
        # 用一个类属性来表示类的实例,如果类已经有这个属性(实例)了,就直接返回实例,如果没有再创建
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance


# 继承类(实现单例模式效果的类),想要把这个类设置成为单例的类,就去继承单例类这个基类,也就继承了new方法
class MyClass(Singleton):
    def __init__(self, a):
        self.a = a


# 创建一个MyClass的对象
a = MyClass(10)  # 首次实例化这个单例类
b = MyClass(20)  # 再次实例化这个类  -结果会变为新实例化时的值-

# 验证
print(a.a)    # 20
print(b.a)    # 20
print(id(a),id(b))    # id相同,同一个实例

'''
用到单例模式的场景:希望这个实例最好只出现一次
比如写一个web框架,这个框架有一个日志系统,这个日志对象只能有一个,不能两个日志对象同时操作一个同一个文件,不然会出现冲突
数据库连接池连接器,也最好做成单例的,不能建立多个数据库连接
操作系统的文件系统
'''

双重检测

饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。有一种既支持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方式。

通过双重检测方式,我们要保证同一时刻,多个线程只有一个能够操作实例的创建,我们可以通过多线程锁来实现。只要实例被创建之后,即便再调用 get_instance 函数也不会再进入到加锁逻辑中了
具体实例如下:

from threading import Lock


class Singleton:
    """
    通过一个多线程锁保证同一时刻最多只有一个线程能创建实例
    不能通过 new 方法创建实例
    """
    # 线程锁
    _lock = Lock()

    def __new__(cls, *args, **kwargs):
        msg = "Cann't new a SingletonSample class, please use get_instance method!"
        raise NotImplementedError(msg)

    # 静态方法
    @classmethod
    def get_instance(cls):
        if not hasattr(cls, "_instance"):
            with cls._lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super().__new__(cls)
        return cls._instance

JavaScript

单例模式的思路是:保证一个类只能被实例一次,每次获取的时候,如果该类已经创建过实例则直接返回该实例,否则创建一个实例保存并返回。

单例模式的核心就是创建一个唯一的对象,而在javascript中创建一个唯一的对象太简单了,为了获取一个对象而去创建一个类有点多此一举。如const obj = {},obj就是独一无二的一个对象,在全局作用域的声明下,可以在任何地方对它访问,这就满足了单例模式的条件。

我们常见到的登录弹窗,要么显示要么隐藏,不可能同时出现两个弹窗,下面我们通过一个类来模拟弹窗。

class LoginFrame {
    static instance = null
    constructor(state){
        this.state = state
    }
    show(){
        if(this.state === 'show'){
            console.log('登录框已显示')
            return
        }
        this.state = 'show'
        console.log('登录框展示成功')
    }
    hide(){
        if(this.state === 'hide'){
            console.log('登录框已隐藏')
            return
        }
        this.state = 'hide'
        console.log('登录框隐藏成功')
    }
    // 通过静态方法获取静态属性instance上是否存在实例,如果没有创建一个并返回,反之直接返回已有的实例
    static getInstance(state){
        if(!this.instance){
            this.instance = new LoginFrame(state)
        }
        return this.instance
    }
}
const p1 = LoginFrame.getInstance('show')
const p2 = LoginFrame.getInstance('hide')
console.log(p1 === p2) // true


总结

以上就是单例模式的几种实现方法,除了以上的几种实现方法,还有其他各种各样的实现方法,重要的是能够理解其实现原理和思路。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值