设计模式及Python实现

前言

  • 本文是将书中Java代码意会之后转译成Python代码,由于语言的特性不同,难免有我可能注意不到的细节。本人对Python谈不上精通,还在一点一点前进
  • 本文是从个人飞书的知识仓库摘出来,作为公开笔记发表在牛客网上
  • 这是根据《研磨设计模式》而写的笔记
  • 本笔记还在持续更新。

第一章 基本概念

设计模式就是设计方面的模板,也即设计方面的方式或方法。

设计模式:是指在软件开发中,经过验证,用于解决在特定环境中,反复出现的,特定问题的解决方案。

第二章 简单工厂

Java的接口:是一种特殊的抽象类,跟一般的抽象类相比,接口里面的所有方法都是抽象方法,接口里面的所有属性都是常量。换句话,接口里面只有方法定义没有方法实现。接口,就是实现类的外观。外部调用和内部实现,被接口隔离开的。

使用接口的好处,接口不变,内部实现的变化就不会影响到外部的使用,使得系统变得更加灵活。更好的可扩展和可维护。这就是接口是系统可插拔的保证。

在开发中,优先选用接口;既要定义子类行为,又要为子类提供公共方法,就需要选择抽象类。

Java中非常注重层的划分和模块的划分,通常按照三部分划分程序,表现层,逻辑层和数据层。

设计模式及Python实现

一个层的内部的各个模块的交互,要通过接口。

简单工厂的定义:提供一个创建对象实例的功能,而无需关注其具体实现。被创建的实例可以是接口,可以是抽象类,也可以是具体的类。

from abc import abstractmethod, ABCMeta
class Api :
    @abstractmethod
    def operation(self,str) :
        pass

class ImplA(Api) :
    def operation(self,str):
        print("ImplA s == ",str)
        
class ImplB(Api) :
    def operation(self,str):
        print("ImplB s == ",str)
        
class Factory:
    @staticmethod
    def createApi(condition):
        Api = None
        if condition == 1 :
            Api = ImplA()
        elif condition == 2 :
            Api = ImplB
        return Api
    
if __name__ == '__main__' :
    api = Factory.createApi(1) 
    api.operation("this is a simple factory!")    

理解这个模式的重点,就是理解为什么要把对实例的实例化移到工厂类里面。

工厂就是用来创造东西的,在Java里面,通常用来创造接口,但是也可以创造抽象类,甚至是一个具体的类实例。

静态工厂:通常不用创建工厂实例,因为没有必要,可以把简单工厂做成一个工具类,直接使用静态方法就行。为了防止无端造出一个工厂实例,还可以把简单工厂的构造方法私有化。

简单工厂创建对象的范围:虽然理论上讲,简单工厂什么都能创建,但对于简单工厂创建的范围,通常不要太大。建议控制在一个独立的组件级别或者一个模块级别。

yaml配置格式:

yaml支持的数据格式包括,字典,数组和纯量。

对象表示成一组键值对

url: xxxx

log:

file_name : xx

back_count:x

也可以将所有的键值写成一个行内对象。

log: {file_name: test.log, backup_count: 5}

设计模式及Python实现

设计模式及Python实现

设计模式及Python实现

import yaml

yamlPath = "your_path"

to_impl_class = ""
# data = None
with open(yamlPath , 'rb') as f :
    data = yaml.safe_load_all(f)
    to_impl_class = next(data)['ImplClass']

from abc import abstractmethod, ABCMeta
class Api :
    @abstractmethod
    def operation(self,str) :
        pass

class ImplA(Api) :
    def operation(self,str):
        print("ImplA s == ",str)
        
class ImplB(Api) :
    def operation(self,str):
        print("ImplB s == ",str)
        
class Factory:
    @staticmethod
    def createApi():
        return eval(to_impl_class)()
    
if __name__ == '__main__' :
    api = Factory.createApi() 
    api.operation("this is a simple factory!")    

简单工厂有以下优缺点:

友好地实现了组件的封装,然后让组件外部真正面向接口编程

解耦,实现了客户端和具体实现类的解耦。客户端只是通过工厂获得它需要的接口对象。

缺点是,可能增加客户端的复杂度;

