日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Python設(shè)計模式:工廠方法模式初探

軟件設(shè)計大師總是要比初學(xué)者更加清楚該如何設(shè)計軟件,因為他們手中掌握著設(shè)計模式這一法寶。作為一種高級的軟件復(fù)用形式,設(shè)計模式是眾多優(yōu)秀軟件設(shè)計師集體智慧的結(jié)晶,能夠很好地指導(dǎo)軟件設(shè)計過程。

成都創(chuàng)新互聯(lián)公司是一家專注于成都做網(wǎng)站、成都網(wǎng)站建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)與策劃設(shè)計,饒河網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:饒河等地區(qū)。饒河做網(wǎng)站價格咨詢:028-86922220

推薦閱讀:Python設(shè)計模式:用模式改變軟件設(shè)計

工廠方法(Factory Method)模式又稱為虛擬構(gòu)造器(Virtual Constructor)模式或者多態(tài)工廠(Polymorphic Factory)模式,屬于類的創(chuàng)建型模式。在工廠方法模式中,父類負(fù)責(zé)定義創(chuàng)建對象的公共接口,而子類則負(fù)責(zé)生成具體的對象,這樣做的目的是將類的實例化操作延遲到子類中完成,即由子類來決定究竟應(yīng)該實體化哪一個類。

在簡單工廠模式中,一個工廠類處于對產(chǎn)品類進行實例化的中心位置上,它知道每一個產(chǎn)品類的細(xì)節(jié),并決定何時哪一個產(chǎn)品類應(yīng)當(dāng)被實例化。簡單工廠模式的優(yōu)點是能夠使客戶端獨立于產(chǎn)品的創(chuàng)建過程,并且在系統(tǒng)中引入新產(chǎn)品時無需對客戶端進行修改,缺點是當(dāng)有新產(chǎn)品要加入到系統(tǒng)中時,必須對工廠類進行修改,以加入必要的處理邏輯。簡單工廠模式的致命弱點就是處于核心地位的工廠類,因為一旦它無法確定要對哪個類進行實例化時,就無法使用該模式,而工廠方法模式則可以很好地避免這一問題。

考慮這樣一個應(yīng)用程序框架(Framework),它可以用來瀏覽各種格式的文檔,如TXT、DOC、PDF、HTML等,設(shè)計時為了讓軟件的體系結(jié)構(gòu)能夠盡可能地通用,定義了Application和Document這兩個抽象父類,客戶必須通過它們的子類來處理某一具體類型的文檔。例如,要想利用該框架來編寫一個PDF文件瀏覽器,必須先定義PDFApplication和PDFDocument這兩個類,它們應(yīng)該分別繼承于Application和Document。

Application的職責(zé)是對Document進行管理,并且在需要時創(chuàng)建它們,比如當(dāng)用戶從菜單中選擇Open或者New的時候,Application就要負(fù)責(zé)創(chuàng)建一個Document的實例。顯而易見,被實例化的特定Document子類是與具體應(yīng)用相關(guān)的,因此Application無法預(yù)測哪個Document的子類將被實例化,它只知道一個新的Document何時(When)被創(chuàng)建,但并不知道哪種(Which)具體的Document將被創(chuàng)建。此時若仍堅持使用簡單工廠模式會出現(xiàn)一個非常尷尬的局面:框架必須實例化類,但它只知道不能被實例化的抽象類。

解決的辦法是使用工廠方法模式,它封裝了哪一個Document子類將被創(chuàng)建的信息,并且能夠?qū)⑦@些信息從框架中分離出來。如圖1所示,Application的子類重新定義了Application的抽象方法createDocument(),并返回某個恰當(dāng)?shù)腄ocument子類的實例。我們稱createDocument()是一個工廠方法(factory method),因為它非常形象地描述了類的實例化過程,即負(fù)責(zé)"生產(chǎn)"一個對象。

簡單說來,工廠方法模式的作用就是可以根據(jù)不同的條件生成各種類的實例,這些實例通常屬于多個相似的類型,并且具有共同的父類。工廠方法模式將這些實例的創(chuàng)建過程封裝了起來,從而簡化了客戶程序的編寫,并改善了軟件體系結(jié)構(gòu)的可擴展性,使得將來能夠以最小的代價加入新的子類。工廠方法這一模式適合在如下場合中運用:

◆當(dāng)無法得知必須創(chuàng)建的對象屬于哪個類的時候,或者無法得知屬于哪個類的對象將被返回的時候,但前提是這些對象都符合一定的接口標(biāo)準(zhǔn)。

◆當(dāng)一個類希望由它的子類來決定所創(chuàng)建的對象的時候,其目的是使程序的可擴展性更好,在加入其他類時更具彈性。

◆當(dāng)創(chuàng)建對象的職責(zé)被委托給多個幫助子類(helper subclass)中的某一個,并且希望將哪個子類是代理者這一信息局部化的時候。

