设计模式之单例模式

197 阅读8分钟

前言

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

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

实战

源码示例

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. 确保一个类只有一个实例,并且提供一个访问它的全局方法

设计思想 有一些人,你希望是唯一的,程序也一样,有一些类,你希望实例是唯一的。单例就是一个类只能有一个对象(实例),单例就是用来控制某些事物只允许有一个个体,比如在我们生活的世界中,有生命的星球只有一个——地球(至少到目前为止人类所发现的世界中是这样的)。

人如果脚踏两只船,你的生活将会翻船!程序中的部分关键类如果有多个实例,将容易使逻辑混乱,程序崩溃!

单例模式的模型抽象 代码框架 单例的实现方式有很多种,下面列出几种常见的方式。

重写 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 中 newinit 都是 public 的,所以我们需要通过重写 newinit 方法来改造对象的创建过来,从而实现单例模式。如果你要更详细地了解 Python 中 newinit 的原理和用法,请参见《深入理解 Python 中的 new 和 init》

自定义 metaclass 的方法

源码实例

class Singleton2(type):
    """单例实现方式二"""

    def __init__(cls, what, bases=Nonedict=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 的原理》

装饰器的方法

源码实例

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