歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> Python 設計模式——Factory Method模式

Python 設計模式——Factory Method模式

日期:2017/2/27 14:23:37   编辑:更多Linux
  一、簡介  工廠方法(Factory Method)模式又稱為虛擬構造器(Virtual ConstrUCtor)模式或者多態工廠(Polymorphic Factory)模式,屬於類的創建型模式。在工廠方法模式中,父類負責定義創建對象的公共接口,而子類則負責生成具體的對象,這樣做的目的是將類的實例化操作延遲到子類中完成,即由子類來決定究竟應該實體化哪一個類。    在簡單工廠模式中,一個工廠類處於對產品類進行實例化的中心位置上,它知道每一個產品類的細節,並決定何時哪一個產品類應當被實例化。簡單工廠模式的優點是能夠使客戶端獨立於產品的創建過程,並且在系統中引入新產品時無需對客戶端進行修改,缺點是當有新產品要加入到系統中時,必須對工廠類進行修改,以加入必要的處理邏輯。簡單工廠模式的致命弱點就是處於核心地位的工廠類,因為一旦它無法確定要對哪個類進行實例化時,就無法使用該模式,而工廠方法模式則可以很好地避免這一問題。    考慮這樣一個應用程序框架(Framework),它可以用來浏覽各種格式的文檔,如TXT、DOC、PDF、Html等,設計時為了讓軟件的體系結構能夠盡可能地通用,定義了Application和Document這兩個抽象父類,客戶必須通過它們的子類來處理某一具體類型的文檔。例如,要想利用該框架來編寫一個PDF文件浏覽器,必須先定義PDFApplication和PDFDocument這兩個類,它們應該分別繼承於Application和Document。    Application的職責是對Document進行管理,並且在需要時創建它們,比如當用戶從菜單中選擇Open或者New的時候,Application就要負責創建一個Document的實例。顯而易見,被實例化的特定Document子類是與具體應用相關的,因此Application無法預測哪個Document的子類將被實例化,它只知道一個新的Document何時(When)被創建,但並不知道哪種(Which)具體的Document將被創建。此時若仍堅持使用簡單工廠模式會出現一個非常尴尬的局面:框架必須實例化類,但它只知道不能被實例化的抽象類。    解決的辦法是使用工廠方法模式,它封裝了哪一個Document子類將被創建的信息,並且能夠將這些信息從框架中分離出來。如圖1所示,Application的子類重新定義了Application的抽象方法createDocument(),並返回某個恰當的Document子類的實例。我們稱createDocument()是一個工廠方法(factory method),因為它非常形象地描述了類的實例化過程,即負責"生產"一個對象。     圖1   簡單說來,工廠方法模式的作用就是可以根據不同的條件生成各種類的實例,這些實例通常屬於多個相似的類型,並且具有共同的父類。工廠方法模式將這些實例的創建過程封裝了起來,從而簡化了客戶程序的編寫,並改善了軟件體系結構的可擴展性,使得將來能夠以最小的代價加入新的子類。工廠方法這一模式適合在如下場合中運用:    當無法得知必須創建的對象屬於哪個類的時候,或者無法得知屬於哪個類的對象將被返回的時候,但前提是這些對象都符合一定的接口標准。   當一個類希望由它的子類來決定所創建的對象的時候,其目的是使程序的可擴展性更好,在加入其他類時更具彈性。   當創建對象的職責被委托給多個幫助子類(helper subclass)中的某一個,並且希望將哪個子類是代理者這一信息局部化的時候。   需要說明的是,使用工廠方法模式創建對象並不意味著一定會讓代碼變得更短(實事上往往更長),並且可能需要設計更多的輔助類,但它的確可以靈活地、有彈性地創建尚未確定的對象,從而簡化了客戶端應用程序的邏輯結構,並提高了代碼的可讀性和可重用性。    二、模式引入  工廠方法這一模式本身雖然並不復雜,但卻是最重要的設計模式之一,無論是在COM、CORBA或是EJB中,都可以隨處見到它的身影。面向對象的一個基本思想是在不同的對象間進行責權的合理分配,從本質上講,工廠方法模式是一種用來創建對象的多態方法(polymorphic method),它在抽象父類中聲明用來創建對象的方法接口,而具體子類則通過覆蓋該方法將對象的創建過程局部化,包括是否實例化一個子類,以及是否對它進行初始化等等。從某種程度上說,工廠方法可以看成是構造函數的特殊化,其特殊性表現在能夠用一致的方法來創建不同的對象,而不用擔心當前正在對哪個類進行實例化,因為究竟創建哪個類的對象將取決於它的子類。    假設我們打算開發一個用於個人信息管理(Personal Information Manager,PIM)的軟件,它可以保存日常工作和生活中所需的各種信息,包括地址本、電話簿、約會提醒、日程安排等等。很顯然,PIM用戶界面(User Interface)的設計將是比較復雜的,因為必須為每種信息的輸入、驗證和修改都提供單獨的界面,以便同用戶進行交互。比較簡單的做法是在PIM中為各種信息的處理編寫相應的用戶界面,但代價是將導致軟件的可擴展性非常差,因為一旦今後要加入對其他信息(比如銀行帳戶)進行管理的功能時,就必須對PIM進行修改,添加相應的用戶界面,從而最終導致PIM變得越來越復雜,結構龐大而難以維護。改進的辦法是將處理各種信息的用戶界面從PIM中分離出來,使PIM不再關心用戶如何輸入數據,如何對用戶輸入進行驗證,以及用戶如何修改信息等,所有的這些都交由一個專門的軟件模塊來完成,而PIM要做的只是提供一個對這些個人信息進行管理的總體框架。    在具體實現時可以設計一個通用接口Editable,並且讓所有處理特定個人信息(如通信地址和電話號碼)的用戶界面都繼承於它,而PIM則通過Editable提供的方法getEditor()獲得Editor的一個實例,並利用它來對用戶輸入進行統一的處理。例如,當用戶完成輸入之後,PIM可以調用Editor中的方法getContent()來獲取用戶輸入的數據,或者調用resetUI()來清除用戶輸入的數據。在采用這一體系結構之後,如果要擴展PIM的功能,只需添加與之對應的Editable和Editor就可以了,而不用對PIM本身進行修改。    現在離目標還有一步之遙,由於Editable和Editor都只是通用的接口,但PIM卻需要對它們的子類進行實例化,此時自然應該想到運用工廠方法模式,為PIM定義一個EditableFactory接口來創建Editable的對象。這樣一來,整個PIM的體系結構就將如圖2所示。     圖2   Editable接口定義了一個公共的構造性方法(builder method)getEditor(),它返回一個Editor對象,其完整的代碼如清單1所示。任何一項個人信息都擁有自己獨立的用戶界面(Editor),負責獲取數據並在需要的時候進行修改,而PIM唯一要做事情的只是通過Editable來獲得Editor,並利用它來對用戶輸入的數據進行相應的操作。    代碼清單1:editable.py  class Editable:   """ 個人信息用戶界面的公共接口 """     # 獲得個人信息編輯界面   def getEditor(self):  pass     Editor接口給出了處理所有個人信息的公共接口,其完整的代碼如清單2所示。PIM通過調用getUI()方法能夠獲得與用戶進行交互的UI組件,根據當前正在處理的個人信息的不同,這些組件可能簡單到只是一個文本輸入框,也可以復雜到是一個包含了多個圖形控件(Widget)的對話框。利用Editor提供的getContent()、commitChanges()和resetUI()方法,PIM還可以獲取、提交或者清空用戶輸入的個人信息。在引入Editor之後, PIM就能夠從處理特定個人信息的用戶界面中解脫出來,從而可以將注意力集中在如何對這些信息進行統一管理的問題上。    代碼清單2:editor.py  class Editor:   """ 用戶使用特定的Editor來編輯個人信息 """     # 獲取代表用戶界面(UI)的對象   def getUI(self):  pass      # 獲取用戶輸入的數據   def getContent(self):  pass     # 提交用戶輸入的數據   def commitChanges(self):  pass     # 清空用戶輸入的數據   def resetUI(self):  pass     EditableAddress是Editable的一個具體實現,PIM使用它來處理個人地址信息,其完整的代碼如清單3所示。    代碼清單3:editableaddress.py  from editor import Editor  from editable import Editable  import Tkinter    class EditableAddress(Editable):   """ 用於處理個人地址信息的Editable """      # 構造函數   def __init__(self, master):  self.master = master  self.name = ""  self.province = ""  self.city = ""  self.street = ""  self.zipcode = ""  self.editor = AddressEditor(self)     # 獲取相關聯的Editor   def getEditor(self):  return self.editor     class AddressEditor(Editor, Tkinter.Frame):   """ 用於處理個人地址信息的Editor """      # 構造函數   def __init__(self, owner):  Tkinter.Frame.__init__(self, owner.master)  self.owner = owner  self.name = Tkinter.StringVar()  self.province = Tkinter.StringVar()  self.city = Tkinter.StringVar()  self.street = Tkinter.StringVar()  self.zipcode = Tkinter.StringVar()  self.createWidgets()     # 構造用戶界面   def cre




Copyright © Linux教程網 All Rights Reserved