需要說明的是,使用工廠方法模式創(chuàng)建對象并不意味著一定會讓代碼變得更短(實事上往往更長),并且可能需要設(shè)計更多的輔助類,但它的確可以靈活地、有彈性地創(chuàng)建尚未確定的對象,從而簡化了客戶端應(yīng)用程序的邏輯結(jié)構(gòu),并提高了代碼的可讀性和可重用性。

#p#

二、模式引入

工廠方法這一模式本身雖然并不復(fù)雜,但卻是最重要的設(shè)計模式之一,無論是在COM、CORBA或是EJB中,都可以隨處見到它的身影。面向?qū)ο蟮囊粋€基本思想是在不同的對象間進行責(zé)權(quán)的合理分配,從本質(zhì)上講,工廠方法模式是一種用來創(chuàng)建對象的多態(tài)方法(polymorphic method),它在抽象父類中聲明用來創(chuàng)建對象的方法接口,而具體子類則通過覆蓋該方法將對象的創(chuàng)建過程局部化,包括是否實例化一個子類,以及是否對它進行初始化等等。從某種程度上說,工廠方法可以看成是構(gòu)造函數(shù)的特殊化,其特殊性表現(xiàn)在能夠用一致的方法來創(chuàng)建不同的對象,而不用擔(dān)心當(dāng)前正在對哪個類進行實例化,因為究竟創(chuàng)建哪個類的對象將取決于它的子類。

假設(shè)我們打算開發(fā)一個用于個人信息管理(Personal Information Manager,PIM)的軟件,它可以保存日常工作和生活中所需的各種信息,包括地址本、電話簿、約會提醒、日程安排等等。很顯然,PIM用戶界面(User Interface)的設(shè)計將是比較復(fù)雜的,因為必須為每種信息的輸入、驗證和修改都提供單獨的界面,以便同用戶進行交互。比較簡單的做法是在PIM中為各種信息的處理編寫相應(yīng)的用戶界面,但代價是將導(dǎo)致軟件的可擴展性非常差,因為一旦今后要加入對其他信息(比如銀行帳戶)進行管理的功能時,就必須對PIM進行修改,添加相應(yīng)的用戶界面,從而最終導(dǎo)致PIM變得越來越復(fù)雜,結(jié)構(gòu)龐大而難以維護。改進的辦法是將處理各種信息的用戶界面從PIM中分離出來,使PIM不再關(guān)心用戶如何輸入數(shù)據(jù),如何對用戶輸入進行驗證,以及用戶如何修改信息等,所有的這些都交由一個專門的軟件模塊來完成,而PIM要做的只是提供一個對這些個人信息進行管理的總體框架。

在具體實現(xiàn)時可以設(shè)計一個通用接口Editable,并且讓所有處理特定個人信息(如通信地址和電話號碼)的用戶界面都繼承于它,而PIM則通過Editable提供的方法getEditor()獲得Editor的一個實例,并利用它來對用戶輸入進行統(tǒng)一的處理。例如,當(dāng)用戶完成輸入之后,PIM可以調(diào)用Editor中的方法getContent()來獲取用戶輸入的數(shù)據(jù),或者調(diào)用resetUI()來清除用戶輸入的數(shù)據(jù)。在采用這一體系結(jié)構(gòu)之后,如果要擴展PIM的功能,只需添加與之對應(yīng)的Editable和Editor就可以了,而不用對PIM本身進行修改。

現(xiàn)在離目標(biāo)還有一步之遙,由于Editable和Editor都只是通用的接口,但PIM卻需要對它們的子類進行實例化,此時自然應(yīng)該想到運用工廠方法模式,為PIM定義一個EditableFactory接口來創(chuàng)建Editable的對象。這樣一來,整個PIM的體系結(jié)構(gòu)就將如圖2所示。

Editable接口定義了一個公共的構(gòu)造性方法(builder method)getEditor(),它返回一個Editor對象,其完整的代碼如清單1所示。任何一項個人信息都擁有自己獨立的用戶界面(Editor),負(fù)責(zé)獲取數(shù)據(jù)并在需要的時候進行修改,而PIM唯一要做事情的只是通過Editable來獲得Editor,并利用它來對用戶輸入的數(shù)據(jù)進行相應(yīng)的操作。

 
 
 
 
  1. 代碼清單1:editable.py  
  2. class Editable:  
  3.   """ 個人信息用戶界面的公共接口 """  
  4.   # 獲得個人信息編輯界面  
  5.   def getEditor(self):  
  6. pass 

Editor接口給出了處理所有個人信息的公共接口,其完整的代碼如清單2所示。PIM通過調(diào)用getUI()方法能夠獲得與用戶進行交互的UI組件,根據(jù)當(dāng)前正在處理的個人信息的不同,這些組件可能簡單到只是一個文本輸入框,也可以復(fù)雜到是一個包含了多個圖形控件(Widget)的對話框。利用Editor提供的getContent()、commitChanges()和resetUI()方法,PIM還可以獲取、提交或者清空用戶輸入的個人信息。在引入Editor之后, PIM就能夠從處理特定個人信息的用戶界面中解脫出來,從而可以將注意力集中在如何對這些信息進行統(tǒng)一管理的問題上。

 
 
 
 
  1. 代碼清單2:editor.py  
  2. class Editor:  
  3.   """ 用戶使用特定的Editor來編輯個人信息 """  
  4.   # 獲取代表用戶界面(UI)的對象  
  5.   def getUI(self):  
  6.     pass  
  7.     
  8.   # 獲取用戶輸入的數(shù)據(jù)  
  9.   def getContent(self):  
  10.     pass  
  11.   # 提交用戶輸入的數(shù)據(jù)  
  12.   def commitChanges(self):  
  13.     pass  
  14.       
  15.   # 清空用戶輸入的數(shù)據(jù)  
  16.   def resetUI(self):  
  17.     pass 

EditableAddress是Editable的一個具體實現(xiàn),PIM使用它來處理個人地址信息,其完整的代碼如清單3所示。

 
 
 
 
  1. 代碼清單3:editableaddress.py  
  2. from editor import Editor  
  3. from editable import Editable  
  4. import Tkinter  
  5. class EditableAddress(Editable):  
  6.   """ 用于處理個人地址信息的Editable """  
  7.     
  8.   # 構(gòu)造函數(shù)  
  9.   def __init__(self, master):  
  10.     self.master = master  
  11.     self.name = "" 
  12.     self.province = "" 
  13.     self.city = "" 
  14.     self.street = "" 
  15.     self.zipcode = "" 
  16.     self.editor = AddressEditor(self)  
  17.       
  18.   # 獲取相關(guān)聯(lián)的Editor  
  19.   def getEditor(self):  
  20.     return self.editor  
  21.     
  22.     
  23. class AddressEditor(Editor, Tkinter.Frame):  
  24.   """ 用于處理個人地址信息的Editor """  
  25.     
  26.   # 構(gòu)造函數(shù)  
  27.   def __init__(self, owner):  
  28.     Tkinter.Frame.__init__(self, owner.master)  
  29.     self.owner = owner  
  30.     self.name = Tkinter.StringVar()  
  31.     self.province = Tkinter.StringVar()  
  32.     self.city = Tkinter.StringVar()  
  33.     self.street = Tkinter.StringVar()  
  34.     self.zipcode = Tkinter.StringVar()  
  35.     self.createWidgets()  
  36.      
  37.   # 構(gòu)造用戶界面  
  38.   def createWidgets(self):  
  39.     # 姓名  
  40.     nameFrame = Tkinter.Frame(self)  
  41.     nameLabel = Tkinter.Label(nameFrame, text="Name:")  
  42.     nameEntry = Tkinter.Entry(nameFrame, textvariable=self.name)  
  43.     nameLabel.config(anchor=Tkinter.E, width=8, pady=3)  
  44.     nameLabel.pack(side=Tkinter.LEFT)  
  45.     nameEntry.pack(side=Tkinter.LEFT)  
  46.     nameFrame.pack()  
  47.       
  48.     # 省份  
  49.     provinceFrame = Tkinter.Frame(self)  
  50.     provinceLabel = Tkinter.Label(provinceFrame, text="Province:")  
  51.     provinceEntry = Tkinter.Entry(provinceFrame, textvariable=self.province)  
  52.     provinceLabel.config(anchor=Tkinter.E, width=8, pady=3)  
  53.     provinceLabel.pack(side=Tkinter.LEFT)  
  54.     provinceEntry.pack(side=Tkinter.LEFT)  
  55.     provinceFrame.pack()  
  56.     # 城市  
  57.     cityFrame = Tkinter.Frame(self)  
  58.     cityLabel = Tkinter.Label(cityFrame, text="City:")  
  59.     cityEntry = Tkinter.Entry(cityFrame, textvariable=self.city)  
  60.     cityLabel.config(anchor=Tkinter.E, width=8, pady=3)  
  61.     cityLabel.pack(side=Tkinter.LEFT)  
  62.     cityEntry.pack(side=Tkinter.LEFT)  
  63.     cityFrame.pack()  
  64.       
  65.     # 街道  
  66.     streetFrame = Tkinter.Frame(self)  
  67.     streetLabel = Tkinter.Label(streetFrame, text="Street:")  
  68.     streetEntry = Tkinter.Entry(streetFrame, textvariable=self.street)  
  69.     streetLabel.config(anchor=Tkinter.E, width=8, pady=3)  
  70.     streetLabel.pack(side=Tkinter.LEFT)  
  71.     streetEntry.pack(side=Tkinter.LEFT)  
  72.     streetFrame.pack()  
  73.       
  74.     # 郵編  
  75.     zipcodeFrame = Tkinter.Frame(self)  
  76.     zipcodeLabel = Tkinter.Label(zipcodeFrame, text="ZIP Code:")  
  77.     zipcodeEntry = Tkinter.Entry(zipcodeFrame, textvariable=self.zipcode)  
  78.     zipcodeLabel.config(anchor=Tkinter.E, width=8, pady=3)  
  79.     zipcodeLabel.pack(side=Tkinter.LEFT)  
  80.     zipcodeEntry.pack(side=Tkinter.LEFT)  
  81.     zipcodeFrame.pack()  
  82.       
  83.   # 重載Editor中的方法,獲取代表用戶界面(UI)的對象  
  84.   def getUI(self):  
  85.     return self  
  86.     
  87.   # 重載Editor中的方法,獲取用戶輸入的數(shù)據(jù)  
  88.   def getContent(self):  
  89.     content  = "    Name: " + self.name.get() + "\n"  
  90.     content += "Province: " + self.province.get() + "\n"   
  91.     content += "    City: " + self.city.get() + "\n"   
  92.     content += "  Street: " + self.street.get() + "\n"   
  93.     content += "ZIP Code: " + self.zipcode.get()  
  94.     return content  
  95.     
  96.   # 重載Editor中的方法,提交用戶輸入的數(shù)據(jù)  
  97.   def commitChanges(self):  
  98.     selfself.owner.name = self.name.get()  
  99.     selfself.owner.province = self.province.get()  
  100.     selfself.owner.city = self.city.get()  
  101.     selfself.owner.street = self.street.get()  
  102.     selfself.owner.zipcode = self.zipcode.get()  
  103.   # 重載Editor中的方法,清空用戶輸入的數(shù)據(jù)  
  104.   def resetUI(self):  
  105.     self.name.set("")  
  106.     self.province.set("")  
  107.     self.city.set("")  
  108.     self.street.set("")  
  109.     self.zipcode.set("")  

