Python设计模式-创建型:单例模式和工厂模式家族

张春  金牌会员 | 2022-8-25 03:09:32 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 905|帖子 905|积分 2715

Python设计模式-创建型:单例模式和工厂模式家族

知识点

  • 单例模式概念及一般实现
  • 单例模式的装饰器实现
  • 简单工厂模式
  • 抽象工厂模式
单例模式(singleton)



  • 所谓单例模式,也就是说不管什么时候我们要确保只有一个对象实例存在。
  • 很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线程池。
  • 这些场景下,就非常适合使用单例模式。总结起来,就是说不管我们初始化一个对象多少次,真正干活的对象只会生成一次并且在首次生成。
Singleton._instance
  1. # -*- coding: utf-8 -*-
  2. class Singleton(object):
  3.     """
  4.     单例模式
  5.     """
  6.     class _A(object):
  7.         """
  8.        真正干活的类, 对外隐藏
  9.         """
  10.         def __init__(self):
  11.             pass
  12.         def display(self):
  13.             """ 返回当前实例的 ID,是全局唯一的"""
  14.             return id(self)
  15.     # 类变量,用于存储 _A 的实例
  16.     _instance = None
  17.     def __init__(self):
  18.         """ 先判断类变量中是否已经保存了 _A 的实例,如果没有则创建一个后返回"""
  19.         if Singleton._instance is None:
  20.             Singleton._instance = Singleton._A()
  21.     def __getattr__(self, attr):
  22.         """ 所有的属性都应该直接从 Singleton._instance 获取"""
  23.         return getattr(self._instance, attr)
  24. if __name__ == '__main__':
  25.     # 创建两个实例
  26.     s1 = Singleton()
  27.     s2 = Singleton()
  28.     print(id(s1), s1.display())
  29.     print(id(s2), s2.display())
复制代码

  • 使用类变量 Singleton._instance 来存储创建的实例,并且保证只会创建一次实例。
  • 由于 Python 是一门动态语言,我们可以在运行时改变类定义。
  • 在首次初始化Singleton时,我们将首次生成类_A的实例,并将其存储到 Singleton._instance 中,以后每次初始化 Singleton 时都从 Singleton._instance 获取真正干活的实例,这样我们就实现了单例模式。
装饰器
  1. # -*- coding: utf-8 -*-
  2. class Singleton:
  3.     """
  4.     单例类装饰器,可以用于想实现单例的任何类。注意,不能用于多线程环境。
  5.     """
  6.     def __init__(self, cls):
  7.         """ 需要的参数是一个类 """
  8.         self._cls = cls
  9.     def Instance(self):
  10.         """
  11.         返回真正的实例
  12.         """
  13.         try:
  14.             return self._instance
  15.         except AttributeError:
  16.             self._instance = self._cls()
  17.             return self._instance
  18.     def __call__(self):
  19.         raise TypeError('Singletons must be accessed through `Instance()`.')
  20.     def __instancecheck__(self, inst):
  21.         return isinstance(inst, self._decorated)
  22. # 装饰器
  23. @Singleton
  24. class A:
  25.     """一个需要单例模式的类"""
  26.     def __init__(self):
  27.         pass
  28.     def display(self):
  29.         return id(self)
  30. if __name__ == '__main__':
  31.     s1 = A.Instance()
  32.     s2 = A.Instance()
  33.     print(s1, s1.display())
  34.     print(s2, s2.display())
  35.     print(s1 is s2)
复制代码

  • 用装饰器实现了单例模式,任何想使用单例模式的类,只需要使用 Singleton 装饰器装饰一下就可以使用了。
  • 可以看到其核心工作原理其实和第一种实现方式是一致的,也是使用内置的属性 Singleton._instance 来存储实例的。通过使用装饰器的模式我们将代码解耦了,使用更加灵活
案例-单例模式-连接sqlite3数据库
  1. # -*- coding: utf-8 -*-
  2. import sqlite3
  3. from flask import current_app
  4. from flask import _app_ctx_stack as stack
  5. class SQLite3(object):
  6.     def __init__(self, app=None):
  7.         self.app = app
  8.         if app is not None:
  9.             self.init_app(app)
  10.     def init_app(self, app):
  11.         """
  12.         典型的 Flask 扩展的初始化方式
  13.         """
  14.         app.config.setdefault('SQLITE3_DATABASE', ':memory:')
  15.         app.teardown_appcontext(self.teardown)
  16.     def connect(self):
  17.         """
  18.         连接到 sqlite 数据库
  19.         """
  20.         return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
  21.     def teardown(self, exception):
  22.           """
  23.           关闭 sqlite 链接
  24.           """
  25.         ctx = stack.top
  26.         if hasattr(ctx, 'sqlite3_db'):
  27.             ctx.sqlite3_db.close()
  28.     @property
  29.     def connection(self):
  30.         """
  31.         单例模式在这里:使用 flask._app_ctx_stack 存放 sqlite 链接,
  32.         每次获取数据库链接时都通过 connection 获取
  33.         """
  34.         ctx = stack.top
  35.         if ctx is not None:
  36.             if not hasattr(ctx, 'sqlite3_db'):
  37.                 ctx.sqlite3_db = self.connect()
  38.             return ctx.sqlite3_db
复制代码
简单工厂模式 Simple factory

