python之单例模式和工厂模式

Pet 2019-06-25

单例模式

所谓单例模式,也就是说不管什么时候我们要确保只有一个对象实例存在。很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线程池。这些场景下,就非常适合使用单例模式。总结起来,就是说不管我们初始化一个对象多少次,真正干活的对象只会生成一次并且在首次生成。

我们可以使用单例模式来保证连接数据库只会发生一次。下面我们看看一个简单的 Flask Web 框架的 sqlite 扩展。

-- 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

在以上的代码中,我们每次使用数据库的时候通过 SQLite3.connection 获取数据库连接就可以了。SQLite3.connection保证了数据库连接只会发生一次,其原理和之前我们实现单例模式的方式相同,只不过这里存储实例的地方变成 flask._app_ctx_stack了。

可以看到单例模式的实现只需要找一个变量存放创建的实例,然后每次获取实例时,先检查变量中是否已保存实例,如果没有则创建一个实例并将其存放到变量中,以后都从这个变量中获取实例就可以了。单例模式中,只会创建一次实例。

工厂模式

“工厂”两字,一目了然。所谓工厂模式,也就是说我们可以通过工厂类创建产品。

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

现在我们有一个产品是课程,但是仅仅依靠课程还没办法提供完美的服务,因为在 实验楼 你可以边学课程边做实验呢。在哪里做实验呢?当然是在虚拟机里了。当然我们也有很多种虚拟机,比如 Linux 虚拟机和 Mac 虚拟机。

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

抽象工厂模式顺利的解决了工厂方法模式中遇到的问题,我们通过将产品的创建进行组合放入一个工厂类中,不但减少了工厂类的数量,还增加了生产产品体系的能力(比如课程和虚拟机组成了一个产品体系)实验楼。现在,工厂类不仅仅能创建课程,还能创建虚拟机,我们只需要一座工厂就能为 实验楼 用户服务啦 。

从简单工厂模式到抽象工厂模式,我们都是在用后一种模式解决前一种模式的缺陷,都是在最大程度降低代码的耦合性。在使用工厂模式家族时,不管使用哪一种工厂模式,只要能达到最大程度的解耦,都是不错的选择。

相关推荐