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

    添加时间:2013-5-28 点击量:

     目次


        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
    7class 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):
    12if cls._instance is None:
    13 # cls._instance = super(Singleton, cls).__call__()
    14return cls._instance
    15
    16
    17 class MyClass1(Singleton):
    18 a = 1
    19
    20class 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
    38class 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(m29
    44 a.setWorkExperience(1998-2000ABC.COM
    45
    46 b = a.shallowClone()
    47 b.setWorkExperience(2000-2006QQ.COM
    48
    49 c = a.deepClone()
    50 c.setWorkExperience(2006-2009360.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。


    未完待续……




    我们永远不要期待别人的拯救,只有自己才能升华自己。自己已准备好了多少容量,方能吸引对等的人与我们相遇,否则再美好的人出现、再动人的事情降临身边,我们也没有能量去理解与珍惜,终将擦肩而过。—— 姚谦《品味》
    分享到: