生活中的设计模式之单例模式:你是我生命的唯一
设计模式是面向对象的高度抽象和总结,而越抽象的东西越难以理解。
为了降低设计模式的阅读门槛,我将以生活中的小故事开始,用生动的方式由浅入深地讲述每一个模式。让你再次看到设计模式时不只是一个模式,还是生活中的一个个小确幸!
今天我们先来看看单例模式。
【故事剧情】
爱情是每一个都渴望的,Tony 也是一样!自从毕业后,Tony 就一直没再谈过恋爱,离上一次的初恋也已经过去两年。一个巧合的机会,Tony 终于遇上了自己的喜欢的人,她叫 Jenny,有一头长发,天生爱笑、声音甜美、性格温和……
作为一个程序员的 Tony,直男癌的症状也很明显:天生木讷、不善言辞。Tony 自然不敢正面表白,但他也有自己的方式,以一种传统书信的方式,展开了一场暗流涌动的追求……经历了一次次屡战屡败,屡败屡战的追求之后,Tony 和 Jenny 终于在一起了!
然而好景不太长,由于种种的原因,最后 Jenny 还是和 Tony 分开了……
人生就像一种旅行,蜿蜒曲折,一路向前!沿途你会看到许多的风景,也会经历很多的黑夜,但我们无法回头!有一些风景可能很短暂,而有一些风景我们希望能够伴随自己走完余生。Tony 经历过一次被爱,也经历过一次追爱;他希望下次能找到一个可陪伴自己走完余生的她,也是他的唯一!
用程序来模拟生活
相信每一个人都渴望有一个纯洁的爱情,希望找到唯一的她。不管你是单身狗一个,还是已经成双成对,肯定都希望你的伴侣是唯一的!程序如人生,程序也一样,有一些类你希望它只有一个实例。
我们用程序来模拟一个真爱。
源码示例:
class MyBeautifulGril(object):
"""我的漂亮女神"""
__instance = None
__isFirstInit = False
def __new__(cls, name):
if not cls.__instance:
MyBeautifulGril.__instance = super().__new__(cls)
return cls.__instance
def __init__(self, name):
if not self.__isFirstInit:
self.__name = name
print("遇见" + name + ",我一见钟情!")
MyBeautifulGril.__isFirstInit = True
else:
print("遇见" + name + ",我置若罔闻!")
def showMyHeart(self):
print(self.__name + "就我心中的唯一!")
测试代码:
def TestLove():
jenny = MyBeautifulGril("Jenny")
jenny.showMyHeart()
kimi = MyBeautifulGril("Kimi")
kimi.showMyHeart()
print("id(jenny):", id(jenny), " id(kimi):", id(kimi))
输出结果:
遇见Jenny,我一见钟情!
Jenny就我心中的唯一!
遇见Kimi,我置若罔闻!
Jenny就我心中的唯一!
id(jenny): 47127888 id(kimi): 47127888
看到了没,一旦你初次选定了 Jenny,不管换几个女人,你心中念叨的还是 Jenny!这才是真爱啊!哈哈……
从剧情中思考单例模式
单例模式
Ensure a class has only one instance, and provide a global point of access to it.
确保一个类只有一个实例,并且提供一个访问它的全局方法。
设计思想
有一些人,你希望是唯一的,程序也一样,有一些类,你希望实例是唯一的。单例就是一个类只能有一个对象(实例),单例就是用来控制某些事物只允许有一个个体,比如在我们生活的世界中,有生命的星球只有一个——地球(至少到目前为止人类所发现的世界中是这样的)。
人如果脚踏两只船,你的生活将会翻船!程序中的部分关键类如果有多个实例,将容易使逻辑混乱,程序崩溃!
单例模式的模型抽象
代码框架
单例的实现方式有很多种,下面列出几种常见的方式。
1. 重写 _new_ 和 _init_ 方法
源码示例:
class Singleton1(object):
"""单例实现方式一"""
__instance = None
__isFirstInit = False
def __new__(cls, name):
if not cls.__instance:
Singleton1.__instance = super().__new__(cls)
return cls.__instance
def __init__(self, name):
if not self.__isFirstInit:
self.__name = name
Singleton1.__isFirstInit = True
def getName(self):
return self.__name
# Test
tony = Singleton1("Tony")
karry = Singleton1("Karry")
print(tony.getName(), karry.getName())
print("id(tony):", id(tony), "id(karry):", id(karry))
print("tony == karry:", tony == karry)
输出结果:
Tony Tony
id(tony): 46050320 id(karry): 46050320
tony == karry: True
在 Python 3 的类中,_new_ 负责对象的创建,而 _init_ 负责对象的初始化;_new_是一个类方法,而 _init_ 是一个对象方法。
_new_ 是我们通过类名进行实例化对象时自动调用的,_init_ 是在每一次实例化对象之后调用的,_new_ 方法创建一个实例之后返回这个实例对象,并将其传递给 _init_ 方法的 self 参数。
在上面的示例代码中,我们定义了一个静态的 _instance_ 类变量,用来存放 Singleton1 的对象,_new_ 方法每次返回同一个_instance对象_(若未初始化,则进行初始化)。因为每一次通过 s = Singleton1() 的方式创建对象时,都会自动调用 _init_ 方法来初始化实例对象;因此 _isFirstInit_ 的作用就是确保只对 _instance_ 对象进行一次初始化,故事剧情中的代码就是用这种方式实现的单例。
在 Java 和 C++ 这种静态语言中,实现单例模式的一个最简单的方法就是:将构造函数声明成 private,再定义一个 getInstance() 的静态方法返回一个对象,并确保 getInstance() 每次返回同一个对象即可,如下面的 Java 示例代码。
/**
* Java中单例模式的实现,未考虑线程安全
*/
public class Singleton {
private static Singleton instance = null;
private String name;
private Singleton(String name) {
this.name = name;
}
public static Singleton getInstance(String name) {
if (instance == null) {
instance = new Singleton(name);
}
return instance;
}
}
Python 中 _new_ 和 _init_ 都是 public 的,所以我们需要通过重写 _new_ 和 _init_ 方法来改造对象的创建过来,从而实现单例模式。如果你要更详细地了解 Python 中 _new_ 和 _init_ 的原理和用法,请参见《深入理解 Python 中的 __new__
和 __init__
》。
2. 自定义 metaclass 的方法
class Singleton2(type):
"""单例实现方式二"""
def __init__(cls, what, bases=None, dict=None):
super().__init__(what, bases, dict)
cls._instance = None # 初始化全局变量cls._instance为None
def __call__(cls, *args, **kwargs):
# 控制对象的创建过程,如果cls._instance为None则创建,否则直接返回
if cls._instance is None:
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class CustomClass(metaclass=Singleton2):
"""用户自定义的类"""
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
tony = CustomClass("Tony")
karry = CustomClass("Karry")
print(tony.getName(), karry.getName())
print("id(tony):", id(tony), "id(karry):", id(karry))
print("tony == karry:", tony == karry)
输出结果:
Tony Tony
id(tony): 50794608 id(karry): 50794608
tony == karry: True
在上面的代码中,我们定义了一个 metaclass(Singleton2)来控制对象的实例化过程。在定义自己的类时,我们通过 class CustomClass(metaclass=Singleton2) 来显示地指定 metaclass 为 Singleton2。如果你还不太熟悉 metaclass,想了解更多关于它的原理,请参见《附录 Python 中 metaclass 的原理》。
3. 装饰器的方法
def singletonDecorator(cls, *args, **kwargs):
"""定义一个单例装饰器"""
instance = {}
def wrapperSingleton(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]
return wrapperSingleton
@singletonDecorator
class Singleton3:
"""使用单例装饰器修饰一个类"""
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
tony = Singleton3("Tony")
karry = Singleton3("Karry")
print(tony.getName(), karry.getName())
print("id(tony):", id(tony), "id(karry):", id(karry))
print("tony == karry:", tony == karry)
输出结果:
Tony Tony
id(tony): 46206704 id(karry): 46206704
tony == karry: True
装饰器的实质就是对传进来的参数进行补充,可以在原有的类不做任何代码变动的前提下增加额外的功能,使用装饰器可以装饰多个类。用装饰器的方式来实现单例模式,通用性非常好,在实际项目中用的非常多。
类图
上面的代码框架可用类图表示如下:
基于框架的实现
通过上面的方式三,我们知道,定义通用的装饰器方法之后再用它去修饰一个类,这个类就成了一个单例类,使用起来非常方便。最开始的示例代码我们假设它为 version 1.0,那么再看看基于装饰器的 version 2.0 吧。
@singletonDecorator
class MyBeautifulGril(object):
"""我的漂亮女神"""
def __init__(self, name):
self.__name = name
if self.__name == name:
print("遇见" + name + ",我一见钟情!")
else:
print("遇见" + name + ",我置若罔闻!")
def showMyHeart(self):
print(self.__name + "就我心中的唯一!")
输出结果:
遇见Jenny,我一见钟情!
Jenny就我心中的唯一!
Jenny就我心中的唯一!
id(jenny): 58920752 id(kimi): 58920752
应用场景
-
你希望这个类只有一个且只能有一个实例; -
项目中的一些全局管理类(Manager)可以用单例来实现。
即日起至 5 月 26 日,专栏《白话设计模式 28 讲》限时特惠 ¥29 ¥39。看故事学设计模式,一看就懂!