【Python设计模式】02 单例模式

1. python实现经典的单例模式

python通过覆盖__new__()方法来控制对象的创建。
if not hasattr(cls, ‘instance’):方法hasattr用于查看对象cls是否具有属性instance, 该属性的作用是检查该类是否已经生成了一个对象。

class singleton(object):
	def __new__(cls):
		if not hasattr(cls, 'instance'):
			cls.instance = super(singleton, cls).__new__(cls)
		return cls.instance

s = Singleton()
print("Object created", s)

s1 = Singleton()
print("Object created", s1)

运行结果:
Object created <main.Singleton object at 0x7eff5af722e8>
Object created <main.Singleton object at 0x7eff5af722e8>

2. 单例模式中的懒汉式实例化

懒汉式实例化能够确保在实际需要时才创建对象,有效节约资源。


class Singleton:
	__instance = None
	def __init__(self):
		if not Singleton.__instance:
			print("__init__ method called..")
		else:
			print("Instance already created:", \
				self.getInstance())

	@classmethod
	def getInstance(cls):
		if not cls.__instance:
			cls.__instance = Singleton()
		return cls.__instance


s1 = Singleton()
print("Object created,", Singleton.getInstance())

s2 = Singleton()
print("Object created,", Singleton.getInstance())

运行结果:
init method called…
init method called…
Object created, <main.Singleton object at 0x7eff5a74c978>
Instance already created: <main.Singleton object at 0x7eff5a74c978>
Object created, <main.Singleton object at 0x7eff5a74c978>

3. Monostate 单例模式

通常程序员需要的是让实例共享相同的状态
开发人员应该关注状态和行为, 而不是同一性
由于Monostate单例模式是基于所有对象共享相同状态,因此也被称为单态模式
与单例模式不同的地方: 创建两个实例b, b1, 会生成两个不同的对象,而单例模式只能生成一个对象
然而对象的状态,即b.__dict__和b1.__dict__却是相同的


class Borg:
	__shared_state = {"1": "2"}
	def __init__(self):
		self.x = 1
		self.__dict__ = self.__shared_state
		pass
	
b = Borg()
b1 = Borg()
b.x = 4

print("Borg Object 'b' : ", b)
print("Borg Object 'b1': ", b1)
print("Borg state  'b' : ", b.__dict__)
print("Borg state  'b1': ", b1.__dict__)

运行结果:
Borg Object ‘b’ : <main.Borg object at 0x7eff5a74c908>
Borg Object ‘b1’: <main.Borg object at 0x7eff5a74c400>
Borg state ‘b’ : {‘1’: ‘2’, ‘x’: 4}
Borg state ‘b1’: {‘1’: ‘2’, ‘x’: 4}

除此之外, 我们还可以通过修改__new__方法本身来实现 Borg 模式

class Borg(object):
	_shared_state = {"a":"as"}
	def __new__(cls, *args, **kwargs):
		obj = super(Borg, cls).__new__(cls, *args, **kwargs)
		obj.__dict__ = cls._shared_state()
		return obj

4. 单例和元类

类的定义由它的元类决定,所以当我们用类 A 创建一个类时,Python 通过A=Type(name, bases, dict)创建它

  • name : 这是类的名称
  • base : 这是基类
  • dict : 这是属性变量
# 假设一个类int 有预定义的元类MyInt
class MyInt(type):
	def __call__(cls, *args, **kwds):
		print("****** Here's My int ******", args)
		print("Now do whatever you want with these objects...")
		return type.__call__(cls, *args, **kwds)


class int(metaclass=MyInt):
	def __init__(self, x, y):
		self.x = x
		self.y = y


i = int(4, 5)

运行结果:
****** Here’s My int ****** (4, 5)
Now do whatever you want with these objects…
对于已经存在的类来说,当需要创建对象时,将调用Python的__call__()方法
在这段代码中, 当我们使用int(4, 5)实例化类int时,元类MyInt的__call__()方法
将被调用,这意味着元类控制着对象是的实例化

由于元类对类的创建和对象实例化有更多的控制权,所以它可以用于创建单例
注意:为了控制类的创建和初始化,元类将覆盖__new__和__init__方法
以下示例代码是基于元类的单例实现

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 Logger(metaclass=MetaSingleton):
	pass

