美文网首页设计模式学习收藏
设计模式:单例模式

设计模式:单例模式

作者: 狄仁杰666 | 来源:发表于2022-02-07 17:31 被阅读0次

前言

来啦老铁!

笔者正在学习常见的设计模式,且将设计模式系列学习文章归入 “设计模式学习” 专题,赶快关注专题一起学习吧!

今天我们继续学习:

  • 单例模式

备注:笔者的学习资料大部分来源于:菜鸟教程

学习路径

  1. 单例模式简介;
  2. 单例模式代码实现;
  3. 单例模式优缺点分析;
  4. 单例模式使用场景介绍;

1. 单例模式简介;

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

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

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

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

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

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

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

2. 单例模式代码实现;

虽然单例模式很 Java 的样子,单其实其他语言一样能用、有用,我们还是从 python 的角度来看下单例模式怎么用代码来实现吧~

据了解,python 中有几种方式可以实现单例模式:
1. 使用模块实现单例;
2. 使用函数装饰器实现单例;
3. 使用类装饰器实现单例;
4. 使用 new 关键字实现单例;
5. 使用 metaclass 实现单例;

1). 使用模块实现单例;

python的模块就是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc文件,当第二次导入的时候,就会直接加载.pyc文件,而不是再次执行模块代码.如果我们把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

作者:莫辜负自己的一世韶光
链接:https://www.jianshu.com/p/6a1690f0dd00
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 创建一个类,例如 Database 类;
import time


class Database:
    def __init__(self):
        time.sleep(1)
        pass

    def get_mysql(self):
        print("get mysql...")


database = Database()

  • 使用 Database 类演示单例模式;
from database import database


def test():
    database.get_mysql()
    print("id:", id(database))
    database.get_mysql()
    print("id:", id(database))


if __name__ == '__main__':
    test()

  • 结果:


    单例模式演示

从打印出的对象 id 可以发现,该方式多次使用 database 对象时未创建多个 database 对象,符合单例模式;

2). 使用函数装饰器实现单例;
  • 创建一个类,如 Car 类,并用函数装饰器完成单例模式;
def singleton(cls):
    _instance = {}

    def fn():
        if cls not in _instance:
            # 实例化类,并用字典存储
            _instance[cls] = cls()
        # 返回字典中存储的类
        return _instance[cls]

    return fn


@singleton
class Car:
    def __init__(self):
        pass

    def get_bmw(self):
        print("get BMW...")


if __name__ == "__main__":
    car_1 = Car()
    car_1.get_bmw()
    print(id(car_1))

    car_2 = Car()
    car_2.get_bmw()
    print(id(car_2))

  • 使用函数装饰器实现的单例模式;

结果:


函数装饰器实现的单例模式

成功~

3). 使用类装饰器实现单例;
  • 创建一个类,如 Phone 类:
class Singleton:
    def __init__(self, cls):
        self._cls = cls
        self._instance = {}

    def __call__(self):
        if self._cls not in self._instance:
            self._instance[self._cls] = self._cls()
        return self._instance[self._cls]


@Singleton
class Phone:
    def __init__(self):
        pass

    def get_huawei(self):
        print("Hua Wei...")


if __name__ == "__main__":
    phone_1 = Phone()
    phone_1.get_huawei()
    print(id(phone_1))

    phone_2 = Phone()
    phone_2.get_huawei()
    print(id(phone_2))

  • 使用类装饰器实现的单例模式;
    结果:


    类装饰器实现的单例模式

与函数装饰器方式类似,成功~

4). 使用 new 关键字实现单例;
  • 创建一个类,如 School 类:
class School(object):
    _instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance

    def get_qinghua(self):
        print("qing hua...")


if __name__ == '__main__':
    school_1 = School()
    school_1.get_qinghua()
    print(id(school_1))

    school_2 = School()
    school_2.get_qinghua()
    print(id(school_2))

  • 使用 new 关键字实现的单例模式;
    结果:
    __new__ 关键字实现的单例模式

成功~

5). 使用 metaclass 实现单例;
  • 了解使用 type 创造类的方法;
    在继续实现之前,我们需要了解一个 python 知识点,即使用 type 创造类的方法,其例子类似:
def fn(self):
    # self 可以为任意其他字符,这里只是一个类似占位符的东西
    print("this is a test")


if __name__ == "__main__":
    # clazz 可以为其他任意字符
    # "fn" 为可被访问的方法名,也可以为其他任意名字,"fn" 与 fn 可以名字不同
    clazz = type("clazz", (), {"fn": fn})
    test_class = clazz()
    test_class.fn()

这样,我们就会用 type 创建类了,笔者也是第一次使用这种方式,只能说 python 还是有很多隐藏的知识点呀~

  • 创建一个类,如 Color 类:
class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class Color(metaclass=Singleton):
    def __init__(self):
        pass

    def get_blue(self):
        print("get blue color...")


if __name__ == "__main__":
    color_1 = Color()
    color_1.get_blue()
    print(id(color_1))

    color_2 = Color()
    color_2.get_blue()
    print(id(color_2))

  • 使用 mataclass 实现单例模式;
    结果:


    mataclass 实现单例模式

成功~

注意,以上 5 种实现单例模式的方式,除了第 1 种在多线程场景下,能确保只有 1 个对象被创建,其他的均不能确保只有 1 个对象被创建,“单例”得还是不够,因此,如果在多线程场景下,则需要给对象实例化的时候加锁,例如:

import threading
from time import sleep


class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        with Color.__lock__:
            if cls not in cls._instances:
                cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]


class Color(metaclass=Singleton):
    __lock__ = threading.Lock()

    def __init__(self):
        sleep(1)
        pass

    def get_blue(self):
        print("get blue color...")


def worker():
    color = Color()
    print(id(color), "\n")


if __name__ == "__main__":
    # color_1 = Color()
    # color_1.get_blue()
    # print(id(color_1))
    #
    # color_2 = Color()
    # color_2.get_blue()
    # print(id(color_2))

    task_list = []
    for i in range(5):
        t = threading.Thread(target=worker)
        task_list.append(t)

    for t in task_list:
        t.start()

    for t in task_list:
        t.join()

或者使用装饰器做一个锁,这样就能做到“全局”只有一个实例了~

3. 单例模式优缺点分析;

优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。

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

4. 单例模式使用场景介绍;

  1. 一个班级只有一个班主任。
  2. Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
  3. 一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
  4. 要求生产唯一序列号。
  5. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  6. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

其他参考文献:https://zhuanlan.zhihu.com/p/37534850

如果本文对您有帮助,麻烦点赞、关注!

谢谢!

相关文章

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • python 单例

    仅用学习参考 目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计...

  • 基础设计模式:单例模式+工厂模式+注册树模式

    基础设计模式:单例模式+工厂模式+注册树模式 单例模式: 通过提供自身共享实例的访问,单例设计模式用于限制特定对象...

  • 单例模式

    JAVA设计模式之单例模式 十种常用的设计模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法...

  • 设计模式之单例模式

    单例设计模式全解析 在学习设计模式时,单例设计模式应该是学习的第一个设计模式,单例设计模式也是“公认”最简单的设计...

网友评论

    本文标题:设计模式:单例模式

    本文链接:https://www.haomeiwen.com/subject/tahpcrtx.html