EditablePhone是Editable的另一個具體實現(xiàn),PIM使用它來處理個人電話號碼,其完整的代碼如清單4所示。

 
 
 
 
  1. 代碼清單4:editablephone.py  
  2. from editor import Editor  
  3. from editable import Editable  
  4. import Tkinter  
  5. class EditablePhone(Editable):  
  6.   """ 用于處理個人電話號碼的Editable """  
  7.     
  8.   # 構(gòu)造函數(shù)  
  9.   def __init__(self, master):  
  10.     self.master =master  
  11.     self.areaCode = "";  
  12.     self.phoneNumber = "" 
  13.     self.editor = PhoneEditor(self)  
  14.       
  15.   # 獲取相關(guān)聯(lián)的Editor  
  16.   def getEditor(self):  
  17.     return self.editor  
  18.     
  19. class PhoneEditor(Editor, Tkinter.Frame):  
  20.   """ 用于處理個人電話號碼的Editor """  
  21.     
  22.   # 構(gòu)造函數(shù)  
  23.   def __init__(self, owner):  
  24.     self.owner = owner  
  25.     Tkinter.Frame.__init__(self, self.owner.master)  
  26.     self.areaCode = Tkinter.StringVar()  
  27.     self.phoneNumber = Tkinter.StringVar()  
  28.     # 構(gòu)造用戶界面  
  29.     codeLabel = Tkinter.Label(self, text="Area Code:")  
  30.     codeEntry = Tkinter.Entry(self, textvariable=self.areaCode)  
  31.     codeLabel.config(anchor=Tkinter.E, width=12, pady=3)  
  32.     codeLabel.grid(row=0, column=0)  
  33.     codeEntry.grid(row=0, column=1)  
  34.       
  35.     numberLabel = Tkinter.Label(self, text="Phone Number:")  
  36.     numberEntry = Tkinter.Entry(self, textvariable=self.phoneNumber)  
  37.     numberLabel.config(anchor=Tkinter.E, width=12, pady=3)  
  38.     numberLabel.grid(row=1, column=0)  
  39.     numberEntry.grid(row=1, column=1)  
  40.       
  41.   # 重載Editor中的方法,獲取代表用戶界面(UI)的對象  
  42.   def getUI(self):  
  43.     return self  
  44.     
  45.   # 重載Editor中的方法,獲取用戶輸入的數(shù)據(jù)  
  46.   def getContent(self):  
  47.     content  = "   Area Code: " + self.areaCode.get() + "\n"  
  48.     content += "Phone Number: " + self.phoneNumber.get() + "\n"   
  49.     return content  
  50.    
  51.   # 重載Editor中的方法,提交用戶輸入的數(shù)據(jù)  
  52.   def commitChanges(self):  
  53.     selfself.owner.areaCode = self.areaCode.get()  
  54.     selfself.owner.phoneNumber = self.phoneNumber.get()  
  55.       
  56.   # 重載Editor中的方法,清空用戶輸入的數(shù)據(jù)  
  57.   def resetUI(self):  
  58.     self.areaCode.set("")  
  59.     self.phoneNumber.set("") 

EditableFactory接口是在PIM中應(yīng)用工廠方法模式的核心,其完整的代碼如清單5所示。與簡單工廠模式中負(fù)責(zé)創(chuàng)建所有對象的"超級類"不同,EditableFactory只定義了如何實例化Editable的工廠方法createEditable(),并不掌握它們的生殺大權(quán),真正負(fù)責(zé)完成創(chuàng)建工作的是EditableFactory的子類。

 
 
 
 
  1. 代碼清單5:editablefactory.py  
  2. class EditableFactory:  
  3.   """ 用于創(chuàng)建Editable的工廠類 """  
  4.     
  5.   #  實例化Editable對象  
  6.   def createEditable(self, master):  
  7.     pass 