可能通过客户端的参数来选择具体的实现类,如此一来就必须让客户端理解各个参数表示的具体功能和函数,这样就暴露了内部实现。这种情况可以使用配置文件避免暴露。

不方便扩展子工厂,使用静态方法创建接口,就不能通过写简单的工厂类的子类来改变创建接口的方法的行为了。不过一般不需要为简单工厂创建子类的。

选择简单工厂的场景:

  • 想要完全隔离具体实现,让外部只能通过接口操作封装体,那么可以使用简单工厂。让客户端通过工厂获取相应接口,无需关心具体的实现。
  • 如果想要把对外创建对象的职责集中管理和控制,可以选用简单工厂。可以把对外创建对象的职责集中到一个简单的工厂来,实现集中管理和控制。

简单工厂,是用来选择实现的,可以选择任意接口实现,一个简单工厂可以有多个用于选择并创建对象的方法,多个方法创建的对象可以有关系也可以没关系。

抽象工厂模式用来选择产品簇的实现,一般有多个选择并创建对象的方法,但是这些方法创建的对象之间是有关系的,这些创建的对象通常是构建一个产品簇所需要的部件对象。所以某种意义上讲,抽象工厂退化成只有一个实现,不分层次,那就相当于简单工厂了。

第三章 外观模式

外观模式的定义:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  • 界面,主要指的是从一个组件外部来看这个组件,能够看到什么,这就是这个组件的界面,也就是所说的外观。比如,你中一个类的外部看这个类,那么这个类的public方法就是这个类的外观。因为你从类的外部看这个类,只能看到这些。
  • 接口,这里和Java的interface不一样。这里主要指的是外部和内部交互的这么一个通道,通常指的一些方法,比如是一些类的方法,或者是interface的方法。换句话说,这里说的接口,不等价于interface。

外观模式就是引入一个外观类,在这个类里面定义客户端想要的简单的方法,然后在这些方法的实现里面,由外观类再去分别调用内部的多个模块来实现功能,从而让客户端变得简单。客户端只需要和外观类交互就可以了

下面给出外观模式的结构示意图:

设计模式及Python实现

Facade,定义子系统的多个模块对外的高级接口,通常需要调用内部的多个模块,从而把客户的请求代理给适当的子系统对象。

模块,接受Facade对象的委派,真正实现功能,各个模块之间可能有交互。

这里面关系是单向的,换言之——Facade对象知道各个模块,但是各个模块不应该知道Facade对象。

from abc import abstractclassmethod
class ModuleA:
    @abstractclassmethod
    def testA(self):
        pass
class ModuleB:
    @abstractclassmethod
    def testB(self):
        pass
    
class ModuleC:
    @abstractclassmethod
    def testC(self):
        pass
    
class ImplA(ModuleA) :
    def testA(self):
        print("A模块操作testA方法")
        
class ImplB(ModuleB) :
    def testB(self):
        print("B模块操作testB方法")

class ImplC(ModuleC) :
    def testC(self):
        print("C模块操作testC方法")

class Facade :
    def test(self) :
        A_module_api = ImplA()
        A_module_api.testA() 
        B_module_api = ImplB()
        B_module_api.testB()
        C_module_api = ImplC()
        C_module_api.testC() 
        
if __name__ == '__main__' :
    Facade().test()

模式讲解

外观模式的目的,并不是给子系统添加新功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部可以更简单的使用子系统。

当然,我们也可以在外观定义一些子系统没有的功能,但是不建议这么做。外观应该是包装已有功能,主要负责组合已有功能实现客户需要,而不是实现新功能。

简单讲一下好处:Facade与各个模块交互的过程已经是内部实现了。这样一来,如果今后调用模块的算法发生了变化,只需要修改Facade的实现就行。

Facade的功能可以被多个客户端调用,也就是说Facade功能共享,也就是实现复用。同样的调用,在Facade中写就行,不需要反复写。对使用Facade的人员,大大省去了学习成本,只需要了解Facade内部即可,无需深入到子系统内部。简化开发。

设计模式及Python实现

可以把外观类的方法直接实现成静态方法,这样就可以不需要创建外观对象的实例直接调用。相当于把外观类看成了一个辅助工具类。

Facade可以实现成为一个接口。这样会增加系统的复杂度,因为这样会需要一个Facade的实现,还需要一个来获取Facade接口对象的工厂。

