首先來解決一個常見的疑問:可能有朋友會認爲,上面示例中的簡單工廠看起來不就是把客戶端裏面的「new Impl()」移動到簡單工廠裏面嗎?不仍是同樣經過new一個實現類來獲得接口嗎?把「new Impl()」這句話放到客戶端和放到簡單工廠裏面有什麼不一樣嗎?
理解這個問題的重點就在於理解簡單工廠所處的位置。
根據前面的學習,咱們知道接口是用來封裝隔離具體的實現的,目標就是不要讓客戶端知道封裝體內部的具體實現。簡單工廠的位置是位於封裝體內的,也就是簡單工廠是跟接口和具體的實如今一塊兒的,算是封裝體內部的一個類,因此簡單工廠知道具體的實現類是沒有關係的。整理一下簡單工廠的結構圖,新的圖如圖7所示:
圖7 整理後的簡單工廠結構
html
圖7中虛線框,就比如是一個組件的包裝邊界,表示接口、實現類和工廠類組合成了一個組件,在這個封裝體裏面,只有接口和工廠是對外的,也就是讓外部知道並使用的,因此故意漏了一些在虛線框外,而具體的實現類是不對外的,被徹底包含在虛線框內。
對於客戶端而言,只是知道了接口Api和簡單工廠Factory,經過Factory就能夠得到Api了,這樣就達到了讓Client在不知道具體實現類的狀況下獲取接口Api。
因此看似簡單的把「new Impl()」這句話從客戶端裏面移動到了簡單工廠裏面,實際上是有了質的變化的。java
(1)簡單工廠的功能
工廠嘛,就是用來造東西的。在Java裏面,一般狀況下是用來造接口的,可是也能夠造抽象類,甚至是一個具體的類實例。
必定要注意,雖然前面的示例是利用簡單工廠來建立的接口,可是也是能夠用簡單工廠來建立抽象類或者是普通類的實例的。
(2)靜態工廠
使用簡單工廠的時候,一般不用建立簡單工廠類的類實例,沒有建立實例的必要。所以能夠把簡單工廠類實現成一個工具類,直接使用靜態方法就能夠了,也就是說簡單工廠的方法一般都是靜態的,因此也被稱爲靜態工廠。若是要防止客戶端無謂的創造簡單工廠實例,還能夠把簡單工廠的構造方法私有化了。
(3)萬能工廠
一個簡單工廠能夠包含不少用來構造東西的方法,這些方法能夠創造不一樣的接口、抽象類或者是類實例,一個簡單工廠理論上能夠構造任何東西,因此又稱之爲「萬能工廠」。
雖然上面的實例中,在簡單工廠裏面只有一個方法,但事實上,是能夠有不少這樣建立方法的,這點要注意。
(4)簡單工廠建立對象的範圍
雖然從理論上講,簡單工廠什麼都能造,但對於簡單工廠可建立對象的範圍,一般不要太大,建議控制在一個獨立的組件級別或者一個模塊級別,也就是一個組件或模塊一個簡單工廠。不然這個簡單工廠類會職責不明,有點大雜燴的感受。
(5)簡單工廠的調用順序示意圖
簡單工廠的調用順序如圖8所示:
圖8 簡單工廠的調用順序示意圖數據庫
(6)簡單工廠命名的建議編程
雖說簡單工廠的方法可能是用來造接口的,可是仔細分析就會發現,真正能實現功能的是具體的實現類,這些實現類是已經作好的,並非真的靠簡單工廠來創造出來的,簡單工廠的方法無外乎就是:實現了選擇一個合適的實現類來使用。
因此簡單工廠方法的內部主要實現的功能是「選擇合適的實現類」來建立實例對象。既然要實現選擇,那麼就須要選擇的條件或者是選擇的參數,選擇條件或者是參數的來源一般又有幾種:設計模式
下面來看個示例,看看由客戶端來傳入參數,如何寫簡單工廠中的方法。
(1)在剛纔的示例上再添加一個實現,稱爲Impl2,示例代碼以下:api
/** * 對接口的一種實現 */ public class Impl2 implements Api{ public void test1(String s) { System.out.println("Now In Impl The input s=="+s); } }
(2)如今對Api這個接口,有了兩種實現,那麼工廠類該怎麼辦呢?到底如何選擇呢?不可能兩個同時使用吧,看看新的工廠類,示例代碼以下:緩存
/** * 工廠類,用來創造Api的 */ public class Factory { /** * 具體的創造Api的方法,根據客戶端的參數來建立接口 * @param type 客戶端傳入的選擇創造接口的條件 * @return 創造好的Api對象 */ public static Api createApi(int type){ //這裏的type也能夠不禁外部傳入,而是直接讀取配置文件來獲取 //爲了把注意力放在模式自己上,這裏就不去寫讀取配置文件的代碼了 //根據type來進行選擇,固然這裏的1和2應該作成常量 Api api = null; if(type==1){ api = new Impl(); }else if(type==2){ api = new Impl2(); } return api; } }
(3)客戶端沒有什麼變化,只是在調用Factory的createApi方法的時候須要傳入參數,示例代碼以下:工具
public class Client { public static void main(String[] args) { //注意這裏傳遞的參數,修改參數就能夠修改行爲,試試看吧 Api api = Factory.createApi(2); api.test1("哈哈,沒關係張,只是個測試而已!"); } }
(4)要注意這種方法有一個缺點
因爲是從客戶端在調用工廠的時候,傳入選擇的參數,這就說明客戶端必須知道每一個參數的含義,也須要理解每一個參數對應的功能處理。這就要求必須在必定程度上,向客戶暴露必定的內部實現細節。學習
如今已經學會經過簡單工廠來選擇具體的實現類了,但是還有問題。好比:在如今的實現中,再新增長一種實現,會怎樣呢?
那就須要修改工廠類,才能把新的實現添加到現有系統中。好比如今新加了一個實現Impl3,那麼須要相似下面這樣來修改工廠類:測試
public class Factory { public static Api createApi(int type){ Api api = null; if(type==1){ api = new Impl(); }else if(type==2){ api = new Impl2(); } else if(type==3){ api = new Impl3(); } return api; } }
每次新增長一個實現類都來修改工廠類的實現,確定不是一個好的實現方式。那麼如今但願新增長了實現類事後不修改工廠類,該怎麼辦呢?
一個解決的方法就是使用配置文件,當有了新的實現類事後,只要在配置文件裏面配置上新的實現類就行了,在簡單工廠的方法裏面可使用反射,固然也可使用IoC/DI(控制反轉/依賴注入,這個不在這裏討論)來實現。
看看如何使用反射加上配置文件,來實現添加新的實現類事後,無須修改代碼,就能把這個新的實現類加入應用中。
(1)配置文件用最簡單的properties文件,實際開發中可能是xml配置。定義一個名稱爲「FactoryTest.properties」的配置文件,放置到Factory同一個包下面,內容以下:
ImplClass=cn.javass.dp.simplefactory.example5.Impl
若是新添加了實現類,修改這裏的配置就能夠了,就不須要修改程序了。
(2)此時的工廠類實現以下:
/** * 工廠類,用來創造Api對象 */ public class Factory { /** * 具體的創造Api的方法,根據配置文件的參數來建立接口 * @return 創造好的Api對象 */ public static Api createApi(){ //直接讀取配置文件來獲取須要建立實例的類 //至於如何讀取Properties,還有如何反射這裏就不解釋了 Properties p = new Properties(); InputStream in = null; try { in = Factory.class.getResourceAsStream( "FactoryTest.properties"); p.load(in); } catch (IOException e) { System.out.println( "裝載工廠配置文件出錯了,具體的堆棧信息以下:"); e.printStackTrace(); }finally{ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } //用反射去建立,那些例外處理等完善的工做這裏就不作了 Api api = null; try { api = (Api)Class.forName(p.getProperty("ImplClass")) .newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return api; } }
(3)此時的客戶端就變得很簡單了,再也不須要傳入參數,代碼示例以下:
public class Client { public static void main(String[] args) { Api api = Factory.createApi(); api.test1("哈哈,沒關係張,只是個測試而已!"); } }
把上面的示例代碼敲到電腦裏面,測試一下,體會體會。
1:簡單工廠的本質
簡單工廠的本質是:選擇實現。
注意簡單工廠的重點在選擇,實現是已經作好了的。就算實現再簡單,也要由具體的實現類來實現,而不是在簡單工廠裏面來實現。簡單工廠的目的在於爲客戶端來選擇相應的實現,從而使得客戶端和實現之間解耦,這樣一來,具體實現發生了變化,就不用變更客戶端了,這個變化會被簡單工廠吸取和屏蔽掉。
實現簡單工廠的難點就在於 「如何選擇」實現,前面講到了幾種傳遞參數的方法,那都是靜態的參數,還能夠實現成爲動態的參數。好比:在運行期間,由工廠去讀取某個內存的值,或者是去讀取數據庫中的值,而後根據這個值來選擇具體的實現等等。
2:什麼時候選用簡單工廠
建議在以下狀況中,選用簡單工廠:
簡單工廠模式結束,謝謝觀賞
---------------------------------------------------------------------------
私塾在線學習網原創內容 跟着cc學設計系列 之 研磨設計模式
原創內容,轉載請註明出處【http://sishuok.com/forum/blogPost/list/117.html】
---------------------------------------------------------------------------