EditableAddressFactory是EditableFactory的一個具體實現(xiàn),PIM使用它來實例化EditableAddress對象,其完整的代碼如清單6所示。

 
 
 
 
  1. 代碼清單6:editableaddressfactory.py  
  2. from editablefactory import EditableFactory  
  3. from editableaddress import EditableAddress  
  4. class EditableAddressFactory(EditableFactory):  
  5.   """ 用于創(chuàng)建EditableAddress的工廠類 """  
  6.     
  7.   # 重載EditableFactory中的方法,實例化EditableAddress對象  
  8.   def createEditable(self, master):  
  9.     address = EditableAddress(master)  
  10.     return address 

EditablePhoneFactory是EditableFactory的另一個具體實現(xiàn),PIM使用它來實例化EditablePhone對象,其完整的代碼如清單7所示。

 
 
 
 
  1. 代碼清單7:editablephonefactory.py  
  2. from editablefactory import EditableFactory  
  3. from editablephone import EditablePhone  
  4. class EditablePhoneFactory(EditableFactory):  
  5.   """ 用于創(chuàng)建EditablePhone的工廠類 """  
  6.     
  7.   # 重載EditableFactory中的方法,實例化EditablePhone對象  
  8.   def createEditable(self, master):  
  9.     phone = EditablePhone(master)  
  10.     return phone 

所有這些輔助類都定義好之后,接下去就可以編寫PIM類了,它提供了一個對各種個人信息進行統(tǒng)一管理的框架,其完整的代碼如清單8所示。

 
 
 
 
  1. 代碼清單8:pim.py  
  2. from editablephone import EditablePhone  
  3. from editableaddressfactory import EditableAddressFactory  
  4. from editablephonefactory import EditablePhoneFactory  
  5. import Tkinter  
  6. class PIM:  
  7.   """ 個人信息管理 """  
  8.     
  9.   # 構(gòu)造函數(shù)  
  10.   def __init__(self):  
  11.     mainFrame = Tkinter.Frame()      
  12.     mainFrame.master.title("PIM")  
  13.     # 命令按鈕  
  14.     addressButton = Tkinter.Button(mainFrame, width=10, text="Address")  
  15.     phoneButton = Tkinter.Button(mainFrame, width=10, text="Phone")  
  16.     commitButton = Tkinter.Button(mainFrame, width=10, text="Commit")      
  17.     resetButton = Tkinter.Button(mainFrame, width=10, text="Reset")          
  18.     addressButton.config(command=self.addressClicked)  
  19.     phoneButton.config(command=self.phoneClicked)      
  20.     commitButton.config(command=self.commitClicked)          
  21.     resetButton.config(command=self.resetClicked)          
  22.     addressButton.grid(row=0, column=1, padx=10, pady=5, stick=Tkinter.E)  
  23.     phoneButton.grid(row=1, column=1, padx=10, pady=5, stick=Tkinter.E)  
  24.     commitButton.grid(row=2, column=1, padx=10, pady=5, stick=Tkinter.E)  
  25.     resetButton.grid(row=3, column=1, padx=10, pady=5, stick=Tkinter.E)  
  26.       
  27.  # 用來容納各類Editor的容器  
  28.     self.editorFrame = Tkinter.Frame(mainFrame)  
  29.     self.editorFrame.grid(row=0, column=0, rowspan=4)  
  30.     self.editorFrame.grid_configure(stick=Tkinter.N, pady=15)  
  31.     self.editor = Tkinter.Frame(self.editorFrame)  
  32.     self.editor.grid()  
  33.       
  34.  # 個人信息顯示區(qū)域  
  35.     self.content = Tkinter.StringVar()  
  36.     self.contentLabel = Tkinter.Label(mainFrame, width=50, height=5)  
  37.     self.contentLabel.configure(textvariable=self.content)  
  38.     self.contentLabel.configure(anchor=Tkinter.W, font="Arial 10 italic bold")  
  39.     self.contentLabel.configure(relief=Tkinter.RIDGE, pady=5, padx=10)  
  40.     self.contentLabel.grid(row=4, column=0, columnspan=2)  
  41.       
  42.     mainFrame.pack()  
  43.     mainFrame.mainloop()  
  44.       
  45.   # Address按鈕的回調(diào)函數(shù)  
  46.   def addressClicked(self):  
  47.     address = EditableAddressFactory().createEditable(self.editorFrame)  
  48.     self.editor.grid_remove()  
  49.     self.editor = address.getEditor()  
  50.     self.editor.getUI().grid()  
  51.   # Phone按鈕的回調(diào)函數(shù)  
  52.   def phoneClicked(self):  
  53.     phone = EditablePhoneFactory().createEditable(self.editorFrame)  
  54.     self.editor.grid_remove()  
  55.     self.editor = phone.getEditor()  
  56.     self.editor.getUI().grid()  
  57.       
  58.   # Commit按鈕的回調(diào)函數(shù)  
  59.   def commitClicked(self):  
  60.     content = self.editor.getContent()  
  61.     self.content.set(content)  
  62.     
  63.   # Reset按鈕的回調(diào)函數(shù)  
  64.   def resetClicked(self):  
  65.     self.editor.resetUI()  
  66.       
  67. # 主函數(shù)  
  68. if (__name__ == "__main__"):  
  69.   app = PIM() 

