4. Builder模式
個人理解,如果說工廠模式旨在選擇創建哪一類的實例,而 Builder 模式的重點是封裝一個實例的複雜創建過程。它可以將一個產品的內部表象與產品的生成過程分割開來,從而可以使一個建造過程生成具有不同的內部表象的產品對象。也就是說,建造的步驟可以穩定不變,但是每一步的內部表象可以靈活變化。UML 圖如下:
一個比較貼切的例子:
下面代碼的例子,建築隊的工人有砌牆,裝窗戶,裝門的技能以及交房的安排,設計師決定了建設房屋的安排和步驟,現在我要通過2個建築隊的民工,建2所房子,實例代碼如下:
- Builder_test.py
- #!/usr/bin/env python
- 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()
5. Singleton 模式
Singleton模式要求一個類有且僅有一個實例,並且提供了一個全局的訪問點,UML 如下:
單例模式雖然不復雜,我一直認為這個模式是最簡單的,當我想用python實現的時候確犯難了,這篇文章也足足用了2星期才寫出來,期間各種查資料(省略1000個字),下面就來說說實現方法。先說以前比較熟悉的像C#這樣的語言,一般的實現方法是:
上面是我熟悉的Singleton模式的創建方法,但是對於 python,既沒有 static 類型,也沒有私有方法和 sealed 修飾的類,如何實現呢?關於私有方法和屬性,我前面已經提到可以用 __name 形式為名稱定義方法名和屬性名來解決; 接著來幾種實作方式:
- 利用 isinstance() 或 issubclass()
isinstance(object, classinfo) 如果 object 是 classinfo 的一個實例或是子類,或者如果 classinfo 和 object 的類型是對象,或是該類型的對象的子類,返回 true; issubclass(class, classinfo) 如果class 是 classinfo 的一個子類返回 true。下面是利用 isinstance 實現的 Singleton 模式範例:
- Singleton_test.py
- #!/usr/bin/env python
- 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()
從運行結果可以看出,我們可以控制同一個子類的生成同一個對象實例,但是如果Singleton類被繼承(不論是子類之間還是,子類的子類)不能控制生成一個實例。這個問題後面再探討。
- 利用 __new__
提到 __new__ 就不能不說 __init__,先說說關於 __new__ 和 __init__ 的不同與用法:
示例代碼如下:
- #!/usr/bin/env python
- class Singleton( object ):
- def __new__(cls):
- print("Singleton __new__ being called")
- if not hasattr(cls, '_instance' ):
- cls._instance = object.__new__(cls)
- return cls._instance
- def __init__(self):
- print("Singletop __init__ being called")
- class MyClass1(Singleton):
- a = 1
- def __init__(self):
- print("MyClass1 __init__ being called")
- 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
- def __init__(self):
- print("MyClass2 __init__ being called")
- three = MyClass2()
- three.a = 4
- print 'three.a=' ,three.a
- assert(isinstance(three,MyClass2))
- print three.__class__,id(three)
- 利用元類 __metaclass__
利用元類編寫單例其實原理和重寫 __new__ 是一樣的,都是在對象創建的時候進行攔截。範例代碼如下:
- #!/usr/bin/env python
- class Singleton(type) :
- def __init__(cls, name, bases, dict):
- print("Singleton __init__ being called")
- super(Singleton, cls).__init__(name, bases, dict)
- cls._instance = None
- def __call__(cls):
- print("Singleton __call__ being called")
- if cls._instance is None:
- cls._instance = super(Singleton, cls).__call__()
- return cls._instance
- class MyClass1(object):
- __metaclass__ = Singleton
- a = 1
- def __init__(self):
- print("MyClass1 __init__ being called")
- 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(object):
- __metaclass__ = Singleton
- a = 2
- def __init__(self):
- print("MyClass2 __init__ being called")
- three = MyClass2()
- three.a = 4
- print 'three.a=' ,three.a
- assert(isinstance(three,MyClass2))
- print three.__class__,id(three)
- class MyClass1(object):
- __metaclass__ = Singleton
其中還用到了 __call__. object.__call__(self[, args...]) 當把一個實例當作方法來調用的時候,形如 instance(arg1,args2,...),那麼實際上調用的就是 instance.__call__(arg1 ,arg2,...),實際上 __call__ 模擬了 () 調用,作用在實例上,因此 __init__ 作用完了,才調用 __call__. 關於元類的具體解析請參考 這裡.
- 利用 python Decorator Library——Singleton
python 提供了豐富的裝飾者庫,其中就有現成的Singleton,官方參考鏈接參考 這裡. 這邊改寫了一個較簡單版本:
- #!/usr/bin/env python
- 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)
6. Prototype 模式
原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。原型模式與工廠模式一樣都生成一個對象,區別就是工廠模式是創建新的對象,而原型模式是克隆一個已經存在的對象,所以在對像初始化操作比較複雜的情況下,很實用,它能大大降低耗時,提高性能,因為“不用重新初始化對象,而是動態地獲得對象運行時的狀態”。
先來看看,原型模式的UML:
對於 python 實現原型模式有現成的 copy 模塊 可用。
淺拷貝和深拷貝之間的區別僅適用於復合對象(包含其他對像也就是子對象,如 list 類或實例對象):
下面為範例代碼:
- #!/usr/bin/env python
- import copy
- class ICloneable:
- def shallowClone(self, name):
- obj = copy.copy(self)
- print("Test of shallowcopy: %s/%s" % (id(self), id(obj)))
- obj.name = name
- return obj
- def deepClone(self, name):
- obj = Resume(name)
- obj.sex = self.sex
- obj.age = self.age
- obj.work.companys = []
- for company in self.work.companys:
- obj.work.companys.append(copy.deepcopy(company))
- print("Test of deepcopy: %s/%s" % (id(self), id(obj)))
- obj.name = name
- return obj
- class Company:
- workData = ""
- name = ""
- phone = ""
- def __init__(self, workData, name, phone):
- print("Company __init__ being called")
- self.name = name
- self.phone = phone
- self.workData = workData
- class WorkExperience:
- companys = []
- def __init__(self):
- print("WorkExperience __init__ being called")
- class Resume(ICloneable):
- name = ""
- sex = ""
- age = 0
- work = None
- def __init__(self, name):
- print("Resume __init__ being called")
- self.name = name
- self.work = WorkExperience()
- def setPersonInfo(self, sex, age):
- self.sex = sex
- self.age = age
- def setWorkExperience(self, workData, company_name, company_phone):
- company = Company(workData, company_name, company_phone)
- self.work.companys.append(company)
- def addCompany(self, name, phone):
- company = Company(name, phone)
- self.work.companys.append(company)
- def display(self):
- print('%s, %s, %d|%s' % (self.name,self.sex,self.age, id(self)))
- for c in self.work.companys:
- print("%s:" % (c.workData))
- print("\t%s (%s)|%s" % (c.name, c.phone, id(c)))
- print('')
- def client():
- a = Resume('Tom')
- a.setPersonInfo( 'm' , 29 )
- a.setWorkExperience( "1998-2000" , "ABC.COM", "09123" )
- b = a.shallowClone('Mary')
- b.setPersonInfo( 'f', 18)
- b.setWorkExperience("2000-2006" , "QQ.COM", "098" )
- c = a.deepClone('John')
- c.setPersonInfo('m', 36)
- c.setWorkExperience("2006-2009" , "360.COM", "765" )
- a.display()
- b.display()
- c.display()
- return
- if __name__ == '__main__' :
- client()
從結果可以看出,當 b 是 a 的淺拷貝,那麼 b 中的實例對象 WorkExperience 只會復制了 a 中的引用,當不論是 a,b 哪一個修改都會改變 a 和 b 的 WorkExperience 實例; c 是 a 的深拷貝,創建了新的 WorkExperience 實例,所以 c 只會改變自己的WorkExperience!
Supplement
* [OO 設計模式] Gossip@DesignPattern : Creational - Prototype Pattern (原型模式)
沒有留言:
張貼留言