一个函数,传入需要创建的产品类型,然后返回相应的产品
  1. # -*- coding: utf-8 -*-
  2. import random
  3. class BasicCourse(object):
  4.     """
  5.     基础课程
  6.     """
  7.     def get_labs(self):
  8.         return "basic_course: labs"
  9.     def __str__(self):
  10.         return "BasciCourse"
  11. class ProjectCourse(object):
  12.     """
  13.     项目课
  14.     """
  15.     def get_labs(self):
  16.         return "project_course: labs"
  17.     def __str__(self):
  18.         return "ProjectCourse"
  19. class SimpleCourseFactory(object):
  20.     @staticmethod
  21.     def create_course(type):
  22.         """ 简单工厂,用于创建课程"""
  23.         if type == 'bc':
  24.             return BasicCourse()
  25.         elif type == 'pc':
  26.             return ProjectCourse()
  27. if __name__ == '__main__':
  28.     t = random.choice(['bc', 'pc'])
  29.     course = SimpleCourseFactory.create_course(t)
  30.     print(course.get_labs())
复制代码
工厂方法模式 factory method

上面的简单工厂模式中,我们遇到了问题:如果需要增加一种课程,那我们需要修改工厂代码。仔细想想,如果对工厂进行抽象化,让每个工厂只负责一种产品的生产,那这样当增加一种产品时,就不需要修改已有的工厂了,只需要新增加一个工厂就行了,这样就避免修改整个工厂了
  1. # -*- coding: utf-8 -*-
  2. import random
  3. import abc
  4. class BasicCourse(object):
  5.     """
  6.         基础课程
  7.         """
  8.     def get_labs(self):
  9.         return "basic_course: labs"
  10.     def __str__(self):
  11.         return "BasicCourse"
  12. class ProjectCourse(object):
  13.     """
  14.         项目课
  15.         """
  16.     def get_labs(self):
  17.         return "project_course: labs"
  18.     def __str__(self):
  19.         return "ProjectCourse"
  20. class Factory(metaclass=abc.ABCMeta):
  21.     """
  22.         抽象工厂类
  23.         """
  24.     @abc.abstractmethod
  25.     def create_course(self):
  26.         pass
  27. class BasicCourseFactory(Factory):
  28.     """
  29.         基础课程工厂类
  30.         """
  31.     def create_course(self):
  32.         return BasicCourse()
  33. class ProjectCourseFactory(Factory):
  34.     """
  35.         项目课程工厂类
  36.         """
  37.     def create_course(self):
  38.         return ProjectCourse()
  39. def get_factory():
  40.     """
  41.         随机获取一个工厂类
  42.         """
  43.     return random.choice([BasicCourseFactory, ProjectCourseFactory])()
  44. if __name__ == '__main__':
  45.     factory = get_factory()
  46.     course = factory.create_course()
  47.     print(course.get_labs())
复制代码
我们有两种课程:BasicCourse 和 ProjectCourse,分别对应基础课和项目课。接着,我们创建了一个抽象的工厂 Factory,该工厂有一抽象方法Factory.create_course用于创建课程,最后我们基于抽象工厂实现了生产基础课程的工厂BasicCourseFactory和生产项目课的工厂ProjectCourseFactory。这样当我们新增加一种课程时,就不需要修改已经存在的基础课工厂和项目课工厂了。这里需要说明下,我们通过 Python 的abc模块实现抽象类和抽象方法。
抽象工厂模式

在工厂方法模式中,我们会遇到一个问题,当产品非常多时,继续使用工厂方法模式会产生非常多的工厂类。
如果按照工厂方法模式的作法,我们需要创建 Linux 虚拟机工厂类和 Mac 虚拟机工厂类, 这样我们就会有一堆工厂类了。我们就不能创建出一个能同时创建课程和虚拟机的工厂吗?
  1. -*- coding: utf-8 -*-
  2. import random
  3. import abc
  4. # 两种类型的课程
  5. class BasicCourse(object):
  6.     """
  7.     基础课程
  8.     """
  9.     def get_labs(self):
  10.         return "basic_course: labs"
  11.     def __str__(self):
  12.         return "BasicCourse"
  13. class ProjectCourse(object):
  14.     """
  15.     项目课
  16.     """
  17.     def get_labs(self):
  18.         return "project_course: labs"
  19.     def __str__(self):
  20.         return "ProjectCourse"
  21. # 两种类型的虚拟机
  22. class LinuxVm(object):
  23.     """
  24.     Linux 虚拟机
  25.     """
  26.     def start(self):
  27.         return "Linux vm running"
  28. class MacVm(object):
  29.     """
  30.     Mac OSX 虚拟机
  31.     """
  32.     def start(self):
  33.         return "Mac OSX vm running"
  34. class Factory(metaclass=abc.ABCMeta):
  35.     """
  36.     抽象工厂类, 现在工厂类不仅能创建课程,还能创建虚拟机了
  37.     """
  38.     @abc.abstractmethod
  39.     def create_course(self):
  40.         pass
  41.     @abc.abstractmethod
  42.     def create_vm(self):
  43.         pass
  44. class BasicCourseLinuxFactory(Factory):
  45.     """
  46.     基础课程工厂类
  47.     """
  48.     def create_course(self):
  49.         return BasicCourse()
  50.     def create_vm(self):
  51.         return LinuxVm()
  52. class ProjectCourseMacFactory(Factory):
  53.     """
  54.     项目课程工厂类
  55.     """
  56.     def create_course(self):
  57.         return ProjectCourse()
  58.     def create_vm(self):
  59.         return MacVm()
  60. def get_factory():
  61.     """
  62.     随机获取一个工厂类
  63.     """
  64.     return random.choice([BasicCourseLinuxFactory, ProjectCourseMacFactory])()
  65. if __name__ == '__main__':
  66.     factory = get_factory()
  67.     course = factory.create_course()
  68.     vm = factory.create_vm()
  69.     print(course.get_labs())
  70.     print(vm.start())
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张春

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表