圖3是PIM在運行時的界面效果。

#p#

三、一般結(jié)構(gòu)

工廠方法模式是簡單工廠模式的進一步抽象和推廣,它不僅保持了簡單工廠模式能夠向客戶隱藏類的實例化過程這一優(yōu)點,而且還通過多態(tài)性克服了工廠類過于復(fù)雜且不易于擴展的缺點。在工廠方法模式中,處于核心地位的工廠類不再負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體的創(chuàng)建工作交由子類去完成。工廠方法模式中的核心工廠類經(jīng)過功能抽象之后,成為了一個抽象的工廠角色,僅負(fù)責(zé)給出具體工廠子類必須實現(xiàn)的接口,而不涉及哪種產(chǎn)品類應(yīng)當(dāng)被實例化這一細(xì)節(jié)。工廠方法模式的一般性結(jié)構(gòu)如圖4所示,圖中為了簡化只給出了一個產(chǎn)品類和一個工廠類,但在實際系統(tǒng)中通常需要設(shè)計多個產(chǎn)品類和多個工廠類。

工廠方法模式的實質(zhì)是將對象的創(chuàng)建延遲到其子類實現(xiàn),即由子類根據(jù)當(dāng)前情況動態(tài)決定應(yīng)該實例化哪一個產(chǎn)品類。從上圖可以看出,工廠方法模式涉及到抽象工廠角色、具體工廠角色、抽象產(chǎn)品角色和具體產(chǎn)品角色四個參與者。

◆抽象工廠(Creator)角色:是工廠方法模式的核心,它負(fù)責(zé)定義創(chuàng)建抽象產(chǎn)品對象的工廠方法。抽象工廠不能被外界直接調(diào)用,但任何在模式中用于創(chuàng)建產(chǎn)品對象的工廠類都必須實現(xiàn)由它所定義的工廠方法。

具體工廠(Concrete Creator)角色:是工廠方法模式的對外接口,它負(fù)責(zé)實現(xiàn)創(chuàng)建具體產(chǎn)品對象的內(nèi)部邏輯。具體工廠與應(yīng)用密切相關(guān),可以被外界直接調(diào)用,創(chuàng)建所需要的產(chǎn)品。

抽象產(chǎn)品(Product)角色:是工廠方法模式所創(chuàng)建的所有對象的父類,它負(fù)責(zé)描述所有具體產(chǎn)品共有的公共接口。

具體產(chǎn)品(Concrete Product)角色:是工廠方法模式的創(chuàng)建目標(biāo),所有創(chuàng)建的對象都是充當(dāng)這一角色的某個具體類的實例。

抽象工廠角色負(fù)責(zé)聲明工廠方法(factory method),用來"生產(chǎn)"抽象產(chǎn)品,以下是抽象工廠的示例性Python代碼:

 
 
 
 
  1. 代碼清單9:creator.py  
  2. class Creator:  
  3.   """ 抽象工廠角色 """  
  4.     
  5.   # 創(chuàng)建抽象產(chǎn)品的工廠方法  
  6.   def factoryMethod(self):  
  7.     pass 

具體工廠角色負(fù)責(zé)創(chuàng)建一個具體產(chǎn)品的實例,并將其返回給調(diào)用者。具體工廠是與具體產(chǎn)品相關(guān)的,實現(xiàn)時一般常用的做法是為每個具體產(chǎn)品定義一個具體工廠。以下是具體工廠的示例性Python代碼:

 
 
 
 
  1. 代碼清單10:concretecreator.py  
  2. class ConcreteCreator(Creator):  
  3.   """ 具體工廠角色 """  
  4.     
  5.   # 創(chuàng)建具體產(chǎn)品的工廠方法  
  6.   def factoryMethod(self):  
  7.     product =  ConcreteProduct()  
  8.     return product 

抽象產(chǎn)品角色的主要目的是為所有的具體產(chǎn)品提供一個共同的接口,通常只需給出相應(yīng)的聲明就可以了,而不用給出具體的實現(xiàn)。以下是抽象產(chǎn)品類的示例性Python代碼:

 
 
 
 
  1. 代碼清單11:product.py  
  2. class Product:  
  3.   """ 抽象產(chǎn)品角色 """  
  4.     
  5.   # 所有產(chǎn)品類的公共接口  
  6.   def interface(self):  
  7.     pass 

