写给新手的设计模式教程——单例模式

时间:2021-07-24 16:38:31

零、写在前面

 

想写设计模式,是因为读完了《漫谈设计模式》,有颇多感触。

将自己对于设计模式的理解记录下来,一方面防止自己遗忘,另一方面也可以给新手们一些入门级的帮助。

如果你要我给你一个看下去的理由的话,那么我只说四个字——浅显易懂

设计模式本来是一个很简单的东西,但是被专家们一总结一归纳,就变成了一套一套的理论。

我觉得对于新手来说,最重要的是搞懂“是什么”和“为什么”,至于更深层的东西,等你实践得多了自然就懂了。

文中示例所用语言为python,简洁明了。

 

个人理解难免有些偏差,如果有错误欢迎大家指出,我会及时改正的。

也欢迎大家提出各种意见建议。

 

重复一遍——浅显易懂。至于到底多浅显易懂,请往下看吧。

 

要查看本系列所有文章,请点击页面右侧“随笔分类”中的“设计模式”。

 

一、单例模式

 

言归正传,先来看问题:

class sql_query():      # 用于进行数据库查询

    def get_db():       # 连接数据库
        ......
        db = create_db_connection()
        ......
        return db

    def run_query(sql):    # 执行查询命令
        db = get_db
        result = db.exec(sql)
        return result


test = sql_query()
print test.run_query("SELECT * FROM books;")
print test.run_query("SELECT * FROM authors;")

很简单的一个数据库查询例子,我们分析一下这个例子有什么问题。

运行了两条查询命令,每次运行都会先创建数据库连接然后执行命令。

也就是说如果我们运行N条命令,就需要创建N次数据库连接。

我们知道无论是网站还是软件,响应速度都是非常重要的。而创建数据库连接本身就是一个非常费时的操作,所以这样的代码会导致非常严重的性能问题。

 

解决方法就是采用单例模式,很多人应该都听过这个名词,单例单例,就是只有一个实例。

看代码:

class sql_query():      # 用于进行数据库查询

    def __init__(self):
        self.db = _get_db

    def _get_db():       # 连接数据库
        ......
        db = create_db_connection()
        ......
        return db

    def run_query(sql):    # 执行查询命令
        result = self.db.exec(sql)
        return result


test = sql_query()
print test.run_query("SELECT * FROM books;")
print text.run_query("SELECT * FROM authors;")

很简单,我们把数据库连接db保存为实例变量,这样只会在创建实例的时候初始化一次db,之后就可以直接使用了。

 

二、小小改进

 

上面的代码虽然已经可以用了,不过还可以进行一下改进:

class sql_query():      # 用于进行数据库查询

    def __init__(self):
        self.db = None

    def _get_db():       # 连接数据库
        ......
        db = create_db_connection()
        ......
        return db

    def run_query(sql):    # 执行查询命令
        if not self.db:
            self.db = _get_db()
        result = self.db.exec(sql)
        return result


test = sql_query()
print test.run_query("SELECT * FROM books;")
print text.run_query("SELECT * FROM authors;")

看出区别了吗?我们这次并没有在创建实例的时候初始化数据库连接,而是在第一次进行查询的时候创建。

这样做的好处就是,我们在真正需要运行查询的时候才创建连接,进一步提高了性能。

 

三、注意事项

 

有几点需要注意:

  • 生产环境使用单例的时候,记得最后要close数据库连接。
  • Python并没有真正意义上的“私有”元素,我个人觉得其实也没必要。提供统一接口已经可以保证封装了。至于防止外部修改,我觉得更应该从软件以外来规定,这不是程序员应该考虑的问题。
  • 单例的典型例子就是数据库连接,不过单例并不只能应用于数据库连接,希望大家好好把握单例的这种思想。
  • 原书中单例部分还涉及到了线程安全,不过我是搞web开发的,这个问题框架会解决,这里不深入讨论。

 

下回我们学习工厂方法。

 

最后,请回答这两个问题:

单例模式能解决什么问题?

单例模式是怎么解决这个问题的?