写给新手的设计模式教程——单例模式
零、写在前面
想写设计模式,是因为读完了《漫谈设计模式》,有颇多感触。
将自己对于设计模式的理解记录下来,一方面防止自己遗忘,另一方面也可以给新手们一些入门级的帮助。
如果你要我给你一个看下去的理由的话,那么我只说四个字——浅显易懂。
设计模式本来是一个很简单的东西,但是被专家们一总结一归纳,就变成了一套一套的理论。
我觉得对于新手来说,最重要的是搞懂“是什么”和“为什么”,至于更深层的东西,等你实践得多了自然就懂了。
文中示例所用语言为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开发的,这个问题框架会解决,这里不深入讨论。
下回我们学习工厂方法。
最后,请回答这两个问题:
单例模式能解决什么问题?
单例模式是怎么解决这个问题的?