单例模式实例一
作为一个实际的用例,我们将通过一个数据库应用程序来展示单例的应用。这里不妨以需要对数据库进行多种读取和写入操作的云服务为例进行讲解。完整的云服务被分解为多个服务,每个服务执行不同的数据库操作。
很明显,跨不同的服务的共享资源是数据库本身。因此,如果我们需要要更好地设计云服务,必须注意以下几点。
① 数据库中操作的一致性,即一个操作不应该与其他操作发生冲突
② 优化数据库的各种操作,以提高内存和CPU的利用率
示例:
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.cursor_obj = self.connection.cursor()
return self.cursor_obj
db1 = Database().connect()
db2 = Database().connect()
print("Database Objects DB1", db1)
print("Database Objects DB2", db2)
通过上面的代码,我们会发现一下几点:
1、我们以MetaSingleton为名创建了一个元类,就像之前说的那样,Python的特殊方法__call__可以通过元类创建单例
2、数据库类由MetaSingleton类装饰后,其行为就会表现为单例。因此,当数据库被实例化时,它只创建一个对象
3.当Web应用程序对数据来执行某些操作时,它会多次实例化数据库类,但只创建一个对象。因为只有一个对象,所以数据库调用是同步的。此外,这样还能够节约系统资源,并且可以避免消耗过多的内存或CPU资源
假如我们要开发的不是单个web应用程序,而是集成化的形式,即多个web应用共享单个数据库。单例在这种情况下不太好使,因为每增加一个web程序,就要新建一个单例,添加一个新的对象来查询数据库。这导致数据库操作无法同步,并且要耗费大量的资源。在这种情况下,数据库连接池比实现单例要好的多。
单例模式实例二
当我们为基础设施提供运行状况监控服务(就像Nagios监控工具一样)。我们创建了HealthCheck类,它作为单例实现。我们还要维护一个被监控的服务器列表。但一个服务器从这个列表中删除时,监控软件应该察觉到这一情况,并从被监控的服务器列表中将其删除。
class HealthCheck(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not HealthCheck._instance:
HealthCheck._instance = super(HealthCheck, cls).__new__(cls, *args, **kwargs)
return HealthCheck._instance
def __init__(self):
self.servers = []
def add_server(self):
self.servers.append("Server 1")
self.servers.append("Server 2")
self.servers.append("Server 3")
self.servers.append("Server 4")
def change_server(self):
self.servers.pop()
self.servers.append("Server 5")
hc1 = HealthCheck()
hc2 = HealthCheck()
print(hc1, hc2)
hc1.add_server()
print("Schedule health check for servers (1)..")
for i in range(4):
print("Checking ", hc1.servers[i])
hc2.change_server()
print("Schedule health check for servers (2)..")
for i in range(4):
print("Checking ", hc2.servers[i])
在上面的代码中,hc1和hc2对象与单例中的类相同。
我们可以使用add_server()方法将服务器添加到基础设施中,以进行状况检查。首先,通过迭代对这些服务器的运行状况进行检查。之后,changeServer()方法会删除最后一个服务器,并计划进行运行状况检查的基础设施中添加一个新的服务器。因此,当运行状况检查进行第二次迭代时,他会使用修改后的服务器列表。
所有的这一切都可以借助单例模式来完成。当添加或删除服务器时,运行状况的检查工作必须由了解基础设施变动情况的同一个对象来完成。
单例模式的缺点
由于单例具有全局访问权限,因此可能会出现以下问题:
1、全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置被使用
2、可能会对同一对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用
3、所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为u全局数据,从而可能无意中影响另一个类
总结:
1、在许多实际应用程序中,我们只需要创建一个对象,如线程池、缓存、对话框、注册表设置等。如果我们为每个应用程序创建多个实例,则会导致资源过度使用。单例模式在这种情况下工作的很好
2、单例是一种经过时间考验的成熟方法,能搞在不带来太多缺陷的情况下提供全局访问点
3、当使用全局变量或类的实例化非常耗费资源但最终却没有用到它们的情况下,单例的影响可以忽略不计