1. 程式人生 > >java設計模式之【工廠模式】的作用

java設計模式之【工廠模式】的作用

在設計模式的教學和推廣過程中,很多企業學員和在校學生經常問我,工廠模式(包括簡單工廠模式、工廠方法模式和抽象工廠模式)到底有什麼用,很多時候通過反射機制就可以很靈活地建立物件,為毛還要工廠?微笑,在本文中我將圍繞建立物件和使用物件來簡單談談工廠的作用。

      與一個物件相關的職責通常有三類:物件本身所具有的職責、建立物件的職責和使用物件的職責。物件本身的職責比較容易理解,就是物件自身所具有的一些資料和行為,可通過一些公開的方法來實現它的職責。在本文中,我們將簡單討論一下物件的建立職責和使用職責。

      在Java語言中,我們通常有以下幾種建立物件的方式:

       (1) 使用new關鍵字直接建立物件;

       (2) 通過反射機制建立物件;

       (3) 通過clone()方法建立物件;

       (4) 通過工廠類建立物件。

      毫無疑問,在客戶端程式碼中直接使用new關鍵字是最簡單的一種建立物件的方式,但是它的靈活性較差,下面通過一個簡單的示例來加以說明: 

  1. class LoginAction {  
  2.     private UserDAO udao;  
  3.     public LoginAction() {  
  4.         udao = new JDBCUserDAO(); //建立物件
  5.     }  
  6.     publicvoid execute() {  
  7.         //其他程式碼
  8.         udao.findUserById(); //使用物件
  9.         //其他程式碼
  10.     }  
  11. }  

      LoginAction類中定義了一個UserDAO型別的物件udao,在LoginAction的建構函式中建立了JDBCUserDAO型別的udao物件,並在execute()方法中呼叫了udao物件的findUserById()方法,這段程式碼看上去並沒有什麼問題。下面我們來分析一下LoginActionUserDAO之間的關係,LoginAction類負責建立了一個UserDAO子類的物件並使用UserDAO的方法來完成相應的業務處理,也就是說LoginAction

即負責udao的建立又負責udao的使用,建立物件和使用物件的職責耦合在一起,這樣的設計會導致一個很嚴重的問題:如果在LoginAction中希望能夠使用UserDAO的另一個子類如HibernateUserDAO型別的物件,必須修改LoginAction類的原始碼,違反了“開閉原則”。如何解決該問題?

      最常用的一種解決方法是將udao物件的建立職責從LoginAction類中移除,在LoginAction類之外建立物件,那麼誰來負責建立UserDAO物件呢?答案是:工廠類。通過引入工廠類,客戶類(如LoginAction)不涉及物件的建立,物件的建立者也不會涉及物件的使用。引入工廠類UserDAOFactory之後的結構如圖1所示:

引入工廠類之後的結構圖

       工廠類的引入將降低因為產品或工廠類改變所造成的維護工作量。如果UserDAO的某個子類的建構函式發生改變或者要需要新增或移除不同的子類,只要維護UserDAOFactory的程式碼,而不會影響到LoginAction;如果UserDAO的介面發生改變,例如新增、移除方法或改變方法名,只需要修改LoginAction,不會給UserDAOFactory帶來任何影響。

在所有的工廠模式中,我們都強調一點:兩個類AB之間的關係應該僅僅是A建立B或是A使用B,而不能兩種關係都有。將物件的建立和使用分離,也使得系統更加符合“單一職責原則”,有利於對功能的複用和系統的維護。

此外,將物件的建立和使用分離還有一個好處:防止用來例項化一個類的資料和程式碼在多個類中到處都是,可以將有關建立的知識搬移到一個工廠類中,這在Joshua Kerievsky的《重構與模式》一書中有專門的一節來進行介紹。因為有時候我們建立一個物件不只是簡單呼叫其建構函式,還需要設定一些引數,可能還需要配置環境,如果將這些程式碼散落在每一個建立物件的客戶類中,勢必會出現程式碼重複、建立蔓延的問題,而這些客戶類其實無須承擔物件的建立工作,它們只需使用已建立好的物件就可以了。此時,可以引入工廠類來封裝物件的建立邏輯和客戶程式碼的例項化/配置選項。

      使用工廠類還有一個“不是特別明顯的”優點,一個類可能擁有多個建構函式,而在JavaC#等語言中建構函式名字都與類名相同,客戶端只能通過傳入不同的引數來呼叫不同的建構函式建立物件,從建構函式和引數列表中也許大家根本不瞭解不同建構函式所構造的產品的差異。但如果將物件的建立過程封裝在工廠類中,我們可以提供一系列名字完全不同的工廠方法,每一個工廠方法對應一個建構函式,客戶端可以以一種更加可讀、易懂的方式來建立物件,而且,從一組工廠方法中選擇一個意義明確的工廠方法,比從一組名稱相同引數不同的建構函式中選擇一個建構函式要方便很多。如圖2所示:

       在圖2中,矩形工廠類RectangleFactory提供了兩個工廠方法createRectangle()createSquare(),一個用於建立長方形,一個用於建立正方形,這兩個方法比直接通過建構函式來建立長方形或正方形物件意義更加明確,也在一定程度上降低了客戶端呼叫時出錯的概率。

      那麼,有人可能會問,是否需要為設計中的每一個類都配備一個工廠類?答案是:具體情況具體分析。如果產品類很簡單,而且不存在太多變數,其構造過程也很簡單,此時無須為其提供工廠類,直接在使用之前例項化即可,例如Java語言中的String類,我們就無須為它專門提供一個StringFactory,這樣做反而有點像殺雞用牛刀,大材小用,而且會導致工廠氾濫,增加系統的複雜度。


https://blog.csdn.net/lovelion/article/details/7523392