logger1 = Logger()
logger2 = Logger()
print(logger1)
print(logger2)

运行结果:
<main.Logger object at 0x7eff5af9ec18>
<main.Logger object at 0x7eff5af9ec18>

5. 单例模式应用I

以需要对数据库进行多种读取和写入操作的云服务为例
完整的云服务被分解为多个服务,每个服务执行不同的数据库操作
针对UI(Web应用程序)上的操作将导致调用API, 最终产生相应的DB操作

很明显,跨不同服务的共享资源是数据库本身,因次,设计云服务需要注意:

  • 数据库中的操作的一致性,即一个操作不应与其他操作发生冲突
  • 优化数据库的各种操作,以提高内存和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.cursorobj = self.connection.cursor()
		return self.cursorobj

db1 = Database().connect()
db2 = Database().connect()

print("Database Objects DB1", db1)
print("Database Objects DB2", db2)

运行结果:
Database Objects DB1
Database Objects DB2

我们发现:

  • 元类MetaSingleton,可以通过python的方法__call__,创建单例
  • 数据库类由元类装饰后,其行为就会表现为单例,因此当数据库类被实例化时,它只创建一个对象
  • 因为只有一个对象,所以对数据库的调用是同步的,节约资源,避免消耗过多的内存和CPU

但是,加入我们要开发的不是单个Web应用程序,而是集群化,即多个Web应用共享一个数据库
这样单例在这种情况下就不好使了,因为每新增一个Web应用程序,就要新建一个单例,添加一个新的对象来查询数据库,这导致数据库无法同步,并且要消耗大量的资源
这种情况下,数据库连接池比实现单例要好得多

6. 单例模式应用II

为基础设施提供运行状况监控服务(就像Nagios一样)
我们创建HealthCheck类,它作为单例实现。我们还要维护一个被监控的服务器列表。
当一个服务器从这个列表中删除时,监控软件应该察觉到这一情况,并从被监控的服务器列表中将其删除

class HealthCheck:
	_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 addServer(self):
		self._servers.append("Server 1")
		self._servers.append("Server 2")
		self._servers.append("Server 3")
		self._servers.append("Server 4")
	def changeServer(self):
		self._servers.pop()
		self._servers.append("Server 5")

hc1 = HealthCheck()
hc2 = HealthCheck()

hc1.addServer()
print("Schedules health check for servers (1)..")
for i in range(4):
	print("Checking ", hc1._servers[i])

hc2.changeServer()
print("Schedules health check for servers (2)..")
for i in range(4):
	print("Checking ", hc2._servers[i])


运行结果:
Schedules health check for servers (1)…
Checking Server 1
Checking Server 2
Checking Server 3
Checking Server 4
Schedules health check for servers (2)…
Checking Server 1
Checking Server 2
Checking Server 3
Checking Server 5

changeServer()方法会删除最后一个服务器,并想计划进行运行状况检查的基础设施中添加一个新的服务器Server 5。因此,当运行状况检查进行第二次迭代时,它会使用修改后的服务器列表。

7. 单例模式的缺点

由于单例具有全局访问权限,因此可能会出现以下问题:

  • 全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序中的其他位置被使用
  • 可能会对同一个对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用
  • 所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为全局数据,从而可能在无意中影响另一个类

8. 单例模式小结

a. 在许多应用程序中,我们只需创建一个对象,如线程池、缓存、对话框、注册表设置等。如果为每个应用程序创建多个实例,则会导致资源过度使用。单例模式在这种情况下工作得很好。
b. 单例是一种经过时间考验的成熟方法,能够在不带来太多缺陷的情况下提供全局访问点。
c. 经典的单例模式:允许进行多次实例化,但最终只返回同一个对象
d. Borg或Monostate模式:这是单例模式的变体。Borg允许创建相同状态的多个对象,这与GoF描述的单例模式有所不同
e. Web应用:单例模式可以用于在多个服务间实现一致的数据操作
f. 最后,我们研究了单例可能出现的错误,以及开发人员需要避免的情况

你可能感兴趣的:(Python3,Design,Patterns)