java工厂方法_Java设计模式之工厂方法模式
2.工廠方法模式
《設計模式之禪》中用神話故事女媧造人來比概述工廠方法模式。
女媧采集黃土捏成人的形狀,然后放到八卦爐中燒制,最后放置到大地上生長,工藝過程是沒有錯的,但是意外隨時都會發生:
第一次烤泥人,感覺應該熟了,往大地上一放,哇,沒烤熟!于是一個白人誕生了!(這也是缺乏經驗的最好證明。)
第二次烤泥人,上一次沒烤熟,這次多烤一會兒,放到世間一看,嘿,熟過頭了,于是黑人誕生了!
第三次烤泥人,一邊燒制一邊察看,直到表皮微黃,嘿,剛剛好,于是黃色人種出現 了!
該過程涉及三個對象:女媧、八卦爐、三種不同膚色的人。女媧可以使用場景類Client來表示,八卦爐類似于一個工廠,負責制造生產產品(即人類),三種不同膚色的人,他們都是同一個接口下不同實現類,都是人,只是膚色語言不同,對于八卦爐來說都是它生產出的產品。分析出如下所示類圖:
類圖比較簡單,AbstractHumanFactory是一個抽象類,定義了一個八卦爐具有的整體功能,HumanFactory為實現類,完成具體的任務——創建人類;Human接口是人類的總稱,其三個實現類分別為三類人種;NvWa類是一個場景類,負責模擬這個場景,執行相關的任務。
我們定義的沒個人中都有兩個方法:getColor(獲得人的皮膚顏色)和talk(交談),代碼如下:
//人類接口
public interfaceHuman{//沒個人種的皮膚都有相應的顏色
public voidgetColor();//人類會說話
public voidtalk();
}
接口Human是對人類的總稱,每個人種都至少具有兩個方法,黑色人種、黃色人種、白色人種的代碼如下:
//黑色人種
public class BlackHuman implementsHuman{public voidgetColor(){
System.out.println("黑色人種的皮膚顏色是黑色的!");
}public voidtalk(){
System.out.println("黑人會說話,一般人聽不懂!");
}
}//黃色人種
public class YellowHuman implementsHuman{public voidgetColor(){
System.out.println("黃色人種的皮膚顏色是黃色的!");
}public voidtalk(){
System.out.println("黃色人種會說話,一般說的 都是雙字節!");
}
}//白色人種
public class WhiteHuman implementsHuman{public voidgetColor(){
System.out.println("白色人種的皮膚顏色是白色的!");
}public voidtalk(){
System.out.println("白色人種會說話,一般都是單字節!");
}
}
//創建人類抽象工廠
public abstract classAbstractHumanFactory{//這里采用了泛型,對createHuman的輸入參數產生兩層限制//1.必須是Class類型//2.必須是Human的實現類
public abstract T createHuman(Classc);
}//創建人類工廠
public class HumanFactory extendsAbstractHumanFactory{public T createHuman(Classc){//定義一個生產的人種
Human human = null;try{//產生一個人種
human =(T)Class.forName(c.getName()).newInstance();
}catch(Exception e){
System.out.println("人種生成錯誤!");
}return(T)human;
}
}
//女媧類
pubic classNvWa{public static voidmain(String[] args){//聲明陰陽八卦爐
AbstractHumanFactory YinYangLu = newHumanFactory();//女媧第一次造人,火候不足,于是白人產生了
System.out.println("造出來的第一批人是白色人種");
Human whiteHuman= YinYangLu.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();//女媧第二次造人, 火候過足, 于是黑人產生了
System.out.println("造出的第二批人是黑色人種");
Human blackHuman= YinYangLu.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();//第三次造人, 火候剛剛好, 于是黃色人種產生了
System.out.println("造出的第三批人是黃色人種");
Human yellowHuman= YinYangLu.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
結果:
造出的第一批人是白色人種
白色人種的皮膚顏色是白色的!
白色人種會說話, 一般都是單字節。
造出的第二批人是黑色人種
黑色人種的皮膚顏色是黑色的!
黑人會說話, 一般人聽不懂。
造出的第三批人是黃色人種
黃色人種的皮膚顏色是黃色的!
黃色人種會說話, 一般說的都是雙字節。
2.1.工廠方法模式的定義
工廠模式使用的頻率非常高,在我們日常的開發中總能見到它的身影。其定義為:
Define an interface for creating an object, but let subclass decide which class to instantiate. Factory Method lets a class defer instantiate to subclasses(定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法是一個類的實例化延遲到其子類)
工廠模式的通用類圖如下:
在工廠方法模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定義;Creator為抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體實現工廠ConcreteCreator完成的。
2.2.工廠方法模式常見的應用場景
?1.工廠方法模式是new一個對象的替代品,所以在所有需要生成對象的地方都可以使用,但是需要慎重地考慮是否要增加一個工廠類進行管理,增加代碼的復雜度。
?2.需要靈活的、可擴展的框架時,可以考慮采用工廠方法模式。萬物皆對象,那萬物也就皆產品類,例如需要設計一個連接郵件服務器的框架,有三種網絡協議可供選擇:POP3、IMAP、HTTP,我們就可以把這三種連接方法作為產品類,定義一個接口如IConnectMail,然后定義對郵件的操作方法,用不同的方法實現三個具體的產品類(也就是連接方式)再定義一個工廠方法,按照不同的傳入條件,選擇不同的連接方式。如此設計,我們做到完美的擴展,如某些郵件服務器提供了WebService接口,很好,我們只要增加一個產品類就可以了。
?3.工廠方法模式可以用在異構項目中,例如通過WebService與一個非Java項目交互,雖然WebService號稱是可以做到異構系統的同構化,但是在實際的開發中,還是會碰到很多問題,如類型問題、WSDL文件的支持問題,等等。從WSDL中產生的對象都認為是一個產品,然后由一個具體的工廠類進行管理,減少與外圍系統的耦合。
?4.可以使用在測試驅動開發的框架下。例如,測試一個類A,就需要把與類A有關聯關系的類B也同時產生出來,我們可以使用工廠方法模式把類B虛擬出來,避免類A與類B的耦合。目前由于JMock和EasyMock的誕生,改使用場景已經弱化了,遇到這種情況時直接考慮使用JMock或EasyMock。
2.3.工廠方法模式的優點
??1.良好的封裝性,代碼結構清晰。一個對象創建是有條件約束的,如一個調用者需要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就可以了,不用知道創建對象的艱辛過程,降低模塊間的耦合。
2.工廠方法模式的擴展性非常優秀。在增加產品類的情況下,只要適當地修改具體的工廠類或擴展一個工廠類,就可以完成"擁抱變化"。例如在我們的例子中,需要增加一個棕色人種,則只需添加一個BrownHuman類,工廠類不用任何修改就可完成系統擴展。
?3.屏蔽產品類。這一特點非常重要,產品類的實現如何變化,調用者都不需要關心,他只需關心產品的接口,只要接口保持不變,系統中的上層模塊就不要發生變化。因為產品類的實例化工作是有工廠類負責的,一個產品對象具體由哪一個產品生成是由工廠類決定的。在數據庫開發中,能夠深切的體會到工廠方法模式的好處:如果使用JDBC連接數據庫。數據庫從MySQL切換到Oracle,需要改動的地方就是切換一下數據庫驅動(前提條件是SQL語句是標準語句),其他的都不需要修改,這是工廠方法模式靈活性的一個直接實例。
?4.工廠方法模式是典型的解耦框架。高層模塊值需要知道產品的抽象類,其他的實現類都不用關心,符合迪米特法則,不需要的就不要去交流;也符合依賴倒置原則,只依賴產品的抽象,當然也符合里氏替換原則,使用產品子類替換產品父類。
2.4.單例模式的代碼實現
抽象產品類代碼如下:
//抽象產品類
public abstract classProduct{//產品類的公共方法
public voidmethod1(){//業務邏輯處理
}//抽象方法
public abstract voidmethod2();
}
具體的產品類??梢杂卸鄠€,都繼承于抽象產品類。其代碼如下:
//產品1繼承自抽象產品類,實現抽象方法
public class ConcreteProduct1 extendsProduct{public voidmethod2(){//具體的業務邏輯
}
}//產品2繼承自抽象產品類,實現抽象方法
public class ConcreteProduct2 extendsProduct{public voidmethod2(){//具體的業務邏輯
}
}
抽象工廠類負責定義產品對象的產生,代碼如下:
//抽象工廠類
public abstract classCreator{//創建一個產品對象,其輸入參數類型可以自行設置。//通常為String、Enum、Class等,也可以為空
public abstract T createProduct(Classc);
}
具體如何產生一個產品的對象,是由具體的工廠類實現的,代碼如下:
//具體工廠類
public class ConcreteCreator extendsCreator{public T createProduct(Classc){
Product product= null;try{
product=(Product)Class.forName(c.getName()).newInstance();
}catch(Exception e){//異常處理
}return(T)product;
}
}
場景類的調用方法代碼如下:
//場景類
public classClient{public static voidmain(String[] args){
Creator creator= newConcreteCreator();
Product product= creator.createProduct(ConcreteProduct1.class);//繼續業務邏輯
}
}
該通用代碼是一個比較實用、易于擴展的框架,讀者可以根據實際項目需要進行擴展
2.5.工廠方法模式的擴展
工廠方法模式有很多擴展,而且與其他模式結合實用威力更大。下面是工廠方法模式的四種擴展。
2.5.1.縮小為簡單工廠模式
我們這樣考慮一個問題,一個模塊僅需要一個工廠類,沒有必要把它生產出來,使用靜態的方法就可以了,根據這一要求,我們把上例中的AbstractHumanFactory修改一下,類圖如下:
在類圖中去掉了AbstractHumanFactory抽象類,同時把createHuman方法設置為靜態類型,簡化了類的創建過程,變更的代碼僅僅是HumanFactory和NvWa類,HumanFactory代碼如下:
//簡單工廠模式中的工廠類
public classHumanFactory{public static T createHuman(Classc){//定義一個生產的人種
Human human = null;try{//產生一個人種
human =(T)Class.forName(c.getName()).newInstance();
}catch(Exception e){
System.out.println("人種生成錯誤!");
}return(T)human;
}
}
HumanFactory類僅有兩個地方發生變化:去掉繼承抽象類,并在createHuman前增加static關鍵字:工廠類發生變化,同時也引起了調用者NvWa的變化。代碼如下:
//女媧類
pubic classNvWa{public static voidmain(String[] args){//女媧第一次造人,火候不足,于是白人產生了
System.out.println("造出來的第一批人是白色人種");
Human whiteHuman= HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();//女媧第二次造人, 火候過足, 于是黑人產生了
System.out.println("造出的第二批人是黑色人種");
Human blackHuman= HumanFactory.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();//第三次造人, 火候剛剛好, 于是黃色人種產生了
System.out.println("造出的第三批人是黃色人種");
Human yellowHuman= HumanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
運行結果沒有發生變化,但是類圖變簡單了,而且調用者也比較簡單,該模式是工廠方法模式的弱化,因為簡單,所以稱為簡單工廠模式(Simple Factory Pattern),也叫作靜態工廠模式。在實際項目中,采用該該方法的案例還是比較多的,其缺點是工廠類擴展比較困難,不符合開閉原則,但它仍然是一個非常實用的設計模式。
2.5.2.升級為多個工廠類
當我們在做一個比較復雜的項目時,經常會遇到初始化一個對象很耗費精力的情況,所有的產品類都放在一個工廠方法中進行初始化會使代碼結構不清晰。例如,一個產品類有5個具體實現,每個實現類的初始化(不僅僅是new,初始化包括new一個對象,并對對象設置一定的初始值)方法都不相同,如果寫在一個工廠方法中,勢必會導致該方法巨大無比
考慮到需要結構清晰,我們就為每個產品定義一個創造者,然后由調用者自己去選擇與那個工廠方法關聯。我們還是以女媧造人為例,每個人種都有一個固定的八卦爐,分別造出黑色人種、白色人種、黃色人種,修改后類圖如下:
每個人種(具體的產品類)都對應了一個創建者,每個創建者都獨立負責創建對應的產品對象,非常符合單一職責原則,按照這種模式代碼如下:
//多工廠模式的抽象工廠類
public abstract classAbstractHumanFactory{//抽象方法中已經不再需要傳遞相關參數了,因為每一個具體的工廠都已經非常明確自己的職責:創建自己負責的產品類對象。
public abstractHuman createHuman();
}//黑色人種的創建工廠實現
public class BlackHumanFactory extendsAbstractHumanFactory{publicHuman createHuman(){return newBlackHuman();
}
}//黃色人種的創建工廠實現
public class YellowHumanFactory extendsAbstractHumanFactory{publicHuman createHuman(){return newYellowHuman();
}
}//白色人種的創建工廠實現
public class WhiteHumanFactory extendsAbstractHumanFactory{publicHuman createHuman(){return newWhiteHuman();
}
}
三個具體的創建工廠都非常簡單,但是,如果一個系統比較復雜時工廠類也會相應地變復雜。場景類NvWa修改后的代碼如下:
//場景類NvWa
public classNvWa{public static voidmain(String[] args){//女媧第一次造人,火候不足,于是白色人種產生了
System.out.println("造出的第一批人是白色人種");
Human whiteHuman= (newWhiteHumanFactory()).createHuman();
whiteHuman.getColor();
whiteHuman.talk();//女媧第二次造人,火候過足,于是黑色人種產生了
System.out.println("造出的第二批人是黑色人種");
Human blackHuman= (newBlackHumanFactory()).createHuman();
blackHuman.getColor();
blackHuman.talk();//女媧第三次造人,火候剛剛好,于是黃色人種產生了
System.out.println("造出的第三批人是黃色人種");
Human yellowHuman= (newYellowHumanFactory()).createHuman();
yellowHuman.getColor();
yellowHuman.talk();
}
}
運行結果還是相同。每一個產品類都對應了一個創建類,好處就是創建類的職責清晰,而且結構簡單,但是給可擴展性和可維護性帶來了一定的影響。如果要擴展一個產品類,就需要建立一個相應的工廠類,這樣就增加了擴展的難度。因為工廠類和產品類的數量相同,維護時需要考慮兩個對象之間的關系。當然,在復雜的應用中一般采用多工廠的方法,然后再增加一個協調類,避免調用者與 各個子工廠交流,協調類的作用是封裝子工廠類,對高層模塊提供統一的訪問接口。
2.5.3.替代單例模式
單例模式以及多例模式都有一定的缺點,我們可以采用工廠方法模式來實現單例的功能。單例模式的核心要求就是在內存中只有一個對象,通過工廠方法模式也可以只在內存中生產一個對象。類圖如下:
Singleton定義了一個private的無參構造函數,目的是不允許通過new的方式創建一個對象,代碼如下:
//單例類
public classSingleton{//不允許通過new產生一個對象
privateSingleton(){
}public voiddoSomething(){//業務處理
}
}
Singleton保證不能通過正常的渠道建立一個對象,那么SingletonFactory需要通過反射的方式創建。
//負責生成單例的工廠類
public classSingletonFactory{private staticSingleton 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 staticSingleton getSingleton(){returnsingleton;
}
}
通過獲得類構造器,然后設置訪問權限,生成一個對象,然后提供外部訪問,保證內存中對象的唯一。當然其他類也可以通過反射的方式建立一個單例對象。
以上通過工廠方法模式創建了一個單例對象,該框架可以繼續擴展,在一個項目中可以產生一個單例構造器,所有需要產生單例的類都遵循一定的規則(構造方法是private),然后通過擴展該框架,只需輸入一個類型就可以獲取唯一的實例。
2.5.4.延遲初始化
何為延遲初始化(Lazy initialization)?一個對象被消費完畢后,并不立刻釋放,工廠類保持其初始化狀態,等待再次被使用。延遲初始化是工廠方法模式的一個擴展應用,其通用類圖如下:
ProductFactory負責產品類對象的創建工作,并且通過prMap變量產生一個緩存,對需要再次被重用的對象保留,Product和ConcreteProduct是一個示例代碼,代碼如下:
ProductFactory代碼如下:
//延遲加載的工廠類
public classProductFactory{private static final Map prMap = newHashMap();public static synchronized Product createProduct(String type) throwsException(){
Product product= null;//如果Map中已經有這個對象
if(prMap.containsKey(type)){
product=prMap.get(type);
}else{if(type.equals("Product1")){
product= newconcreteProduct1();
}else{
product= newconcreteProduct2();
}//同時把對象放到緩存容器中
prMap.put(type,product);
}returnproduct;
}
}
代碼比較簡單,通過定義一個Map容器,容納所有產生的對象,如果在Map容器中已經有的對象,則直接取出返回;如果沒有,則根據需要的類型產生一個對象并放到Map容器中,以方便下次調用。
延遲加載框架是可以擴展的,例如限制某一個產品類的最大實例化數量,可以通過判斷Map中已有的對象來實現,這樣的處理是非常有意義的,例如JDBC連接數據庫,都會要求設置一個MaxConnections最大連接數量,該數量就是內存中最大實例化的數量。
延遲加載還可以用在對象初始化比較復雜的情況下,例如硬件訪問,涉及多方面的交互,則可以通過延遲加載降低對象的產生和銷毀帶來的復雜性。
總結
以上是生活随笔為你收集整理的java工厂方法_Java设计模式之工厂方法模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重磅:Mobileye官宣推迟IPO,营
- 下一篇: 每日题解:LeetCode 718. 最