learn-tech/专栏/白话设计模式28讲(完)/04单例模式:你是我生命的唯一.md
2024-10-16 09:22:22 +08:00

295 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

因收到Google相关通知网站将会择期关闭。相关通知内容
04 单例模式:你是我生命的唯一
【故事剧情】
爱情是每一个都渴望的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
在上面的代码中,我们定义了一个 metaclassSingleton2来控制对象的实例化过程。在定义自己的类时我们通过 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可以用单例来实现。