zoukankan      html  css  js  c++  java
  • python实践设计模式(二)Builder,Singleton,Prototype

     目录

        python实践设计模式(一)概述和工厂模式 

        python实践设计模式(二)Builder,Singleton,Prototype

    在上次学习的基础上,本次继续把创建型模式的其他3种模式学习总结一下。

      4.Builder模式

      个人理解,如果说工厂模式旨在选择创建哪一类的实例,而Builder模式的重点是封装一个实例的复杂创建过程。它可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。也就是说,建造的步骤可以稳定不变,但是每一步的内部表象可以灵活变化。

    UML图如下:

    Builder:为创建Product对象的各个部件指定抽象接口,python中为父类。
    ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口,也就是返回产品类的方法。
    Director:构造一个使用Builer接口的对象,该对象中定义了建造对象的步骤顺序。
    Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的具体装配方法,包含定义组成部件的类,以及将这些部件装配成最终产品的接口。

    一个比较贴切的例子:

      要建一座房子,可是我不知道怎么盖,于是我需要找建筑队的工人他们会,还得找个设计师,他知道怎么设计,我还要确保建筑队的工人听设计师的领导,而设计师本身不干活,只下命令,这里砌一堵墙,这里砌一扇门,这样建筑队的工人开始建设,最后,我可以向建筑队的工人要房子了。在这个过程中,设计师是什么也没有,除了他在脑子里的设计和命令,所以要房子也是跟建筑队的工人要。在这个例子中Director是设计师,Builder代表建筑队工人会的建筑技能,ConcreteBuilder工人建筑技能的具体操作,Product就是我要盖的房子。

      下面代码的例子,建筑队的工人有砌墙,装窗户,装门的技能以及交房的安排,设计师决定了建设房屋的安排和步骤,现在我要通过2个建筑队的民工,建2所房子,实例代码如下:

     1 class Builder:
     2     def BuildWall(self):
     3         pass
     4     def BuildDoor(self):
     5         pass
     6     def BuildWindow(self):
     7         pass
     8     def GetRoom(self):
     9         pass
    10 class ConcreteBuilder1(Builder):
    11     def __init__(self):
    12         self.__Room=[]
    13     def BuildWall(self):
    14         self.__Room.append("Builder1 Build the wall. ")
    15     def BuildDoor(self):
    16         self.__Room.append("Builder1 Build the door. ")
    17     def BuildWindow(self):
    18         self.__Room.append("Builder1 Build the window. ")
    19     def GetRoom(self):
    20         return self.__Room
    21 class ConcreteBuilder2(Builder):
    22     def __init__(self):
    23         self.__Room=[]
    24     def BuildWall(self):
    25         self.__Room.append("Builder2 Build the wall. ")
    26     def BuildDoor(self):
    27         self.__Room.append("Builder2 Build the door. ")
    28     def BuildWindow(self):
    29         self.__Room.append("Builder2 Build the window. ")
    30     def GetRoom(self):
    31         return self.__Room
    32 class Director:
    33     def __init__(self,Builder):
    34         self.__build=Builder
    35     def order(self):
    36         self.__build.BuildWall()        
    37         self.__build.BuildWindow()
    38         self.__build.BuildDoor()
    39 if __name__ == "__main__":
    40     
    41     builder1=ConcreteBuilder1()
    42     director=Director(builder1)
    43     director.order()
    44     print builder1.GetRoom()
    45 
    46     builder2=ConcreteBuilder2()
    47     director=Director(builder2)
    48     director.order()
    49     print builder2.GetRoom()
    Builder

     注:因为python没有private类型的成员,不过我们可以用命名为__name的变量代替,例如上例中的__Room,为什么这样可以呢,我们用builder1=ConcreteBuilder1()
    print dir(builder1)

    打印语句看出如下图,__name实例化后变为__ConcreteBuilder1__name,就是避免外界对__name属性的修改,从而达到了封闭性。

     

       5. Singleton模式

       Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点,UML如下。

      单例模式虽然不复杂,我一直认为这个模式是最简单的,当我想用python实现的时候确犯难了,这篇文章也足足用了2星期才写出来,期间各种查资料(省略1000个字),下面就来说说实现方法。

      先说以前比较熟悉的像C#这样的语言,一般的实现方法是:

      1.有一个私有的无参构造函数,这可以防止其他类实例化它。
      2.单例类被定义为sealed,目的是单例类也不被继承,如果单例类允许继承那么每个子类都可以创建实例,这就违背了Singleton模式“唯一实例”的初衷,所以为了保险起见可以把该类定义成不允许派生,但没有要求一定要这样定义。
      3.一个静态的变量用来保存单实例的引用。
      4.一个公有的静态方法用来获取单实例的引用,如果实例为null 即创建一个。

      上面是我熟悉的Singleton模式的创建方法,但是对于python,既没有static类型,也没有私有方法和sealed修饰的类,如何实现呢?

    • 关于私有方法和属性,我前面已经提到可以用__name形式为名称定义方法名和属性名来解决
    • 利用isinstance()或issubclass()

        本人力推isinstance()和issubclass()2个方法,正是由于python提供这两个方法才能完成设计模式的开发。

        isinstance(object, classinfo)如果object是CLASSINFO的一个实例或是子类,或者如果CLASSINFO和object的类型是对象,或是该类型的对象的子类,返回true。

        issubclass(class, classinfo)如果class是CLASSINFO的一个子类返回true。

        下面是利用 isinstance实现的Singleton模式       

     1 class Singleton:
     2     __singleton = None
     3     @classmethod
     4     def getSingleton(cls):
     5         if not isinstance(cls.__singleton,cls):
     6             cls.__singleton = cls()
     7         return cls.__singleton
     8 
     9 class Test(Singleton):
    10     def test(self):
    11         print self.__class__,id(self)
    12 
    13 class Test1(Test):
    14     def test1(self):
    15         print self.__class__,id(self),'Test1'
    16 
    17 class Test2(Singleton):
    18     def test2(self):
    19         print self.__class__,id(self),'Test2'
    20 
    21 if __name__=='__main__':
    22     
    23 
    24     t1 = Test.getSingleton()
    25     t2 = Test.getSingleton()
    26     
    27     t1.test()
    28     t2.test()
    29     assert(isinstance(t1,Test))
    30     assert(isinstance(t2,Test))
    31     assert(id(t1)==id(t2))
    32 
    33     t1 = Test1.getSingleton()
    34     t2 = Test1.getSingleton()
    35 
    36     assert(isinstance(t1,Test1))
    37     assert(isinstance(t2,Test1))
    38     assert(id(t1)==id(t2))
    39     
    40     t1.test()
    41     t1.test1()
    42     t2.test()
    43     t2.test1()
    44 
    45     t1 = Test2.getSingleton()
    46     t2 = Test2.getSingleton()
    47 
    48     assert(isinstance(t1,Test2))
    49     assert(isinstance(t2,Test2))
    50     assert(id(t1)==id(t2))
    51 
    52     t1.test2()
    53     t2.test2()
    Singleton1

        上面代码的执行结果如下:

        

        从运行结果可以看出,我们可以控制同一个子类的生成同一个对象实例,但是如果Singleton类被继承(不论是子类之间还是,子类的子类)不能控制生成一个实例。这个问题后面再探讨。

    • 利用__new__

        提到__new__就不能不说__init__,先说说关于__new__和__init__的不同与用法:

        object.__new__(cls[, ...]):调用创建cls类的一个新的实例。是静态方法不用声明。返回一个新对象的实例

        object.__init__(self[, ...]):当实例创建的时候调用。没有返回值。

          __new__在__init__这个之前被调用:

          如果__new__返回一个cls的实例,那么新的实例的__init__方法就会被调用,且self是这个新的实例。如果是自定义重写__new__,没有调用__init__的话__init__就不起作用了。

          如果__new__不返回一个cls的实例,那么新的实例的__init__方法就不会被调用。

        示例代码如下:

     1 class Singleton(object):
     2     def __new__(cls):
     3         if not hasattr(cls, '_instance'):
     4             cls._instance = object.__new__(cls)
     5         return cls._instance
     6 
     7 #class Singleton(type):  
     8 #    def __init__(cls, name, bases, dict):  
     9 #        super(Singleton, cls).__init__(name, bases, dict)  
    10 #        cls._instance = None  
    11 #    def __call__(cls):  
    12 #        if cls._instance is None:  
    13 #            cls._instance = super(Singleton, cls).__call__()  
    14 #        return cls._instance
    15 
    16 
    17 class MyClass1(Singleton):
    18     a = 1
    19 
    20 #class MyClass1(object):
    21 #    __metaclass__ = Singleton
    22 
    23 one = MyClass1()
    24 two = MyClass1()
    25 
    26 two.a = 3
    27 print 'one.a=',one.a
    28 
    29 assert(isinstance(one,MyClass1))
    30 assert(isinstance(two,MyClass1))
    31 print one.__class__,id(one)
    32 print two.__class__,id(two)
    33 print one == two
    34 print one is two
    35 
    36 class MyClass2(Singleton):
    37     a = 2
    38 #class MyClass2(object):
    39 #    __metaclass__ = Singleton
    40 
    41 three = MyClass2()
    42 three.a=4
    43 print 'three.a=',three.a
    44 assert(isinstance(three,MyClass2))
    45 print three.__class__,id(three)
    Singleton2

        如上代码,我们重写了__new__方法,没有用到__init__,即使需要用到我们也需要显式的调用,否则__init__不会起作用,这段代码返回的结果与第一种方法类似如下,也没有解决多继承多对象的问题。

       

    • 利用元类__metaclass__ 

        利用元类编写单例其实原理和重写__new__是一样的,都是在对象创建的时候进行拦截。上面Singleton2中注释的代码就是利用__metaclass__,可以用注释部分的声明代替之前的,Singleton类在声明是继承了type,对于type她其实是Python在背后用来创建所有类的元类。

     class MyClass1(object):
          __metaclass__ = Singleton

    在声明MyClass1时用到了以上的方式,原理是这样的,MyClass1中有__metaclass__这个属性吗?如果有,Python会在内存中通过__metaclass__创建一个名字为MyClass1的类对象。如果Python没有找到__metaclass__,它会继续在object(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。这里当程序发现MyClass1中有__metaclass__,所以用Singleton类代替元类type创建这个类。

        其中还用到了__call__   

        object.__call__(self[, args...]):当把一个实例当作方法来调用的时候,形如instance(arg1,args2,...),那么实际上调用的就是 instance.__call__(arg1,arg2,...),实际上__call__模拟了()调用,作用在实例上,因此__init__作用完了,才调用__call__

         关于元类的具体解析请参考http://blog.jobbole.com/21351/

    • 利用pythonDecoratorLibrary——Singleton

      python提供了丰富的装饰者库,其中就有现成的Singleton,官方参考链接http://wiki.python.org/moin/PythonDecoratorLibrary#Singleton

      我改写了一个较简单的版本,如下:

     1 def singleton(cls):
     2     ''' Use class as singleton. '''
     3     def singleton_new():
     4         it =  cls.__dict__.get('__it__')
     5         if it is not None:
     6             return it
     7 
     8         cls.__it__=cls()
     9         return cls.__it__
    10 
    11     return singleton_new
    12 
    13 
    14 @singleton
    15 class Foo:
    16     a = 1
    17 
    18 one = Foo()
    19 two = Foo()
    20 two.a = 3
    21 print 'one.a=',one.a
    22 
    23 print one.__class__,id(one)
    24 print two.__class__,id(two)
    25 print one == two
    26 print one is two
    27 
    28 @singleton
    29 class Foo2:
    30     a = 1
    31 
    32 three = Foo2()
    33 three.a=4
    34 print 'three.a=',three.a
    35 print three.__class__,id(three)
    singleton3

      总结:利用上面多种方法实现后,能实现对于一个类只有一个对象,但是不能避免的事类有继承,有多个子类就可以生成多个子类的对象。其实在python中要实现单例模式并不需要借用类的概念(java和C#需要类是因为所有代码需要写在类中),而是可以借助模块来实现,python的模块本身就是唯一的单例的,其中属性和方法直接写为全局的变量和方法即可。

      6.Prototype模式  

      原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

      原型模式与工厂模式一样都生成一个对象,区别就是工厂模式是创建新的对象,而原型模式是克隆一个已经存在的对象,所以在对象初始化操作比较复杂的情况下,很实用,它能大大降低耗时,提高性能,因为“不用重新初始化对象,而是动态地获得对象运行时的状态”。

      先来看看,原型模式的UML

      

      图中各部分意思如下:

      客户(Client)角色:客户类提出创建对象的请求,让一个原型克隆自身从而创建一个新的对象。
      抽象原型(Prototype)角色:此角色给出所有的具体原型类所需的接口。
      具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象原型角色所要求的接口。

      对于python实现原型模式有现成的copy模块可用。   

      copy.copy(x)浅拷贝
      copy.deepcopy(x) 深拷贝

      浅拷贝和深拷贝之间的区别仅适用于复合对象(包含其他对象也就是子对象,如list类或实例对象):

      浅拷贝——构建一个新的对象然后插入到原来的引用上。只拷贝父对象,不会拷贝对象的内部的子对象。

      深拷贝——构造一个新的对象以递归的形式,然后插入复制到它原来的对象上。拷贝对象及其子对象

     1 import copy 
     2 
     3 class ICloneable: 
     4     def shallowClone(self): 
     5         return copy.copy(self) 
     6      
     7     def deepClone(self): 
     8         return copy.deepcopy(self) 
     9 
    10 
    11 class WorkExperience(ICloneable): 
    12     workData = "" 
    13     company = "" 
    14 
    15 class Resume(ICloneable): 
    16     name = "" 
    17     sex = "" 
    18     age = 0 
    19     work = None 
    20 
    21     def __init__(self, name): 
    22         self.name = name 
    23         self.work = WorkExperience()
    24 
    25     def setPersonInfo(self, sex, age): 
    26         self.sex = sex 
    27         self.age = age 
    28 
    29     def setWorkExperience(self, workData, company): 
    30         self.work.workData = workData 
    31         self.work.company = company
    32              
    33     def display(self): 
    34 
    35         print('%s, %s, %d' % (self.name,self.sex,self.age)) 
    36 
    37         print('%s, %s' % (self.work.workData, self.work.company)) 
    38 
    39  
    40 def client(): 
    41 
    42     a = Resume('Tom') 
    43     a.setPersonInfo('m',29) 
    44     a.setWorkExperience("1998-2000","ABC.COM")     
    45 
    46     b = a.shallowClone()
    47     b.setWorkExperience("2000-2006","QQ.COM")         
    48 
    49     c = a.deepClone()
    50     c.setWorkExperience("2006-2009","360.COM")     
    51      
    52     
    53     a.display()
    54     b.display()   
    55     c.display()     
    56     return 
    57 
    58 if __name__ == '__main__': 
    59     client();
    Prototype

    上面代码运行结果如下:

     从结果可以看出,当b是a的浅拷贝,那么b中的实例对象WorkExperience只会复制了a中的引用,当不论是a,b哪一个修改都会改变a和b的WorkExperience实例。

    c是a的深拷贝,创建了新的WorkExperience实例,所以c只会改变自己的WorkExperience

    到这里6中创建型的模式已经学习完,下面接着学习Structural Patterns。

    未完待续……

  • 相关阅读:
    Appium简介
    本章小结
    测试角色定位,岗位职责
    如何做好空降管理者
    如何把控产品质量
    Appium自动化测试教程-自学网-monkeyrunner API
    Appium自动化测试教程-自学网-monkeyrunner简介
    Appium自动化测试教程-自学网-monkey日志管理
    Appium自动化测试教程-自学网-monkey自定义脚本实践
    Appium自动化测试教程-自学网-monkey参数
  • 原文地址:https://www.cnblogs.com/wly923/p/3071637.html
Copyright © 2011-2022 走看看