04 单例模式:你是我生命的唯一 【故事剧情】 爱情是每一个都渴望的,Tony 也是一样!自从毕业后,Tony 就一直没再谈过恋爱,离上一次的初恋也已经过去两年。一个巧合的机会,Tony 终于遇上了自己的喜欢的人,她叫 Jenny,有一头长发,天生爱笑、声音甜美、性格温和……

作为一个程序员的 Tony,直男癌的症状也很明显:天生木讷、不善言辞。Tony 自然不敢正面表白,但他也有自己的方式,以一种传统书信的方式,展开了一场暗流涌动的追求……经历了一次次屡战屡败,屡败屡战的追求之后,Tony 和 Jenny 终于在一起了!

然而好景不太长,由于种种的原因,最后 Jenny 还是和 Tony 分开了……

人生就像一种旅行,蜿蜒曲折,一路向前!沿途你会看到许多的风景,也会经历很多的黑夜,但我们无法回头!有一些风景可能很短暂,而有一些风景我们希望能够伴随自己走完余生。Tony 经历过一次被爱,也经历过一次追爱;他希望下次能找到一个可陪伴自己走完余生的她,也是他的唯一!

img

用程序来模拟生活

相信每一个人都渴望有一个纯洁的爱情,希望找到唯一的她。不管你是单身狗一个,还是已经成双成对,肯定都希望你的伴侣是唯一的!程序如人生,程序也一样,有一些类你希望它只有一个实例。

我们用程序来模拟一个真爱。

源码示例: 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. 重写 newinit 方法

源码示例: 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 中的 newinit》。

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

装饰器的实质就是对传进来的参数进行补充,可以在原有的类不做任何代码变动的前提下增加额外的功能,使用装饰器可以装饰多个类。用装饰器的方式来实现单例模式,通用性非常好,在实际项目中用的非常多。

类图

上面的代码框架可用类图表示如下:

enter image description here

基于框架的实现

通过上面的方式三,我们知道,定义通用的装饰器方法之后再用它去修饰一个类,这个类就成了一个单例类,使用起来非常方便。最开始的示例代码我们假设它为 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)可以用单例来实现。

参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/%e7%99%bd%e8%af%9d%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%2028%20%e8%ae%b2%ef%bc%88%e5%ae%8c%ef%bc%89/04%20%e5%8d%95%e4%be%8b%e6%a8%a1%e5%bc%8f%ef%bc%9a%e4%bd%a0%e6%98%af%e6%88%91%e7%94%9f%e5%91%bd%e7%9a%84%e5%94%af%e4%b8%80.md