最近在看《head first design mode》,提到经典的单例模式的Java实现:
public class SingleTon {
private static SingleTon uniqueInstance;
private SingleTon() {
}
public static SingleTon getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new SingleTon();
}
return uniqueInstance;
}
}
设计思路是:
- 声明一个静态变量
uniqueInstance
,用来存放将实例化的对象; - 将构造器声明为
private
,这样实例化只能在由类本身发起; - 提供一个静态工厂方法
getInstance
,所有外部类都只能通过这个方法进行实例化。在实例化时只要判断一下实例是否已经创建,如果没创建则实例化一个并赋值给uniqueInstance
, 最后放回uniqueInstance
即可。
注: 这个实现并未考虑多线程的问题,仍然存在多线程创建多个实例的可能性。解决方法有三种:1. 通过synchronize方法将工厂方法声明为同步操作;2. 声明静态变量时直接初始化;3. 利用volatile+synchronize关键字,使用双重锁检查机制解决。有兴趣的同学可以直接google具体实现和利弊点, 这里篇幅限制不展开了。
python中能否参考这个思路来实现呢?看如下代码:
class SingleTon(object):
_instance = None
@classmethod
def get_instance(cls, *args, **kwargs):
if not cls._instance:
cls._instance = SingleTon()
return cls._instance
if __name__ == '__main__':
instance_a = SingleTon.get_instance()
instance_b = SingleTon.get_instance()
instance_c = SingleTon()
instance_d = SingleTon()
print(instance_a is instance_b)
print(instance_c is instance_d)
这段代码的最终输出如下:
True
Flase
可以看到,使用类方法进行实例化时达到了目的;但是,由于 python中并没有访问权限控制机制(而是依赖良好的规范约束,全靠自觉),依然存在调用方直接进行实例化的可能性,从而破坏了单例的事实。
那么如何解决这个问题呢?
重写__new__
方法实现
在解决这个问题前需要了解一下python的两个方法: __new__
和__init__
:
__new__
:这是一个类方法,用于创建实例(只创建对象,不做初始化),并返回创建的实例对象。
__init__
:这是一个实例方法,用来做实例初始化的。该方法总是传入一个self
对象,而self
对象就是__new__
方法创建的。因此,__new__
方法总是先于__init__
被调用。
既然问题的关键就在于控制实例对象的创建,我们很自然的想到重写__new__
方法来达到目的, 具体实现如下:
class SingleTon(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
if __name__ == '__main__':
instance_a = SingleTon()
instance_b = SingleTon()
print(instance_a is instance_b)
得到输出:
True
元类实现
另外一种方法是利用元类(metaclass), 所谓元类就是用来创建类的类,也就是元类的实例是一个类,元类常见于ORM实现。我们知道函数其实也是一个对象,函数的调用实际上是调用了函数对象的__call__
方法。
既然类是元类的一个实例,那么元类的__call__
方法实际就是类实例化方法(会议一下obj=SomeClass()语法)。因此,我们还可以通过指定元类的__call__
方法来达到这个目的,如下:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class ConcreteClass(metaclass=Singleton):
pass
if __name__ == '__main__':
instance_a = ConcreteClass()
instance_b = ConcreteClass()
print(instance_a is instance_b)
得到结果依然是:
True
网友评论