什么是工廠模式?
工廠模式是我們最常用的實例化對象模式了,是用工廠方法代替new操作的一種模式。著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程序系統可以說是隨處可見。因為工廠模式就相當于創建實例對象的new,我們經常要根據類Class生成實例對象,如A a=new A() 工廠模式也是用來創建實例對象的,所以以后new時就要多個心眼,是否可以考慮使用工廠模式,雖然這樣做,可能多做一些工作,但會給你系統帶來更大的可擴展性和盡量少的修改量。
簡單點來說,工廠模式就是提供一個產生實例化對象的制造廠,每一個工廠都會提供類似的對象(實現共同接口),當我們需要某個類的實例化對象時,我們不需要關心服務端是通過什么方式來獲取對象的,我們直接向對應的工廠提出我們的需求即可(實現了客戶端和服務端的分離)。
工廠模式分成三類:
1、簡單工廠模式(Simple Factory)
2、工廠方法模式(Factory Method)
3、抽象工廠模式(Abstract Factory)
一般我們把簡單工廠模式也歸為工廠方法模式,然后抽象工廠模式是工廠方法模式的擴展。
1、工廠方法模式
先來看看工廠方法模式的組成:
工廠類角色:這是本模式的核心,含有一定的商業邏輯和判斷邏輯。在java中它往往由一個具體類實現。抽象產品角色:它一般是具體產品繼承的父類或者實現的接口。在java中由接口或者抽象類來實現。具體產品角色:工廠類所創建的對象就是此角色的實例。在java中由一個具體類實現。

