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

Python136

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

知识点

  • 单例模式概念及一般实现
  • 单例模式的装饰器实现
  • 简单工厂模式
  • 抽象工厂模式
  • 所谓单例模式,也就是说不管什么时候我们要确保只有一个对象实例存在。
  • 很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线程池。
  • 这些场景下,就非常适合使用单例模式。总结起来,就是说不管我们初始化一个对象多少次,真正干活的对象只会生成一次并且在首次生成。
# -*- 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 来存储实例的。通过使用装饰器的模式我们将代码解耦了,使用更加灵活
# -*- 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

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

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

上面的简单工厂模式中,我们遇到了问题:如果需要增加一种课程,那我们需要修改工厂代码。仔细想想,如果对工厂进行抽象化,让每个工厂只负责一种产品的生产,那这样当增加一种产品时,就不需要修改已有的工厂了,只需要新增加一个工厂就行了,这样就避免修改整个工厂了

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

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



相关阅读

Title: Vue 插槽

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

插槽到底是个啥?5分钟搞定

Vue 插槽

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

插槽的基本使用

组件使用slot标签,显示组件标签的内容


    Title

    aaa

    Vue.component('child1', {
        template: `<div>
          首页
          <slot></slot>
        </div>`,

    })
 var vm = new Vue({
        el: '#box',

    })

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

具名插槽

通过在标签内使用slot属性指定值,组件内使用solt标签通过name接收slot属性值


    Title

        pppp
        bbbb

    Vue.component('navbar', {
        template: `<div>
        <slot name="a"></slot>
          navbar
          <slot name="b"></slot>
        </div>`,

    })
var vm = new Vue({
        el: '#box',
        data:{

        }

    })

总结


# 1 计算属性
        -computed--->把方法变成属性---》延缓计算
    -在页面中直接使用函数,页面只要刷新,函数就会重新运行,使用计算属性--》只有当前函数使用的变量发生变化时,才重新运算

# 2 监听属性
        -watch:属性----》只要这个变量发生变化,就会执行方法

# 3 组件化开发
    -局部组件:写在组件内部:Vue.components
  -全局组件:Vue.component()
  -组件有自己的html,css,js,相互不影响
  -template 一定要放在一个标签中
  -data必须是函数data(){retrun {} }
  -各级组件的data数据是不共享的

# 4 组件间通信
    -通过自定义属性:父传子---》自定义的属性写在自定义的组件上---》props:['自定义属性名']
  -通过自定义事件:子传父---》子中调用this.$emit('自定义事件名',参数,参数)--》触发写在定义组件上的 @自定义事件名='函数'---》函数执行(父组件)
  -ref属性:
    放在普通标签---》通过this.$refs.属性值---》原生dom
    放在组件上---》通过this.$refs.属性值---》当前组价对象---》拿到组件中的值,执行组件中的方法

# 5 动态组件
        -多个组件切换,通过动态组件的is来决定显示哪个组件
    -keep-alive:组件切换的时候不销毁

# 6 插槽
        -写内容---》如果定义了插槽---》替换到插槽中
    -具名插槽---》给插槽名名字---》使用的时候,指定替换哪个插槽的内容

Original: https://www.cnblogs.com/48xz/p/16156656.html
Author: HammerZe
Title: Vue 插槽