Facade实现成为接口附带的好处,就是可以有选择性地暴露接口的方法,尽量减少模块对子系统外提供的接口方法。

换句话说,一个模块的接口定义的方法可以分为两类,一类是给子系统外部使用的,一类是子系统内部的模块之间相互调用时使用的。有了Facade接口,那么用于子系统内部的接口功能不再暴露给子系统外部了。

下面是我个人实现的具有接口特点的外观模式,可能写的不是很好,因为Facade实现了那些抽象方法,不应该是Facade的本意。

from abc import abstractclassmethod
class ModuleA:
    # 对外暴露
    @abstractclassmethod
    def a1(self):
        pass
    @abstractclassmethod
    def a2(self):
        pass
    @abstractclassmethod
    def a3(self):
        pass
    
class ModuleB:
    @abstractclassmethod
    def b1(self):
        pass
    # 对外暴露
    @abstractclassmethod
    def b2(self):
        pass
    @abstractclassmethod
    def b3(self):
        pass
    
class ModuleC:
    @abstractclassmethod
    def c1(self):
        pass
    @abstractclassmethod
    def c2(self):
        pass
    # 对外暴露
    @abstractclassmethod
    def c3(self):
        pass

class FacadeApi(ModuleA,ModuleB,ModuleC) :
    @abstractclassmethod
    def a1(self):
        pass
    @abstractclassmethod
    def b2(self):
        pass
    @abstractclassmethod
    def c3(self):
        pass
    @abstractclassmethod
    def test(self) :
        pass

class FacadeImpl(FacadeApi) :
    def a1(self):
        print("this is A's func")
    # 对外暴露
    def b2(self):
        print("this is B's func")
    def c3(self):
        print("this is C's func")
    def test(self) :
        self.a1()
        self.b2()
        self.c3()
    
if __name__ == '__main__' :
    FacadeImpl().test()

再谈外观模式的优缺点:

  • 松散耦合,让子系统内部的模块能够更容易的扩展和维护。
  • 简单易用,客户端不再需要了解子系统的内部实现,再也不需要跟众多子系统内部的模块进行交互,只需要跟外观交互就可以了,相当于外观类为外部客户端使用子系统提供了一站式服务。
  • 更好划分访问层次,通过合理使用Facade,可以更好帮我们划分访问层次,有些是对系统外部使用的,有的是对系统内部使用的,需要把暴露给外部的功能集中在外观中,方便用户端使用也更好隐藏了内部的细节。

外观模式的本质,封装交互,简化调用。

很好体现了最少知识原则,不使用外观模式,那么客户端通常需要和子系统内部的多个模块交互,也就是说客户端会有很多朋友,客户端和这些模块都有依赖关系,任意一个模块的变动都可以引起客户端的变动。

使用外观模式后,客户端只需要和外观类交互,也就是说客户端只有外观类这一个朋友,不需要关心子系统内部的模块的变动情况。客户端只和外观类有依赖关系。

什么时候使用外观模式?

  • 如果你希望一个复杂的子系统提供一个简单接口时候,可以考虑使用外观模式。
  • 让客户程序和抽象类的实现部分松散耦合,可以使用外观模式。使用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性。
  • 如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入口,这样可以简化层间的调用,也可以松散层次之间的关系。

其他

外观模式和中介者模式非常类似。

中介者模式主要用来封装多个对象之间的交互,多用在系统内部的多个模块之间。

外观模式,封装的是单向交互,是从客户端访问系统的调用,没有系统中来访问客户端的调用。

中介者模式的目的是为了松散多个模块之间的耦合。

通常,一个子系统只需要一个外观模式,所以外观模式可以和单例模式组合使用,把Facade类实现成单例。

外观模式的外观类通常需要和内部的多个模块进行交互,每个模块都有自己的接口,所以在外观类的具体实现里面,需要获取这些接口,然后组合这些接口完成客户端的功能。怎么获取这些接口?可以和抽象工厂一起使用,外观类通过抽象工厂获取所需要的接口,抽象工厂也可以吧模块内部的实现对Facade进行屏蔽,也就是说Facade仅仅知道从工厂获取它需要的功能,Facade不知道模块内部细节。

第四章 适配器模式