具體產(chǎn)品角色充當(dāng)最終的創(chuàng)建目標(biāo),一般來講它是抽象產(chǎn)品類的子類,實現(xiàn)了抽象產(chǎn)品類中定義的所有工廠方法,實際應(yīng)用時通常會具有比較復(fù)雜的業(yè)務(wù)邏輯。以下是具體產(chǎn)品類的示例性Python代碼:

 
 
 
 
  1. 代碼清單12:concreteproduct.py  
  2. class ConcreteProduct(Product):  
  3.   """ 具體產(chǎn)品角色 """  
  4.     
  5.   # 公共接口的實現(xiàn)  
  6.   def interface(self):  
  7.     print "Concrete Product Method" 

    
在應(yīng)用工廠方法模式時,通常還需要再引入一個客戶端角色,由它負(fù)責(zé)創(chuàng)建具體的工廠對象,然后再調(diào)用工廠對象中的工廠方法來創(chuàng)建相應(yīng)的產(chǎn)品對象。以下是客戶端的示例性Python代碼:

 
 
 
 
  1. 代碼清單13:client.py  
  2. class Client:  
  3.   """ 客戶端角色 """  
  4.     
  5.   def run(self):  
  6.     creator = ConcreteCreator()  
  7.     product = creator.factoryMethod()  
  8.     product.interface()  
  9. # 主函數(shù)  
  10. if (__name__ == "__main__"):  
  11.   client = Client()  
  12.   client.run() 

  
在這個簡單的示意性實現(xiàn)里,充當(dāng)具體產(chǎn)品和具體工廠角色的類都只有一個,但在真正的實際應(yīng)用中,通常遇到的都是同時會有多個具體產(chǎn)品類的情況,此時相應(yīng)地需要提供多個具體工廠類,每個具體工廠都負(fù)責(zé)生產(chǎn)對應(yīng)的具體產(chǎn)品。

工廠方法模式的活動序列如圖5所示,客戶端Client首先創(chuàng)建ConcreteCreator對象,然后調(diào)用ConcreteCreator對象的工廠方法factoryMethod(),由它負(fù)責(zé)"生產(chǎn)"出所需要的ConcreteProduct對象。

#p#

四、實際運用

使用工廠方法模式可以在不修改具體工廠角色的情況下引入新的產(chǎn)品,這一點無疑使得工廠方法模式具有比簡單工廠模式更好的可擴展性。在開發(fā)實際的軟件系統(tǒng)時,通常是先設(shè)計產(chǎn)品角色,然后才開始設(shè)計工廠角色,而復(fù)雜的需求導(dǎo)致將在抽象產(chǎn)品和具體產(chǎn)品之間形成非常龐大的樹狀結(jié)構(gòu),如圖6所示。

在上面的產(chǎn)品等級結(jié)構(gòu)中,出現(xiàn)了多于一個的抽象產(chǎn)品類,以及多于兩個的類層次,這是在構(gòu)造真實系統(tǒng)中經(jīng)常遇到的情況。在為這一軟件體系結(jié)構(gòu)應(yīng)用工廠方法模式時,通常的做法是按照產(chǎn)品的等級結(jié)構(gòu)再設(shè)計一個相同的工廠等級結(jié)構(gòu),如圖7所示。

定義工廠角色的目的是為了創(chuàng)建相應(yīng)的產(chǎn)品角色,因此整個系統(tǒng)的架構(gòu)將如圖8所示。這一結(jié)構(gòu)常常被稱為平行的類層次(parallel class hierarchies),它使得一個類能夠?qū)⑺囊恍┞氊?zé)委托給另一個獨立的類,而工廠方法則是聯(lián)系兩者之間的紐帶。工廠方法模式并沒有限制產(chǎn)品等級的層數(shù),雖然前面給出的一般性結(jié)構(gòu)中只有兩個層次(抽象產(chǎn)品層和具體產(chǎn)品層),但在實際運用時卻往往需要更加復(fù)雜的產(chǎn)品層次。

在工廠方法模式的一般性結(jié)構(gòu)中,每當(dāng)具體工廠類中的工廠方法被請求時,都會調(diào)用具體產(chǎn)品類的構(gòu)造函數(shù)來創(chuàng)建一個新的產(chǎn)品實例,然后再將這個實例提供給客戶端。但在實際軟件系統(tǒng)中應(yīng)用工廠方法模式時,工廠方法所做的事情可能更加復(fù)雜,其中最常見到的一種情況是循環(huán)使用產(chǎn)品對象。所采用的策略是將工廠對象創(chuàng)建的所有產(chǎn)品對象登記到一個對象池(object pool)中,這樣每當(dāng)客戶請求工廠方法創(chuàng)建相應(yīng)的產(chǎn)品對象時,可以先從對象池中查詢符合條件的產(chǎn)品對象,如果對象池中恰巧有這樣的對象,那就直接將這個產(chǎn)品對象返回給客戶端;如果對象池中沒有這樣的對象,那就創(chuàng)建一個新的滿足要求的產(chǎn)品對象,將其登記到對象池中,然后再返回給客戶端。

