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

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

知识点

  • 单例模式概念及一般实现
  • 单例模式的装饰器实现
  • 简单工厂模式
  • 抽象工厂模式
  • 所谓单例模式,即需要确保任何时候只有一个对象实例。
    [En]

    the so-called singleton mode, that is, we need to make sure that only one object instance exists at any time.*

  • 很多情况下,整个系统只有一个对象,所有的信息都是从这个对象获取的,比如系统配置对象或者线程池。
    [En]

    in many cases, there is only one object in the whole system, and all the information is obtained from this object, such as the system configuration object or thread pool.*

  • 在这些场景中,单身模式非常适合。总而言之,无论我们初始化一个对象多少次,实际的工作对象都只会第一次生成一次。
    [En]

    in these scenarios, singleton mode is very suitable. To sum up, that is, no matter how many times we initialize an object, the actual working object will only be generated once and for the first time.*

-*- 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)装饰器@Singletonclass 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 来存储实例的。通过使用装饰器的模式我们将代码解耦了,使用更加灵活
-*- coding: utf-8 -*-import sqlite3from flask import current_appfrom flask import _app_ctx_stack as stackclass 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

传入要创建的产品类型,然后返回相应产品的函数

[En]

A function that passes in the product type to be created, and then returns the corresponding product

-*- 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())

在上面的简单工厂模式中,我们遇到了一个问题:如果需要添加课程,则需要修改工厂代码。如果你想一想,如果你把工厂抽象出来,让每个工厂只负责一个产品的生产,那么当你添加一个产品时,你不需要修改现有的工厂,你只需要增加一个新的工厂,所以你不需要修改整个工厂。

[En]

In the simple factory pattern above, we encountered a problem: if we need to add a course, we need to modify the factory code. If you think about it, if you abstract the factory so that each factory is responsible for the production of only one product, then when you add a product, you don’t need to modify the existing factory, you just need to add a new factory, so you don’t need to modify the whole factory.

-*- 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模块实现抽象类和抽象方法。

在工厂方法模式中,我们遇到一个问题,当有很多产品时,继续使用工厂方法模式会产生非常大量的工厂类。

[En]

In the factory method pattern, we encounter a problem that when there are a lot of products, continuing to use the factory method pattern results in a very large number of factory classes.

如果按照工厂方法模式的作法,我们需要创建 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())

Original: https://www.cnblogs.com/oceaneyes-gzy/p/16462976.html
Author: OCEANEYES.GZY
Title: Python设计模式-创建型:单例模式和工厂模式家族

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/499105/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球