适配器的角色有点像日常使用的那种转接线,来适配不同的接口。

适配器模式的定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

问题的根源在于接口不兼容,但是功能已经实现了。也就是说,只要想办法让两边的接口都匹配起来,就可以复用代码。按照这个思路,可以定义一个类来实现新版的接口,然后在内部实现的时候,转调用旧版已经实现的功能,这样就可以通过对象组合的方式,既复用了旧版代码也同时满足新版的要求。

设计模式及Python实现

from abc import abstractclassmethod
class TargetApi :
    @abstractclassmethod
    def request(self):
        pass
    
class TargetImpl(TargetApi) :
    def request(self):
        print("this is new class's func")

class AdapteeApi :
    @abstractclassmethod
    def specific_request(self) :
        pass
    
class AdapteeImpl(AdapteeApi) :
    def specific_request(self) :
        print("this is an adaptee, a specific request method is called")

# 适配器要实现target的接口
class Adapter(TargetApi):
    
    def __init__(self,adaptee) :
        if not isinstance(adaptee,AdapteeApi) :
            raise TypeError
        self.adaptee = adaptee
        
    def request(self):
        self.adaptee.specific_request() 
        
if __name__ == "__main__" :
    # 模拟客户端
    adapter = Adapter(AdapteeImpl())
    adapter.request()

小总结:Adapter类继承了新版的抽象类(需要实现新版的抽象类的抽象方法),实现的过程中其实是以旧版类的方法去实现的——这样一来,旧版的代码复用了,而且新版类和旧版类对外暴露的接口统一了(新版类当场实例化直接使用;旧版类的对象需要传入适配器,然后调用适配器中和新版类的同名方法)。

模式讲解

适配器模式的主要功能是进行转换匹配,目的是复用已有的功能,而不是来实现新的接口。

换句话说,不需要适配器模式来实现新的功能,主要负责把不兼容的接口转换成客户端期望的样子就行。

设计模式及Python实现

适配器模式的实现

  1. 适配器通常是一个类,一般会让适配器类实现Target接口,然后在适配器的具体实现调用Adaptee。(适配器是Target类型[对于静态语言来说],但是内部实现委托给Adaptee来完成)
  2. 智能适配器,也可以实现一些Adaptee没有实现,但是Target中定义的功能。这样的写代码过程,相当于利用Adaptee的功能实现更高级的功能,甚至是完全实现新加入的,和已有的都不相关的功能。
  3. 适配多个Adaptee。也就是说在实现Target功能的时候,需要调用多个模块的功能,适配了多个模块才能满足新接口的需求。
  4. 适配器实现的复杂程度,取决于Adaptee和Target的相似程度,相似度越高,那么适配器只需要简单调用一下Adaptee的接口就完事了,但如果相差太大,可能就需要对Adaptee的方法组合调用,才能实现Target定义的一个方法了。
  5. 缺省适配,为一个接口提供缺省实现。继承这个缺省适配对象,让子类可以有选择地覆盖实现需要的方法。

双向适配器

适配器可以实现双向适配,换言之,这个适配器可以当做Target和Adaptee来使用。

from abc import abstractclassmethod
class TargetApi :
    @abstractclassmethod
    def request(self):
        pass

class TargetImpl(TargetApi) :
    def request(self):
        print("this is new class's func")
        
class AdapteeApi :
    @abstractclassmethod
    def  specific_request(self) :
        pass

class AdapteeImpl(AdapteeApi):
    def specific_request(self) :
        print("this is an adaptee, a specific request method is called")

# 适配器要实现target的接口
class Adapter(TargetApi,AdapteeApi):
    
    def __init__(self,target,adaptee) :
        if not isinstance(target,TargetApi) :
            raise TypeError
        if not isinstance(adaptee,AdapteeApi) :
            raise TypeError
        self.adaptee = adaptee
        self.target = target
        
    def request(self):
        self.adaptee.specific_request() 
    def specific_request(self):
        self.target.request() 
        
if __name__ == "__main__" :
    # 模拟客户端
    adapter = Adapter(TargetImpl(),AdapteeImpl())
    adapter.request()
    adapter.specific_request()

如果使用单向适配器,那么被适配的对象不再兼容Adaptee的接口,因为适配器只是实现了Target的接口。这导致了不是所有Adaptee对象可以被使用的地方都能使用适配器。

