设计模式:单例模式
一、理解单例设计模式
1、什么是单例模式
- 确保类有且只有一个对象被创建
- 为对象提供一个访问点,以使程序可以全局访问该对象
- 控制共享资源的并行访问
单例模式的uml图
实现单例模式的一个简单方法是,使构造函数私有化,并且创建一个静态方法来完成对象的初始化,这样,对象将在一次调用时创建,此后,这个类将返回同一个对象
在使用python的时候,我们的实现方式要有所变通,因为它无法创建私有的构造函数
在许多实际应用中,我们只需要创建一个对象,如线程池、缓存、对话框、注册表设置等,如果我们为每个应用程序创建多个实例,则会导致资源的的国度使用,单例模式在这种请路况下工作的很好
单例你是时一种经过时间考验的成熟方法,能够在不带来太多缺陷的情况下提供全局访问
2、应用场景
1、日志记录
使用一个日志类的对象,将多项服务的日志信息按照顺序转存到一个特定的日志文件中
2、数据库操作
我们可能希望使用一个数据库对象对数据库进行操作,以维护数据的一致性;
3、打印机后台处理程序
该程序运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。
二、单例模式的种类
1、懒汉式实例化
在导入模块的时候,我们可能会无意中创建一个对象。但当时根本用不到它。懒汉式实例化能够确保在实际需要时才创建对象,
所以懒汉式实例化时一种节约资源并仅在需要时才创建它的方式
2、模块级别的单例模式
默认情况下,所有的模块都是单例,这是由Python的导入行为所决定的
Python通过下面的方式来工作
1、检查一个Python模块是否已经导入
2、如果已经导入,则返回该模块的对象。如果还没有导入,则导入该模块,并实例化
3、因此、当模块被导入的时候,它就会被初始化,然而,当同一个模块被再次导入的时候,它不会再次初始化,因为单例模式只能有一个对象,所以。它返回同一个对象
3、单例和元类
三、生产案例
1、生产用例
作为一个实际的用例,我们将通过一个数据库应用程序来展示单例的应用。这里我不妨以需求对数据库进行多种读取和写入操作的云服务器为例进行讲解。
完整的云服务被分解多个服务,每个服务执行不同的数据库操作,针对UI(web应用程序)上的操作将导致用API,最终产生相应的DB操作,很明显,跨不同
服务的共享资源是数据库本身。因此,如果我们需要更好的设计云服务,必须注意一下几点
- 数据库中操作的一致性,即一个操作不应该与其它操作发生冲突
- 优化数据库的各种操作,以提高内存和cpu的利用
2、实现代码
import sqlite3 class MetaSingleton(type): _instances = {} def __call__(cls,*args,**kwargs): if cls not in cls._instances: cls._instances[cls] = super(MetaSingleton,cls).__call__(*args,**kwargs) return cls._instances[cls] class Database(metaclass=MetaSingleton): connection = None def connect(self): if self.connection is None: self.connection = sqlite3.connect("db.sqlite3") self.cursorobj = self.connection.cursor() return self.cursorobj db1 = Database().connect() db2 =Database().connect() print("Database Objects DB1",db1) print("Database Objects DB2",db2)
3、输出结果
"C:\Program Files\Python35\python.exe" F:/BaiduNetdiskDownload/designpattern(1)/designpattern/danli.py Database Objects DB1 <sqlite3.Cursor object at 0x0000000000BAC3B0> Database Objects DB2 <sqlite3.Cursor object at 0x0000000000BAC500> Process finished with exit code 0
4、代码说明
1、我们以MetaSingleton为名创建了一个元类,就像在上一节中解释的那样,Python的特殊方法__call__可以通过元类创建单例
2、数据库类由类装饰后,其行为就会表现为单例。因此,当数据库类被实例化时,它只创建一个对象
3、当Web应用程序对数据库执行某些操作时,它会多次实例化数据库类,但只创建一个对象。因为只有一个对象,所以对数据库的调用时同步的。
此外这样还能够节约系统资源,并且可以避免消耗过多的内存或cpu资源
假如我们要开发的不是单个web应用程序,而是集群化的情形,即多个web共享单个数据库,当然单例在这种情况下不太好使,因为每增加一个web应用程序,就要创建一个实例,添加一个新的对象来查询数据库,这导致数据库操作无法同步,并且要耗费大量的资源。在这种情况下连接池比单例要好的多
四、单例模式的优缺点
1、优点
- 对唯一实例的受控访问
- 单例相当全局变量,但防止了命令空间被污染
2、缺点
虽然单例模式在许多情况下效果很好,但这种模式仍然存在一些缺陷,由于单例具有全局访问权限,因此可能会出现以下问题
- 全局变量可能在某处已经被误改,但是开发人员仍然认为他们没有发生变化,而该变量还在应用程序的其他位置被使用
- 可能会对同一个对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用
- 所有依赖于全局变量的类都会由一个类的改变而紧密耦合为全局数据,从而可能在无意中影响一个类
- 当使用全局变量或类的实例化非常耗费资源,但最终却没有用到他们的情况下,单例的影响可以忽略不计