工廠方法模式依賴于工廠角色和產(chǎn)品角色的多態(tài)性,但在實際運用時這個模式可能出現(xiàn)退化,其表現(xiàn)就是多態(tài)性的喪失。在工廠方法模式中,所有的具體工廠對象應(yīng)該共享一個抽象的超類,或者換句話說,應(yīng)當(dāng)有多個具體工廠類作為一個抽象工廠類的子類存在于工廠等級結(jié)構(gòu)中,但如果工廠等級結(jié)構(gòu)中只有一個具體工廠類的話,那么抽象工廠角色可以省略。當(dāng)抽象工廠角色被省略時,工廠方法模式就發(fā)生了退化,這一退化表現(xiàn)為工廠角色多態(tài)性的喪失,退化后的模式仍然可以發(fā)揮部分工廠方法模式的作用,通常被稱為退化的工廠方法模式。退化的工廠方法模式在很大程度上與簡單工廠模式相似,如圖9所示,實際運用時可以考慮用簡單工廠模式進行替代。

在工廠方法模式中,從工廠方法返回的應(yīng)當(dāng)是抽象產(chǎn)品類型,而不是具體產(chǎn)品類型,因為只有這樣才能保證產(chǎn)品角色的多態(tài)性。也就是說,調(diào)用工廠方法的客戶端可以針對抽象產(chǎn)品類進行編程,而不必依賴于具體產(chǎn)品類。在實際運用時有可能會出現(xiàn)一種很特殊的情況,那就是工廠方法只需要返回一個具體產(chǎn)品類,此時工廠方法模式的功能同樣會發(fā)生退化,但這一退化將表現(xiàn)為產(chǎn)品角色多態(tài)性的喪失,如圖10所示。嚴(yán)格說來,當(dāng)工廠方法模式出現(xiàn)這一退化時,就不能再稱為工廠方法模式了,因為客戶端從工廠方法的靜態(tài)類型就可以判斷出將要得到的是什么類型的對象,而這一點恰好違背了工廠方法模式的初衷。

五、優(yōu)勢和不足

在工廠方法模式中,工廠方法用來創(chuàng)建客戶所需要的產(chǎn)品,同時還向客戶隱藏了哪種具體產(chǎn)品類將被實例化這一細(xì)節(jié)。工廠方法模式的核心是一個抽象工廠類,各種具體工廠類通過從抽象工廠類中將工廠方法繼承下來,使得客戶可以只關(guān)心抽象產(chǎn)品和抽象工廠,完全不用理會返回的是哪一種具體產(chǎn)品,也不用關(guān)心它是如何被具體工廠創(chuàng)建的。

基于工廠角色和產(chǎn)品角色的多態(tài)性設(shè)計是工廠方法模式的關(guān)鍵,它使得工廠可以自主確定創(chuàng)建何種產(chǎn)品對象,而如何創(chuàng)建這個對象的細(xì)節(jié)則完全封裝在具體工廠內(nèi)部。工廠方法模式之所以又被稱為多態(tài)工廠模式,顯然是因為所有的具體工廠類都具有同一抽象父類。

使用工廠方法模式的另一個優(yōu)點是在系統(tǒng)中加入新產(chǎn)品時,不需要對抽象工廠和抽象產(chǎn)品提供的接口進行修改,而只要添加一個具體工廠和具體產(chǎn)品就可以了,沒有必要修改客戶端,也沒有必須修改其他的具體工廠和具體產(chǎn)品,系統(tǒng)的可擴展性非常好。優(yōu)秀的面向?qū)ο笤O(shè)計鼓勵使用封裝(Encapsulation)和委托(Delegation)來構(gòu)造軟件系統(tǒng),而工廠方法模式則是使用了封裝和委托的典型例子,其中封裝是通過抽象工廠來體現(xiàn)的,而委托則是通過抽象工廠將創(chuàng)建對象的責(zé)任完全交給具體工廠來體現(xiàn)的。

使用工廠方法模式的缺點是在添加新產(chǎn)品時,需要編寫新的具體產(chǎn)品類,而且還要提供與之對應(yīng)的具體工廠類,當(dāng)兩者都比較簡單時,系統(tǒng)的額外開銷相對較大。

六、小結(jié)

工廠方法模式的核心思想是定義一個用來創(chuàng)建對象的公共接口,由工廠而不是客戶來決定需要被實例化的類,它通常在構(gòu)造系統(tǒng)整體框架時被用到。工廠方法模式看上去似乎比較簡單,但是內(nèi)涵卻極其深刻,抽象、封裝、繼承、委托、多態(tài)等面向?qū)ο笤O(shè)計中的理論都得到了很好的體現(xiàn),應(yīng)用范圍非常廣泛。

原文鏈接:http://www.ibm.com/developerworks/cn/linux/l-pypt/part3/index.html

【編輯推薦】

  1. 全能選手 看看Python應(yīng)乎潮流的72變
  2. 匪夷所思 Python實現(xiàn)尾遞歸優(yōu)化
  3. Python設(shè)計模式:用模式改變軟件設(shè)計
  4. Python閉包的概念、形式與應(yīng)用
  5. 旁觀者清 Python與Ruby各有千秋

新聞標(biāo)題:Python設(shè)計模式:工廠方法模式初探
本文URL:http://m.5511xx.com/article/dhchihg.html