寫個小例子,模擬上帝造人事件,上帝根據不同地理位置氣候的不同,造了三種不同膚色的人類:黃種人,白種人,黑種人。
這是一個膚色的接口(這里我們粗略先認為他們只有膚色之差)
 1 package com.lcw.factory.test1; 2  3 /** 4  * 皮膚接口 5  */ 6 public interface SkinInterface { 7      8     public void skin(); 9 10 }三種膚色的人類,分別是三個實現類(實現SkinInterface接口),由于代碼結構一致,這里只給出一個
 1 package com.lcw.factory.test1; 2  3 public class YellowRace implements SkinInterface { 4  5     @Override 6     public void skin() { 7         System.out.然后,如果按照我們以往的慣例,要拿到對應實現類的對象,我們需要SkinInterface si=new YellowRace();這樣子,表面上看雖然沒有問題,但一類實現類爆發呢?比如有上百千個實現類,那么除了要添加對應的實現類不說,還需要在客戶端去"顯示調用"暴露出實現類的類名,后期維護起來也很麻煩。
這時我們的工廠類就派上用場了
 1 package com.lcw.factory.test1; 2  3 /** 4  * 工廠類(返回對象實例) 5  *  6  */ 7 public class SkinFactory { 8  9     // key為關鍵字,決定實例化哪個類對象10     public SkinInterface getSkin(String key) {11         if ("black".equals(key)) {12             return new BlackRace();13         } else if ("white".equals(key)) {14             return new WhiteRace();15         } else if ("yellow".equals(key)) {16             return new YellowRace();17         }18         return null;19     }20 }寫個實現類試試吧
 1 package com.lcw.factory.test1; 2  3 import java.util.Map; 4  5 public class FactoryTest { 6  7     /** 8      * @param args 9      */10     public static void main(String[] args) {11         //普通調用方法(顯示調用)12         System.out.println("-------------------------普通調用方法-----------------------------");13         SkinInterface yellowRace=new YellowRace();14         SkinInterface whiteRace=new WhiteRace();15         SkinInterface blackRace=new BlackRace();16         17         yellowRace.skin();18         whiteRace.skin();19         blackRace.skin();20         21         22         //工廠方法模式調用23         System.out.println("-------------------------工廠方法模式調用(脫離客戶端和服務端)-----------------------------");24         SkinFactory skinFactory=new SkinFactory();25         SkinInterface skinInterface1=skinFactory.getSkin("black");26         SkinInterface skinInterface2=skinFactory.getSkin("white");27         SkinInterface skinInterface3=skinFactory.getSkin("yellow");28         29         skinInterface1.skin();30         skinInterface2.skin();31         skinInterface3.skin();32     }33 }
從上圖來看,我們已經達到了基本目的,但之前的問題已經存在,雖然我們解決了客戶端的"顯示調用",但服務端卻多了一堆的if..elseif的判斷,如果實現類很多,那么就需要些更多的if..elseif的判斷,這效率很明顯就低了,因為每次客戶端一請求,那么服務端就要做多余的沒玩沒了的判斷。
有沒有什么更好的方法的?那必須是有的,Java有個很強大的機制——反射,我們可以利用反射機制,把所有的類名寫入到一個配置文件,以后單純的維護配置文件即可,看下具體實現過程吧。
首先,我們需要一個配置文件skin.properties
1 black=com.lcw.factory.test1.BlackRace2 yellow=com.lcw.factory.test1.YellowRace3 white=com.lcw.factory.test1.WhiteRace
然后寫一個讀取properties文件的類,把關鍵字(key)=》值(value)封裝到一個map集合。這樣我們在客戶端只需要輸入關鍵字就可以讓服務端自動映射到對應的類。
關于Java讀取配置文件,不熟悉的小伙伴們可以輕戳《Java讀寫配置文件——Properties類的簡要使用筆記》,幫你們總結好了。
 1 package com.lcw.factory.test1; 2  3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Enumeration; 6 import java.util.HashMap; 7 import java.util.Map; 8 import java.util.Properties; 9 10 /**11  * 讀取配置文件類(讀取配置文件信息并把鍵值對封裝成一個Map集合)12  * 13  */14 public class PropertiesReader {15 16     public Map<String,String> getSkinMap(){17         18         Map<String,String> map=new HashMap<String, String>();19         20         Properties properties=new Properties();21         InputStream inStream=getClass().getResourceAsStream("skin.properties");22         try {23             properties.load(inStream);24             Enumeration enumeration=properties.propertyNames();//取得配置文件里的所有key值25             while(enumeration.hasMoreElements()){26                 String key=(String) enumeration.nextElement();27                 map.put(key, properties.getProperty(key));28             }29             return map;30             31         } catch (IOException e) {32             e.printStackTrace();33         }34         35         return null;36         37     }38 }當然,此時的工廠類也需要修改了,看看完整代碼
 1 package com.lcw.factory.test1; 2  3 /** 4  * 工廠類(返回對象實例) 5  *  6  */ 7 public class SkinFactory { 8  9     // key為關鍵字,決定實例化哪個類對象10     public SkinInterface getSkin(String key) {11         if ("black".equals(key)) {12             return new BlackRace();13         } else if ("white".equals(key)) {14             return new WhiteRace();15         } else if ("yellow".equals(key)) {16             return new YellowRace();17         }18         return null;19     }20 21     // 優化版(避免使用if..elseif繁雜判斷)22     public SkinInterface getSkinByClassName(String className) {23         try {24             25             return (SkinInterface) Class.forName(className).newInstance();26             27         } catch (InstantiationException e) {28             e.printStackTrace();29         } catch (IllegalaccessException e) {30             e.printStackTrace();31         } catch (ClassNotFoundException e) {32             e.printStackTrace();33         }34         return null;35     }36 37 }完整測試類:
 1 package com.lcw.factory.test1; 2  3 import java.util.Map; 4  5 public class FactoryTest { 6  7     /** 8      * @param args 9      */10     public static void main(String[] args) {11         //普通調用方法(顯示調用)12         System.out.println("-------------------------普通調用方法-----------------------------");13         SkinInterface yellowRace=new YellowRace();14         SkinInterface whiteRace=new WhiteRace();15         SkinInterface blackRace=new BlackRace();16         17         yellowRace.skin();18         whiteRace.skin();19         blackRace.skin();20         21         22         //工廠方法模式調用23         System.out.println("-------------------------工廠方法模式調用(脫離客戶端和服務端)-----------------------------");24         SkinFactory skinFactory=new SkinFactory();25         SkinInterface skinInterface1=skinFactory.getSkin("black");26         SkinInterface skinInterface2=skinFactory.getSkin("white");27         SkinInterface skinInterface3=skinFactory.getSkin("yellow");28         29         skinInterface1.skin();30         skinInterface2.skin();31         skinInterface3.skin();32         33         34         //工廠方法模式調用(優化版)35         System.out.println("-------------------------工廠方法模式調用(優化版)-----------------------------");36         SkinFactory factory=new SkinFactory();37         38         //SkinInterface skinInterface=factory.getSkinByClassName("com.lcw.factory.test.YellowRace");//過于繁雜的包名而且顯示調用了,我們可以用配置文件來映射簡化39         //skinInterface.skin();40         41         PropertiesReader propertiesReader=new PropertiesReader();42         Map<String,String> map=propertiesReader.getSkinMap();//獲取配置文件里的map集合43         SkinInterface sf1=factory.getSkinByClassName(map.get("black"));44         SkinInterface sf2=factory.getSkinByClassName(map.get("white"));45         SkinInterface sf3=factory.getSkinByClassName(map.get("yellow"));46         47         sf1.skin();48         sf2.skin();49         sf3.skin();50         51         52         53     }54 55 }看下效果圖:

這樣一來,雖然我們多做了很多代碼工廠,但對于日后維護起來就很方便了,不管添加多少的新的實現類,我們都不需要去改動客戶端代碼和工廠類代碼,只需要在配置文件里添加上對應的關鍵字(key),和value(完整類名)即可。
2、抽象工廠模式
在抽象工廠模式中,抽象產品 (AbstractProduct) 可能是一個或多個,從而構成一個或多個產品族(Product Family)。

還是一樣舉個例子,人有男女之別,那么我們以國家來劃分,在中國,有中國男孩,女孩,在美國,有美國男孩,女孩。本質上人只有男、女,那么只是一個基礎的接口,那么不同的國家,就可以看做是不同的工廠,他們分別可以生產出"屬于他們國家的男孩、女孩(產品)"。
來看下具體代碼吧
這是產品的基礎接口:
1 package com.lcw.factory.test2;2 3 /**4  *男孩接口(產品接口)5  */6 public interface Boy {7     public void boy();8 }1 package com.lcw.factory.test2;2 /**3  * 女孩接口(產品接口)4  *5  */6 public interface Girl {7     public void girl();8 }產品的具體實現:(由于代碼結構一致,這里只給出美國)
 1 package com.lcw.factory.test2; 2 /** 3  *  4  *美國男孩(產品) 5  */ 6 public class AmericaBoy implements Boy { 7  8     @Override 9     public void boy() {10         System.out.println("我是男孩,我來自美國。");11     }12 13 } 1 package com.lcw.factory.test2; 2  3 /** 4  * 美國女孩(產品) 5  */ 6 public class AmericaGirl implements Girl { 7  8     @Override 9     public void girl() {10         System.out.println("我是女孩,我來自美國。");11     }12 13 }抽象工廠接口:
 1 package com.lcw.factory.test2; 2 /** 3  *  4  *抽象工廠 5  */ 6 public interface PersonFactory { 7      8     public Boy getBoy(); 9 10     public Girl getGirl();11 12 }具體工廠實現:(同樣的,中國也有相同的具體工廠去實現這個抽象接口,并提供對應的中國男孩、女孩實例,由于代碼結構一直,這里就不貼了)
 1 package com.lcw.factory.test2; 2 /** 3  *  4  *具體工廠類(產生具體實例) 5  */ 6 public class AmericaPersonFactory implements PersonFactory{ 7  8     @Override 9     public Boy getBoy() {10         return new AmericaBoy();11     }12 13     @Override14     public Girl getGirl() {15         return new AmericaGirl();16     }17 18 }來個測試類:
 1 package com.lcw.factory.test2; 2  3 public class PersonFactoryTest { 4      5     public static void main(String[] args) { 6         PersonFactory factory1=new ChinesePersonFactory();//取得工廠對象 7         //獲取所需實例 8         Boy boy1=factory1.getBoy(); 9         Girl girl1=factory1.getGirl();10         11         boy1.boy();12         girl1.girl();13         14         15         16         17         PersonFactory factory2=new AmericaPersonFactory();18         Boy boy2=factory2.getBoy();19         Girl girl2=factory2.getGirl();20         21         22         boy2.boy();23         girl2.girl();24     }25 }看下實現效果:

總結:
(1)工廠方法模式是有一個抽象的父類定義公共接口,子類負責生成具體的對象,這樣做的目的是將類的實例化操作延遲到子類中完成。 (或者由一個具體的類去創建其他類的實例,父類是相同的,父類是具體的。)(2)抽象工廠模式提供一個創建一系列相關或相互依賴對象的接口,而無須指定他們具體的類。它針對的是有多個產品的等級結構。而工廠方法模式針對的是一個產品的等級結構。
作者:Balla_兔子出處:http://m.survivalescaperooms.com/lichenwei/本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準,我分文不收;相不準,你也好回來找我!
新聞熱點
疑難解答