但是双向适配器解决这个问题,因为它同时实现了两者的接口使得双向适配器可以在Target和Adaptee出现的地方使用。

对象适配器和类适配器

根据适配器的实现方式,把适配器分为两种,一种叫对象适配器,另一种叫类适配器。

对象适配器,依赖于对象组合。采用对象组合的方式就像前面展示的一样。

类适配器,采用多继承对一个接口与另一个接口进行适配。

下面是类适配器的结构:

设计模式及Python实现

下面给出的代码是我基于对类适配器的理解:

from abc import abstractclassmethod
class TargetApi :
    @abstractclassmethod
    def request(self):
        pass

class TargetImpl(TargetApi) :
    def request(self):
        print("this is new class's func")
        
class AdapteeApi :
    @abstractclassmethod
    def  specific_request(self) :
        pass

class AdapteeImpl(AdapteeApi):
    def specific_request(self) :
        print("this is an adaptee, a specific request method is called")

class Adapter(TargetImpl,AdapteeImpl):
    
    def request(self):
        self.specific_request() 

        
if __name__ == "__main__" :
    # 模拟客户端
    adapter = Adapter()
    adapter.request()
    adapter.specific_request()

(貌似类适配器天然不支持双向适配?)

权衡:

  • 类适配器使用对象继承的方式,是静态定义的方式;对象适配器使用对象组合的方式,是动态组合的方式
  • 类适配器,适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作。对于对象适配器,允许一个适配器和多个Adaptee及其子类一起工作。
  • 类适配器,可以重定义Adaptee 的部分行为,相当于子类覆盖父类的部分实现方法。对于对象适配器,重定义Adaptee的行为比较麻烦,你可以定义Aadaptee的子类实现重定义,然后让适配器组合子类。
  • 类适配器是通过继承实现的,所以不需要引入额外的对象,直接把类适配器实例化就行。但是对象适配器需要传入额外的实例。
  • 一般情况下,建议使用对象适配器。当然,具体问题还需要具体分析。

优缺点:

  • 更好的复用性:功能都ok,但就是接口名字不兼容,那就用适配器
  • 更好的可扩展性:实现自己开发的功能,通过适配器来调用自己的代码
  • 过多使用适配器会让系统非常凌乱,明明是A接口,但底层却是B的实现。如果没有必要不要乱用。

适配器的本质就是,转化匹配,复用功能。

如果你想用一个已经存在的类,但接口不符合你的要求,用适配器;如果你想创建一个可以复用的类,但是这个类的接口可能和一些不兼容的类一起工作,那就用适配器;如果你想使用一些已经存在的类,你可以直接适配他们的父类。

第五章 单例模式

从读取配置文件的使用场景出发,来思考单例模式的应用。你想,在客户端使用配置文件的时候,是通过建立一个相关的实例来获取配置文件的内容。如果在系统运行的时候,有很多地方需要用到配置文件的内容,也就是说很多地方都需要创建这样的对象实例。

直截了当的说,在一个系统运行期间,某个类只需要一个类实例就行了,那就使用单例模式。

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

对于Java来说,一个类能够创建很多个实例,问题根源在于类的构造方法是公开的,想要控制一个类只被创建一个实例,然后由这个类来提供外部可以访问这个类实例的方法,这就是单例模式实现方式。

对于Python来说,请使用魔术方法__init__和__new__来帮助实现单例模式。

在Java中,单例模式分为两种模式,一种是懒汉式,一种是饿汉式。

所谓饿汉式,在装载类的时候就创建对象。

所谓懒汉式,一直等到马上要使用对象实例的时候才会创建,因此在装载对象的时候不会创建对象实例。

class Singleton:
    instance = None
    def __new__(cls) :
        if cls.instance is None :
            cls.instance = super().__new__(cls)
        return cls.instance
    
    def __init__(self) :
        self.data = 666 # 单例模式,属性没啥意义,因为大家都是一样的,如果有需要自己去main动态绑定去
    
    def __str__(self) :
        return str(self.data) 
    # 所谓全局访问点
    @staticmethod
    def get_instance():
        return Singleton.__dict__['instance']
        
a = Singleton()
b = Singleton()
print(a == b)
print(Singleton.get_instance())

