Python设计模式-创建型:单例模式和工厂模式家族
知识点:
- 单例模式概念及一般实现
- 单例模式的装饰器实现
- 简单工厂模式
- 抽象工厂模式
单例模式(singleton)
- 所谓单例模式,也就是说不管什么时候我们要确保只有一个对象实例存在。
- 很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线程池。
- 这些场景下,就非常适合使用单例模式。总结起来,就是说不管我们初始化一个对象多少次,真正干活的对象只会生成一次并且在首次生成。
Singleton._instance
- # -*- coding: utf-8 -*-
- class Singleton(object):
- """
- 单例模式
- """
- class _A(object):
- """
- 真正干活的类, 对外隐藏
- """
- def __init__(self):
- pass
- def display(self):
- """ 返回当前实例的 ID,是全局唯一的"""
- return id(self)
- # 类变量,用于存储 _A 的实例
- _instance = None
- def __init__(self):
- """ 先判断类变量中是否已经保存了 _A 的实例,如果没有则创建一个后返回"""
- if Singleton._instance is None:
- Singleton._instance = Singleton._A()
- def __getattr__(self, attr):
- """ 所有的属性都应该直接从 Singleton._instance 获取"""
- return getattr(self._instance, attr)
- if __name__ == '__main__':
- # 创建两个实例
- s1 = Singleton()
- s2 = Singleton()
- print(id(s1), s1.display())
- print(id(s2), s2.display())
复制代码
- 使用类变量 Singleton._instance 来存储创建的实例,并且保证只会创建一次实例。
- 由于 Python 是一门动态语言,我们可以在运行时改变类定义。
- 在首次初始化Singleton时,我们将首次生成类_A的实例,并将其存储到 Singleton._instance 中,以后每次初始化 Singleton 时都从 Singleton._instance 获取真正干活的实例,这样我们就实现了单例模式。
装饰器
- # -*- coding: utf-8 -*-
- class Singleton:
- """
- 单例类装饰器,可以用于想实现单例的任何类。注意,不能用于多线程环境。
- """
- def __init__(self, cls):
- """ 需要的参数是一个类 """
- self._cls = cls
- def Instance(self):
- """
- 返回真正的实例
- """
- try:
- return self._instance
- except AttributeError:
- self._instance = self._cls()
- return self._instance
- def __call__(self):
- raise TypeError('Singletons must be accessed through `Instance()`.')
- def __instancecheck__(self, inst):
- return isinstance(inst, self._decorated)
- # 装饰器
- @Singleton
- class A:
- """一个需要单例模式的类"""
- def __init__(self):
- pass
- def display(self):
- return id(self)
- if __name__ == '__main__':
- s1 = A.Instance()
- s2 = A.Instance()
- print(s1, s1.display())
- print(s2, s2.display())
- print(s1 is s2)
复制代码
- 用装饰器实现了单例模式,任何想使用单例模式的类,只需要使用 Singleton 装饰器装饰一下就可以使用了。
- 可以看到其核心工作原理其实和第一种实现方式是一致的,也是使用内置的属性 Singleton._instance 来存储实例的。通过使用装饰器的模式我们将代码解耦了,使用更加灵活
案例-单例模式-连接sqlite3数据库
- # -*- coding: utf-8 -*-
- import sqlite3
- from flask import current_app
- from flask import _app_ctx_stack as stack
- class SQLite3(object):
- def __init__(self, app=None):
- self.app = app
- if app is not None:
- self.init_app(app)
- def init_app(self, app):
- """
- 典型的 Flask 扩展的初始化方式
- """
- app.config.setdefault('SQLITE3_DATABASE', ':memory:')
- app.teardown_appcontext(self.teardown)
- def connect(self):
- """
- 连接到 sqlite 数据库
- """
- return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
- def teardown(self, exception):
- """
- 关闭 sqlite 链接
- """
- ctx = stack.top
- if hasattr(ctx, 'sqlite3_db'):
- ctx.sqlite3_db.close()
- @property
- def connection(self):
- """
- 单例模式在这里:使用 flask._app_ctx_stack 存放 sqlite 链接,
- 每次获取数据库链接时都通过 connection 获取
- """
- ctx = stack.top
- if ctx is not None:
- if not hasattr(ctx, 'sqlite3_db'):
- ctx.sqlite3_db = self.connect()
- return ctx.sqlite3_db
复制代码 简单工厂模式 Simple factory
一个函数,传入需要创建的产品类型,然后返回相应的产品- # -*- coding: utf-8 -*-
- import random
- class BasicCourse(object):
- """
- 基础课程
- """
- def get_labs(self):
- return "basic_course: labs"
- def __str__(self):
- return "BasciCourse"
- class ProjectCourse(object):
- """
- 项目课
- """
- def get_labs(self):
- return "project_course: labs"
- def __str__(self):
- return "ProjectCourse"
- class SimpleCourseFactory(object):
- @staticmethod
- def create_course(type):
- """ 简单工厂,用于创建课程"""
- if type == 'bc':
- return BasicCourse()
- elif type == 'pc':
- return ProjectCourse()
- if __name__ == '__main__':
- t = random.choice(['bc', 'pc'])
- course = SimpleCourseFactory.create_course(t)
- print(course.get_labs())
复制代码 工厂方法模式 factory method
上面的简单工厂模式中,我们遇到了问题:如果需要增加一种课程,那我们需要修改工厂代码。仔细想想,如果对工厂进行抽象化,让每个工厂只负责一种产品的生产,那这样当增加一种产品时,就不需要修改已有的工厂了,只需要新增加一个工厂就行了,这样就避免修改整个工厂了- # -*- coding: utf-8 -*-
- import random
- import abc
- class BasicCourse(object):
- """
- 基础课程
- """
- def get_labs(self):
- return "basic_course: labs"
- def __str__(self):
- return "BasicCourse"
- class ProjectCourse(object):
- """
- 项目课
- """
- def get_labs(self):
- return "project_course: labs"
- def __str__(self):
- return "ProjectCourse"
- class Factory(metaclass=abc.ABCMeta):
- """
- 抽象工厂类
- """
- @abc.abstractmethod
- def create_course(self):
- pass
- class BasicCourseFactory(Factory):
- """
- 基础课程工厂类
- """
- def create_course(self):
- return BasicCourse()
- class ProjectCourseFactory(Factory):
- """
- 项目课程工厂类
- """
- def create_course(self):
- return ProjectCourse()
- def get_factory():
- """
- 随机获取一个工厂类
- """
- return random.choice([BasicCourseFactory, ProjectCourseFactory])()
- if __name__ == '__main__':
- factory = get_factory()
- course = factory.create_course()
- print(course.get_labs())
复制代码 我们有两种课程:BasicCourse 和 ProjectCourse,分别对应基础课和项目课。接着,我们创建了一个抽象的工厂 Factory,该工厂有一抽象方法Factory.create_course用于创建课程,最后我们基于抽象工厂实现了生产基础课程的工厂BasicCourseFactory和生产项目课的工厂ProjectCourseFactory。这样当我们新增加一种课程时,就不需要修改已经存在的基础课工厂和项目课工厂了。这里需要说明下,我们通过 Python 的abc模块实现抽象类和抽象方法。
抽象工厂模式
在工厂方法模式中,我们会遇到一个问题,当产品非常多时,继续使用工厂方法模式会产生非常多的工厂类。
如果按照工厂方法模式的作法,我们需要创建 Linux 虚拟机工厂类和 Mac 虚拟机工厂类, 这样我们就会有一堆工厂类了。我们就不能创建出一个能同时创建课程和虚拟机的工厂吗?- -*- coding: utf-8 -*-
- import random
- import abc
- # 两种类型的课程
- class BasicCourse(object):
- """
- 基础课程
- """
- def get_labs(self):
- return "basic_course: labs"
- def __str__(self):
- return "BasicCourse"
- class ProjectCourse(object):
- """
- 项目课
- """
- def get_labs(self):
- return "project_course: labs"
- def __str__(self):
- return "ProjectCourse"
- # 两种类型的虚拟机
- class LinuxVm(object):
- """
- Linux 虚拟机
- """
- def start(self):
- return "Linux vm running"
- class MacVm(object):
- """
- Mac OSX 虚拟机
- """
- def start(self):
- return "Mac OSX vm running"
- class Factory(metaclass=abc.ABCMeta):
- """
- 抽象工厂类, 现在工厂类不仅能创建课程,还能创建虚拟机了
- """
- @abc.abstractmethod
- def create_course(self):
- pass
- @abc.abstractmethod
- def create_vm(self):
- pass
- class BasicCourseLinuxFactory(Factory):
- """
- 基础课程工厂类
- """
- def create_course(self):
- return BasicCourse()
- def create_vm(self):
- return LinuxVm()
- class ProjectCourseMacFactory(Factory):
- """
- 项目课程工厂类
- """
- def create_course(self):
- return ProjectCourse()
- def create_vm(self):
- return MacVm()
- def get_factory():
- """
- 随机获取一个工厂类
- """
- return random.choice([BasicCourseLinuxFactory, ProjectCourseMacFactory])()
- if __name__ == '__main__':
- factory = get_factory()
- course = factory.create_course()
- vm = factory.create_vm()
- print(course.get_labs())
- print(vm.start())
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |