taoCMS是基于php+sqlite/mysql的国内最小(100Kb左右)的功能完善、开源免费的CMS管理系统

python实践设计模式

2013-06-17

学习python有一段时间,之前就是看看书,最后发现只有实践才能真正学到东西,只有在解决问题中才能真正掌握。之前大牛也有用python实现各种设计模式的,我自己参考之前在学习用C#实现设计模式的经历,也想把23种模式逐一实践,从网上查了一些资料,才明白python虽然是面向对象的,但是和C#,java语言不太一样。影响设计方法不一样有以下几点:

1.python没有接口Interface的类型。

2.由于《Design Patterns—Elements of Reusable Object-Oriented Software》一书采用的是C++来讲述设计模式,因此访问控制符(public、protected和private等关键字)和静态成员方法(类方法)等都可以直接使用,但是这些特性在Python中都无法用到,原因是Python采了与C++完全不同的对象模式。

3.python不是强类型,弱化了多态的概念,一个对象很少只是一个类的实例,而是可以在运行时动态改变,支持相同请求的不同对象针对同一请求所触发的操作可能完全不同。

4.python代码生存在modules中而不必须在class中。

5.python有一些特殊语法运用了设计模式(如decorator ),可以直接应用。

有了以上的不同,就注定python在运用设计模式上与其他面向对象语言的不同,下面是我尝试的实现。

《Design Patterns》一书把设计模式分为了3大类,创建型模式(creational pattern)、结构型模式(structural pattern)和行为型模式(behavioral patterns)。

一. 创建型模式(creational pattern)

对类的实例化过程进行了抽象,能够使软件模块做到与对象创建和组织的无关性。为了使体系结构更加清晰,一些软件在设计上要求当创建类的具体实例时,能够根据具体的语境来动态地决定怎样创建对象,创建哪些对象,以及怎样组织和表示这些对象,而创建型模式所要描述的就是该如何来解决这些问题。

创建型模式包括以下几种:

1、Simple Factory模式

专门定义一个类来负责创建其它类的实例,被创建的实例通常都具有共同的父类。

2、Factory Method模式

将对象的创建交由父类中定义的一个标准方法来完成,而不是其构造函数,究竟应该创建何种对象由具体的子类负责决定

3、Abstract Factory模式

提供一个共同的接口来创建相互关联的多个对象。

4、Singleton模式

保证系统只会产生该类的一个实例,同时还负责向外界提供访问该实例的标准方法。

5、Builder模式

将复杂对象的创建同它们的具体表现形式(representation)区别开来,这样可以根据需要得到具有不同表现形式的对象。

6、Prototype模式

利用一个能对自身进行复制的类,使得对象的动态创建变得更加容易。

1.Simple Factory模式

Simple Factory模式不是独立的设计模式,他是Factory Method模式的一种简单的、特殊的实现。他也被称为静态工厂模式,通常创建者的创建方法被设计为static方便调用,但是python没有static一说。所以可以把创建者也就是工厂设计为一个普通class或全局函数即可。如果是class还需要实例化才能调用工厂方法,而全局函数比较简单,比较接近静态工厂的简便特性。

Simple Factory模式包含以下角色部分,UML可参考下图:

1) 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。由一个具体类实现,通常该类的工厂方法是静态的。在python中工厂为一个普通class或全局函数。

2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。在python中抽象产品一般为父类。

3) 具体产品角色:工厂类所创建的对象就是此角色的实例。由一个具体类实现。

一个Simple Factory模式代码实例

Simple Factory
 class car:
     '''interface as Product'''
     def drive(self):
         pass
 
 class BMW(car):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the BMW as "+self.__name
 
 class Benz(car):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the Benz as "+self.__name
 
 class driver:
     '''Factory also called Creator'''
     def driverCar(self,name):
         if name=="BMW":
             return BMW("BMW")
         elif name=="Benz":
             return Benz("Benz")
         else:
             raise MyInputException(name)
 
 class MyInputException(Exception):  
     def __init__(self, name):  
         Exception.__init__(self)  
         self.name = name
  
 
 if __name__ == "__main__":
     print "please input "BMW" or "Benz" :"
     carname=raw_input()
     dier=driver()
     try:
         d=dier.driverCar(carname)
     except MyInputException,e:
         print "input worry name "+e.name
     else:
         d.drive()

用全局函数改写工厂类,其他部分省略,变化部分如下:

'''Factory also called Creator'''
 def driver(name):
     if name=="BMW":
         return BMW("BMW")
     elif name=="Benz":
         return Benz("Benz")
     else:
         raise MyInputException(name)
 
 if __name__ == "__main__":
     print "please input "BMW" or "Benz" :"
     carname=raw_input()
     try:
         d=driver(carname)
     except MyInputException,e:
         print "input worry name "+e.name
     else:
         d.drive()

2. Factory Method工厂模式

工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。对于python来说,就是工厂类被具体工厂继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。也就是工厂外面再封装一层。

1) 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。

2) 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

3) 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在python中抽象产品一般为父类。

4) 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。由一个具体类实现。

一个Factory Method代码实例

Factory Method
 class car:
     '''interface as Product'''
     def drive(self):
         pass
 
 class BMW(car):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the BMW as "+self.__name
 
 class Benz(car):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the Benz as "+self.__name
 
 class driver:
     '''Factory also called Creator'''
     def driverCar(self):
         return car()
 
 class BMWdriver(driver):
     '''Concrete Creator'''
     def driverCar(self):
         return BMW("BMW")
 
 class Benzdriver(driver):
     '''Concrete Creator'''
     def driverCar(self):
         return Benz("Benz")    
 
 if __name__ == "__main__":
     driver=BMWdriver()
     car=driver.driverCar()
     car.drive()
     driver=Benzdriver()
     car=driver.driverCar()
     car.drive()

3. Abstract Factory模式

抽象工厂是工厂模式的进一步延伸,产品的类变的更加复杂,也就说产品不只是一个接口或父类而是有多个接口和父类了,形成了一个产品族的概念。模式的角色与Factory Method基本一样,UML图如下:

抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。 不过使用抽象工厂是有条件的:

1.系统中有多个产品族,而系统一次只可能消费其中一族产品

2.同属于同一个产品族的产品在一起使用,这一约束必须在系统的设计中体现出来。

简单的实现代码如下:

Abstract Factory
 class Newcar:
     '''Abstract Product'''
     def drive(self):
         pass
 
 class NewBMW(Newcar):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the New BMW as "+self.__name
 
 class NewBenz(Newcar):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the New Benz as "+self.__name
 
 class Oldcar:
     '''Abstract Product'''
     def drive(self):
         pass
 
 class OldBMW(Oldcar):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the Old BMW as "+self.__name
 
 class OldBenz(Oldcar):
     '''Concrete Product'''
     def __init__(self,carname):
         self.__name=carname
     def drive(self):
         print "Drive the Old Benz as "+self.__name
 
 class driver:
     '''Abstract Factory also called Creator'''
     def driverNewCar(self):
         return Newcar()
     def driverOldCar(self):
         return Oldcar()
 
 class BMWdriver(driver):
     '''Concrete Factory or Creator'''
     def driverNewCar(self):
         return NewBMW("NewBMW")
     def driverOldCar(self):
         return OldBMW("OldBMW")
 
 class Benzdriver(driver):
     '''Concrete Factory or Creator'''
     def driverNewCar(self):
         return NewBenz("NewBenz")
     def driverOldCar(self):
         return OldBenz("OldBenz")    
 
 if __name__ == "__main__":
     driver=BMWdriver()
     car=driver.driverNewCar()
     car.drive()
     car=driver.driverOldCar()
     car.drive()
     driver=Benzdriver()
     car=driver.driverNewCar()
     car.drive()
     car=driver.driverOldCar()
     car.drive()

4.Builder模式

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

UML图如下:

1、Builder:为创建Product对象的各个部件指定抽象接口,python中为父类。

2、ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口,也就是返回产品类的方法。

3、Director:构造一个使用Builer接口的对象,该对象中定义了建造对象的步骤顺序。

4、Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的具体装配方法,包含定义组成部件的类,以及将这些部件装配成最终产品的接口。

一个比较贴切的例子:

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

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

class Builder:
     def BuildWall(self):
         pass
     def BuildDoor(self):
         pass
     def BuildWindow(self):
         pass
     def GetRoom(self):
         pass
 class ConcreteBuilder1(Builder):
     def __init__(self):
         self.__Room=[]
     def BuildWall(self):
         self.__Room.append("Builder1 Build the wall. ")
     def BuildDoor(self):
         self.__Room.append("Builder1 Build the door. ")
     def BuildWindow(self):
         self.__Room.append("Builder1 Build the window. ")
     def GetRoom(self):
         return self.__Room
 class ConcreteBuilder2(Builder):
     def __init__(self):
         self.__Room=[]
     def BuildWall(self):
         self.__Room.append("Builder2 Build the wall. ")
     def BuildDoor(self):
         self.__Room.append("Builder2 Build the door. ")
     def BuildWindow(self):
         self.__Room.append("Builder2 Build the window. ")
     def GetRoom(self):
         return self.__Room
 class Director:
     def __init__(self,Builder):
         self.__build=Builder
     def order(self):
         self.__build.BuildWall()        
         self.__build.BuildWindow()
         self.__build.BuildDoor()
 if __name__ == "__main__":
     
     builder1=ConcreteBuilder1()
     director=Director(builder1)
     director.order()
     print builder1.GetRoom()
 
     builder2=ConcreteBuilder2()
     director=Director(builder2)
     director.order()
     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修饰的类,如何实现呢?

1、关于私有方法和属性,我前面已经提到可以用__name形式为名称定义方法名和属性名来解决

2、利用isinstance()或issubclass()

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

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

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

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

class Singleton:
     __singleton = None
     @classmethod
     def getSingleton(cls):
         if not isinstance(cls.__singleton,cls):
             cls.__singleton = cls()
         return cls.__singleton
 
 class Test(Singleton):
     def test(self):
         print self.__class__,id(self)
 
 class Test1(Test):
     def test1(self):
         print self.__class__,id(self),'Test1'
 
 class Test2(Singleton):
     def test2(self):
         print self.__class__,id(self),'Test2'
 
 if __name__=='__main__':
     
 
     t1 = Test.getSingleton()
     t2 = Test.getSingleton()
     
     t1.test()
     t2.test()
     assert(isinstance(t1,Test))
     assert(isinstance(t2,Test))
     assert(id(t1)==id(t2))
 
     t1 = Test1.getSingleton()
     t2 = Test1.getSingleton()
 
     assert(isinstance(t1,Test1))
     assert(isinstance(t2,Test1))
     assert(id(t1)==id(t2))
     
     t1.test()
     t1.test1()
     t2.test()
     t2.test1()
 
     t1 = Test2.getSingleton()
     t2 = Test2.getSingleton()
 
     assert(isinstance(t1,Test2))
     assert(isinstance(t2,Test2))
     assert(id(t1)==id(t2))
 
     t1.test2()
     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__方法就不会被调用。

示例代码如下:

class Singleton(object):
     def __new__(cls):
         if not hasattr(cls, '_instance'):
             cls._instance = object.__new__(cls)
         return cls._instance
 
 #class Singleton(type):  
 #    def __init__(cls, name, bases, dict):  
 #        super(Singleton, cls).__init__(name, bases, dict)  
 #        cls._instance = None  
 #    def __call__(cls):  
 #        if cls._instance is None:  
 #            cls._instance = super(Singleton, cls).__call__()  
 #        return cls._instance
 
 class MyClass1(Singleton):
     a = 1
 
 #class MyClass1(object):
 #    __metaclass__ = Singleton
 
 one = MyClass1()
 two = MyClass1()
 
 two.a = 3
 print 'one.a=',one.a
 
 assert(isinstance(one,MyClass1))
 assert(isinstance(two,MyClass1))
 print one.__class__,id(one)
 print two.__class__,id(two)
 print one == two
 print one is two
 
 class MyClass2(Singleton):
     a = 2
 #class MyClass2(object):
 #    __metaclass__ = Singleton
 
 three = MyClass2()
 three.a=4
 print 'three.a=',three.a
 assert(isinstance(three,MyClass2))
 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

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

def singleton(cls):
     ''' Use class as singleton. '''
     def singleton_new():
         it =  cls.__dict__.get('__it__')
         if it is not None:
             return it
 
         cls.__it__=cls()
         return cls.__it__
 
     return singleton_new
 
 @singleton
 class Foo:
     a = 1
 
 one = Foo()
 two = Foo()
 two.a = 3
 print 'one.a=',one.a
 
 print one.__class__,id(one)
 print two.__class__,id(two)
 print one == two
 print one is two
 
 @singleton
 class Foo2:
     a = 1
 
 three = Foo2()
 three.a=4
 print 'three.a=',three.a
 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类或实例对象):

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

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

import copy 
 
 class ICloneable: 
     def shallowClone(self): 
         return copy.copy(self) 
      
     def deepClone(self): 
         return copy.deepcopy(self) 
 
 class WorkExperience(ICloneable): 
     workData = "" 
     company = "" 
 
 class Resume(ICloneable): 
     name = "" 
     sex = "" 
     age = 0 
     work = None 
 
     def __init__(self, name): 
         self.name = name 
         self.work = WorkExperience()
 
     def setPersonInfo(self, sex, age): 
         self.sex = sex 
         self.age = age 
 
     def setWorkExperience(self, workData, company): 
         self.work.workData = workData 
         self.work.company = company
              
     def display(self): 
 
         print('%s, %s, %d' % (self.name,self.sex,self.age)) 
 
         print('%s, %s' % (self.work.workData, self.work.company)) 
 
 def client(): 
 
     a = Resume('Tom') 
     a.setPersonInfo('m',29) 
     a.setWorkExperience("1998-2000","ABC.COM")     
 
     b = a.shallowClone()
     b.setWorkExperience("2000-2006","QQ.COM")         
 
     c = a.deepClone()
     c.setWorkExperience("2006-2009","360.COM")     
      
     a.display()
     b.display()   
     c.display()     
     return 
 
 if __name__ == '__main__': 
     client();

Prototype

上面代码运行结果如下:

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

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

类别:技术文章 | 阅读:198708 | 评论:0 | 标签:python 设计模式

想收藏或者和大家分享这篇好文章→

“python实践设计模式”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

taoCMS发布taoCMS 3.0.2(最后更新21年03月15日),请大家速速升级,欢迎大家试用和提出您宝贵的意见建议。

捐助与联系

☟请使用新浪微博联系我☟

☟在github上follow我☟

标签云