这里补充讲一下,dir()和__dict__的区别。

  • dir()是一个函数,返回的是list;
  • __dict__是一个字典,键为属性名,值为属性值;
  • dir()用来寻找一个对象的所有属性,包括__dict__中的属性,__dict__是dir()的子集;

并不是所有对象都拥有__dict__属性。许多内建类型就没有__dict__属性,如list,此时就需要用dir()来列出对象的所有属性。

classmethod是一种特殊的类型方法,第一个参数永远是类本身而不是实例,通常用于定义类级别的方法。

staticmethod是另一种特殊类型的方法,他不需要参数,并且与类本身和实例没有关系,用于定义不需要访问类和实例属性的函数。

下面是chatgpt给出的两种模式的代码,有意思的是,他并没有用上__new__

Lazy singleton:妥妥的可运行代码。

class LazySingleton:
    __instance = None
    def __init__(self):
        if LazySingleton.__instance != None:
            raise Exception("This class is a singleton!")
        else:
            LazySingleton.__instance = self
    @classmethod
    def getInstance(cls):
        if cls.__instance == None:
            cls.__instance = LazySingleton()
        return cls.__instance

Eager singleton

请注意,Python中没有饿汉式的单例模式的写法

chatgpt的解释如下:Python 没有饿汉式的单例模式写法是因为 Python 的语言特性和设计理念不需要这样的写法。在 Python 中,类的实例是可以随时创建和销毁的,因此不需要预先创建实例并维护单例的概念。

然而,如果你确实需要实现单例模式,可以使用元类或装饰器来实现。这些方法允许你在类定义时定义单例行为,并且可以在不改变类定义的情况下实现单例。

下面的例子仅供理解饿汉式单例模式使用。只是一个理解饿汉的示例代码,不是真正的Python饿汉式单例模式!

class EagerSingleton:
    __instance = EagerSingleton()  # 会发现没有定义,代码不通过。
    def __init__(self):
        if type(self).__instance != self:
            raise Exception("This class is a singleton!")
    @classmethod
    def getInstance(cls):
        return cls.__instance

但是不意味着Python就没有饿汉的理念,如果你想实现饿汉式单例模式的效果,可以使用模块。在 Python 中,模块只会加载一次,因此可以将模块作为单例模式的实现。

其他:

  • 你也可以使用缓存的思想来实现这个事情。
  • 单例模式的优缺点: 懒汉式,时间换空间饿汉式,空间换时间
  • 不加同步的懒汉式是线程不安全的。
  • 饿汉式是线程安全的。但是Python除了模块的思想,简单的coding无法实现这个事情。
  • 线程安全的懒汉式代码:双重检查加锁,既实现线程安全,又能让性能不受很大的影响。并不是每次进入全局访问点都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才会进入同步,这是第一层检查,进入同步块之后,会再次检查实例是否存在,如果不存在,在同步的情况下,创建一个实例,这是第二重检查。
import threading

class Singleton:
    _instance_lock = threading.Lock()
    _instance = None

    def __init__(self):
        if type(self)._instance != None :
            raise Exception("this is a Singleton")
        else :
            type(self)._instance = self

    @classmethod
    def instance(cls):
        if not cls._instance:
            with cls._instance_lock:
                if not cls._instance:
                    cls._instance = cls()
        return cls._instance

思考单例模式

单例模式的本质,控制实例数目。

我如果不是只控制拥有一个,我想系统运行的时候,有三个实例,那怎么办?你可以使用缓存的方法。也可以coding逻辑。

class LazySingleton:
    __instance = {} 
    __index = 0 
    __max_instance = 2
    @classmethod
    def getInstance(cls):
        if len(cls.__instance) <= cls.__max_instance :
            cls.__instance[cls.__index] = LazySingleton()
            submit =  cls.__instance[cls.__index]
            cls.__index += 1 
            if cls.__index >= cls.__max_instance :
                cls.__index = 0 
        return submit

for i in range(6) :
    x = LazySingleton.getInstance() 
    print(id(x)) 

相关模式:很多模式都可以使用单例模式,只要这些模式中的某个类,需要控制实例为一个的时候,就可以很自然地使用单例模式,比如抽象工厂方法中的具体工厂类就通常是一个单例。