模式的定義java
工廠方法模式使用的頻率很是高,在咱們平常的開發中總能見到它的身影。其定義爲:數據庫
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定義一個用於建立對象的接口,讓子類決定實例化哪個類。工廠方法使一個類的實例化延遲到其子類。),類圖以下:設計模式
在工廠方法模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定義;Creator爲抽象建立類,也就是抽象工廠,具體如何建立產品類是由具體的實現工廠ConcreteCreator完成的。工廠方法模式的變種較多,咱們來看一個比較實用的通用源碼。緩存
抽象類產品代碼框架
public abstract class Product { //產品類的公共方法 public void method1(){ //業務邏輯處理 } //抽象方法 public abstract void method2(); }
具體的產品類能夠有多個,都繼承於抽象產品類,其源代碼以下所示:ide
public class ConcreteProduct1 extends Product { public void method2() { //業務邏輯處理 } } public class ConcreteProduct2 extends Product { public void method2() { //業務邏輯處理 } }
抽象工廠類負責定義產品對象的產生,源代碼以下所示:函數
public abstract class Creator { /* * 建立一個產品對象,其輸入參數類型能夠自行設置 * 一般爲String、Enum、Class等,固然也能夠爲空 */ public abstract <T extends Product> T createProduct(Class<T> c); } }
具體如何產生一個產品的對象,是由具體的工廠類實現的,以下所示:設計
public class ConcreteCreator extends Creator { public <T extends Product> T createProduct(Class<T> c){ Product product=null; try { product = (Product)Class.forName(c.getName()).newInstance(); } catch (Exception e) { //異常處理 } return (T)product; } }
場景類的調用方法以下所示:code
public class Client { public static void main(String[] args) { Creator creator = new ConcreteCreator(); Product product = creator.createProduct(ConcreteProduct1.class); /* * 繼續業務處理 */ } }
該通用代碼是一個比較實用、易擴展的框架,咱們能夠根據實際項目須要進行擴展。對象
工廠方法模式的優勢
首先,良好的封裝性,代碼結構清晰。一個對象建立是有條件約束的,如一個調用者須要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就能夠了,不用知道建立對象的艱辛過程,下降模塊間的耦合。
其次,工廠方法模式的擴展性很是優秀。在增長產品類的狀況下,只要適當地修改具體的工廠類或擴展一個工廠類,就能夠完成「擁抱變化」。
再次,屏蔽產品類。這一特色很是重要,產品類的實現如何變化,調用者都不須要關心,它只須要關心產品的接口,只要接口保持不變,系統中的上層模塊就不要發生變化。由於產品類的實例化工做是由工廠類負責的,一個產品對象具體由哪個產品生成是由工廠類決定的。在數據庫開發中,你們應該可以深入體會到工廠方法模式的好處:若是使用JDBC鏈接數據庫,數據庫從MySQL切換到Oracle,須要改動的地方就是切換一下驅動名稱(前提條件是SQL語句是標準語句),其餘的都不須要修改,這是工廠方法模式靈活性的一個直接案例。
最後,工廠方法模式是典型的解耦框架。高層模塊值須要知道產品的抽象類,其餘的實現類都不用關心,符合迪米特法則,我不須要的就不要去交流;也符合依賴倒置原則,只依賴產品類的抽象;固然也符合里氏替換原則,使用產品子類替換產品類,沒問題!
工廠方法模式的擴展
工廠方法模式有不少擴展,並且與其餘模式結合使用威力更大,下面將介紹4種擴展。
縮小爲簡單工廠模式:咱們這樣考慮一個問題:一個模塊僅須要一個工廠類,沒有必要把它產生出來,使用靜態的方法就能夠了,根據這一要求,咱們去掉工程類Creator,將具體工廠類的建立方法改爲靜態的就能夠了。該模式是工廠方法模式的弱化,由於簡單,因此稱爲簡單工廠模式(Simple Factory Pattern),也叫作靜態工廠模式。在實際項目中,採用該方法的案例仍是比較多的,其缺點是工廠類的擴展比較困難,不符合開閉原則,但它仍然是一個很是實用的設計模式。
升級爲多個工廠類:當咱們在作一個比較複雜的項目時,常常會遇到初始化一個對象很耗費精力的狀況,全部的產品類都放到一個工廠方法中進行初始化會使代碼結構不清晰。例如,一個產品類有5個具體實現,每一個實現類的初始化(不只僅是new,初始化包括new一個對象,並對對象設置必定的初始值)方法都不相同,若是寫在一個工廠方法中,勢必會致使該方法巨大無比,那該怎麼辦?考慮到須要結構清晰,咱們就爲每一個產品定義一個創造者,而後由調用者本身去選擇與哪一個工廠方法關聯。注意,抽象方法中已經再也不須要傳遞相關參數了,由於每個具體的工廠都已經很是明確本身的職責:建立本身負責的產品類對象。
這樣用的好處就是建立類的職責清晰,並且結構簡單,可是給可擴展性和可維護性帶來了必定的影響。爲何這麼說呢?若是要擴展一個產品類,就須要創建一個相應的工廠類,這樣就增長了擴展的難度。由於工廠類和產品類的數量相同,維護時須要考慮兩個對象之間的關係。固然,在複雜的應用中通常採用多工廠的方法,而後再增長一個協調類,避免調用者與各個子工廠交流,協調類的做用是封裝子工廠類,對高層模塊提供統一的訪問接口。
替代單例模式:單例模式的核心要求就是在內存中只有一個對象,經過工廠方法模式也能夠只在內存中生產一個對象,類圖以下所示
很是簡單的類圖,Singleton定義了一個private的無參構造函數,目的是不容許經過new的方式建立一個對象,代碼以下所示
public class Singleton { //不容許經過new產生一個對象 private Singleton(){ } public void doSomething(){ //業務處理 } }
Singleton保證不能經過正常的渠道創建一個對象,那SingletonFactory如何創建一個單例對象呢?答案是經過反射方式建立,代碼以下
public class SingletonFactory { private static Singleton singleton; static{ try { Class cl= Class.forName(Singleton.class.getName()); //得到無參構造 Constructor constructor=cl.getDeclaredConstructor(); //設置無參構造是可訪問的 constructor.setAccessible(true); //產生一個實例對象 singleton = (Singleton)constructor.newInstance(); } catch (Exception e) { //異常處理 } } public static Singleton getSingleton(){ return singleton; } }
經過得到類構造器,而後設置訪問權限,生成一個對象,而後提供外部訪問,保證內存中的對象惟一。固然,其餘類也能夠經過反射的方式創建一個單例對象,確實如此,可是一個項目或團隊是有章程和規範的,況且已經提供了一個得到單例對象的方法,爲何還要從新建立一個新對象呢?除非是有人做惡。
延遲初始化:何爲延遲初始化(Lazy initialization)?一個對象被消費完畢後,並不馬上釋放,工廠類保持其初始狀態,等待再次被使用。延遲初始化是工廠方法模式的一個擴展應用,其通用類圖如
ProductFactory負責產品類對象的建立工做,而且經過prMap變量產生一個緩存,對須要再次被重用的對象保留,Product和ConcreteProduct是一個示例代碼,參考代碼以下
public class ProductFactory { private static final Map<String,Product> prMap = new HashMap(); public static synchronized Product createProduct(String type) throws Exception{ Product product =null; //若是Map中已經有這個對象 if(prMap.containsKey(type)){ product = prMap.get(type); }else{ if(type.equals("Product1")){ product = new ConcreteProduct1(); }else{ product = new ConcreteProduct2(); } //同時把對象放到緩存容器中 prMap.put(type,product); } return product; } }
代碼還比較簡單,經過定義一個Map容器,容納全部產生的對象,若是在Map容器中已經有的對象,則直接取出返回;若是沒有,則根據須要的類型產生一個對象並放入到Map容器中,以方便下次調用。延遲加載框架是能夠擴展的,例如限制某一個產品類的最大實例化數量,能夠經過判斷Map中已有的對象數量來實現,這樣的處理是很是有意義的,例如JDBC鏈接數據庫,都會要求設置一個MaxConnections最大鏈接數量,該數量就是內存中最大實例化的數量。延遲加載還能夠用在對象初始化比較複雜的狀況下,例如硬件訪問,涉及多方面的交互,則能夠經過延遲加載下降對象的產生和銷燬帶來的複雜性。