设计模式(一) 工厂模式 五种写法总结
轉(zhuǎn)載請(qǐng)標(biāo)明出處:
http://blog.csdn.net/zxt0601/article/details/52798423
本文出自:【張旭童的博客】
系列開篇瞎BB
設(shè)計(jì)模式相關(guān)的文章學(xué)習(xí)與總結(jié),一直有意為之,一直又覺得時(shí)機(jī)不到。
一 是怕自己代碼經(jīng)驗(yàn)還不夠,學(xué)習(xí)了也不懂,強(qiáng)行理解沒有意義。
二 是怕自己學(xué)習(xí)了以后總結(jié)出來,萬一有不對(duì)的地方,誤人子弟。
而在現(xiàn)在的公司摸爬滾打半年后,感覺自己寫代碼遇到了瓶頸,想寫好寫優(yōu)雅,卻不清楚這么寫究竟是自以為優(yōu)雅 還是真的優(yōu)雅。或?qū)χ匆恍┫到y(tǒng)源碼、框架源碼時(shí),不太理解他們這么寫是為什么。
于是我開始了學(xué)習(xí)之路,從比較簡(jiǎn)單的工廠模式開刀,看了大概10+篇資料,發(fā)現(xiàn)各位大大對(duì)工廠模式的各種寫法叫法不一,理解也不一,而且沒有一篇是比較全的收錄各種寫法的。so,這也堅(jiān)定了我將它總結(jié)寫出來的決心,既然每個(gè)人的理解都有或多或少的缺失或沖突,那我也總結(jié)一份我的理解,呈現(xiàn)出來,供各位看官參考 點(diǎn)評(píng)。
一概述:
屬于創(chuàng)建型設(shè)計(jì)模式,需要生成的對(duì)象叫做產(chǎn)品 ,生成對(duì)象的地方叫做工廠 。
使用場(chǎng)景:
在任何需要生成復(fù)雜對(duì)象的地方,都可以使用工廠方法模式。
直接用new可以完成的不需要用工廠模式
個(gè)人理解,重點(diǎn)就是這個(gè)復(fù)雜 (構(gòu)造函數(shù)有很多參數(shù))和 是否可以 直接用new。(不理解這句話的話,看完一圈例子就理解了)
下面逐個(gè)介紹我所知道的各種工廠模式以及它們的特點(diǎn),使用場(chǎng)景,并盡可能的找出JDK SDK里它們的身影。
二 簡(jiǎn)單(靜態(tài))工廠:
一個(gè)栗子:
我喜歡吃面條,抽象一個(gè)面條基類,(接口也可以),這是產(chǎn)品的抽象類。
先來一份蘭州拉面(具體的產(chǎn)品類):
public class LzNoodles extends INoodles {@Overridepublic void desc() {System.out.println("蘭州拉面 上海的好貴 家里才5 6塊錢一碗");} }程序員加班必備也要吃泡面(具體的產(chǎn)品類):
public class PaoNoodles extends INoodles {@Overridepublic void desc() {System.out.println("泡面好吃 可不要貪杯");} }還有我最愛吃的家鄉(xiāng)的干扣面(具體的產(chǎn)品類):
public class GankouNoodles extends INoodles {@Overridepublic void desc() {System.out.println("還是家里的干扣面好吃 6塊一碗");} }準(zhǔn)備工作做完了,我們來到一家“簡(jiǎn)單面館”(簡(jiǎn)單工廠類),菜單如下:
public class SimpleNoodlesFactory {public static final int TYPE_LZ = 1;//蘭州拉面public static final int TYPE_PM = 2;//泡面public static final int TYPE_GK = 3;//干扣面public static INoodles createNoodles(int type) {switch (type) {case TYPE_LZ:return new LzNoodles();case TYPE_PM:return new PaoNoodles();case TYPE_GK:default:return new GankouNoodles();}} }簡(jiǎn)單面館就提供三種面條(產(chǎn)品),你說你要啥,他就給你啥。這里我點(diǎn)了一份干扣面:
/*** 簡(jiǎn)單工廠模式*/INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK);noodles.desc();輸出:
還是家里的干扣面好吃 6塊一碗特點(diǎn)
1 它是一個(gè)具體的類,非接口 抽象類。有一個(gè)重要的create()方法,利用if或者 switch創(chuàng)建產(chǎn)品并返回。
2 create()方法通常是靜態(tài)的,所以也稱之為靜態(tài)工廠。
缺點(diǎn)
1 擴(kuò)展性差(我想增加一種面條,除了新增一個(gè)面條產(chǎn)品類,還需要修改工廠類方法)
2 不同的產(chǎn)品需要不同額外參數(shù)的時(shí)候 不支持。
三 另一種簡(jiǎn)單工廠(反射):
利用反射Class.forName(clz.getName()).newInstance()實(shí)現(xiàn)的簡(jiǎn)單工廠:
public class StaticNoodlesFactory {/*** 傳入Class實(shí)例化面條產(chǎn)品類** @param clz* @param <T>* @return*/public static <T extends INoodles> T createNoodles(Class<T> clz) {T result = null;try {result = (T) Class.forName(clz.getName()).newInstance();} catch (Exception e) {e.printStackTrace();}return result;} }點(diǎn)菜時(shí):
/*** 另一種簡(jiǎn)單工廠* 利用Class.forName(clz.getName()).newInstance()*/System.out.println("=====另一種簡(jiǎn)單工廠利用Class.forName(clz.getName()).newInstance()======" +"\n個(gè)人覺得不好,因?yàn)檫@樣和簡(jiǎn)單的new一個(gè)對(duì)象一樣,工廠方法應(yīng)該用于復(fù)雜對(duì)象的初始化" +"\n 這樣像為了工廠而工廠");//蘭州拉面INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);lz.desc();//泡面INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);pm.desc();輸出:
=====另一種簡(jiǎn)單工廠利用Class.forName(clz.getName()).newInstance()====== 個(gè)人覺得不好,因?yàn)檫@樣和簡(jiǎn)單的new一個(gè)對(duì)象一樣,工廠方法應(yīng)該用于復(fù)雜對(duì)象的初始化這樣像為了工廠而工廠 蘭州拉面 上海的好貴 家里才5 6塊錢一碗 泡面好吃 可不要貪杯特點(diǎn)
1 它也是一個(gè)具體的類,非接口 抽象類。但它的create()方法,是利用反射機(jī)制生成對(duì)象返回,好處是增加一種產(chǎn)品時(shí),不需要修改create()的代碼。
缺點(diǎn)
這種寫法粗看牛逼,細(xì)想之下,不談reflection的效率還有以下問題:
1 個(gè)人覺得不好,因?yàn)镃lass.forName(clz.getName()).newInstance()調(diào)用的是無參構(gòu)造函數(shù)生成對(duì)象,它和new Object()是一樣的性質(zhì),而工廠方法應(yīng)該用于復(fù)雜對(duì)象的初始化 ,當(dāng)需要調(diào)用有參的構(gòu)造函數(shù)時(shí)便無能為力了,這樣像為了工廠而工廠。
2 不同的產(chǎn)品需要不同額外參數(shù)的時(shí)候 不支持。
四 多方法工廠(常用)
使用方法二 三實(shí)現(xiàn)的工廠,都有一個(gè)缺點(diǎn):不同的產(chǎn)品需要不同額外參數(shù)的時(shí)候 不支持。
而且如果使用時(shí)傳遞的type、Class出錯(cuò),將不能得到正確的對(duì)象,容錯(cuò)率不高。
而多方法的工廠模式為不同產(chǎn)品,提供不同的生產(chǎn)方法,使用時(shí) 需要哪種產(chǎn)品就調(diào)用該種產(chǎn)品的方法,使用方便、容錯(cuò)率高。
工廠如下:
public class MulWayNoodlesFactory {/*** 模仿Executors 類* 生產(chǎn)泡面** @return*/public static INoodles createPm() {return new PaoNoodles();}/*** 模仿Executors 類* 生產(chǎn)蘭州拉面** @return*/public static INoodles createLz() {return new LzNoodles();}/*** 模仿Executors 類* 生產(chǎn)干扣面** @return*/public static INoodles createGk() {return new GankouNoodles();} }使用時(shí):
/*** 多方法靜態(tài)工廠(模仿Executor類)*/System.out.println("==============================模仿Executor類==============================" +"\n 這種我比較青睞,增加一個(gè)新面條,只要去增加一個(gè)static方法即可,也不修改原方法邏輯");INoodles lz2 = MulWayNoodlesFactory.createLz();lz2.desc();INoodles gk2 = MulWayNoodlesFactory.createGk();gk2.desc();輸出:
==============================模仿Executor類============================== 這種我比較青睞,增加一個(gè)新面條,只要去增加一個(gè)static方法即可,也不修改原方法邏輯 蘭州拉面 上海的好貴 家里才5 6塊錢一碗 還是家里的干扣面好吃 6塊一碗源碼撐腰環(huán)節(jié)
查看java源碼:java.util.concurrent.Executors類便是一個(gè)生成Executor 的工廠 ,其采用的便是 多方法靜態(tài)工廠模式:
例如ThreadPoolExecutor類構(gòu)造方法有5個(gè)參數(shù),其中三個(gè)參數(shù)寫法固定,前兩個(gè)參數(shù)可配置,如下寫。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}又如JDK想增加創(chuàng)建ForkJoinPool類的方法了,只想配置parallelism參數(shù),便在類里增加一個(gè)如下的方法:
public static ExecutorService newWorkStealingPool(int parallelism) {return new ForkJoinPool(parallelism,ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);}這個(gè)例子可以感受到工廠方法的魅力了吧:方便創(chuàng)建 同種類型的 復(fù)雜參數(shù) 對(duì)象。
五 普通工廠
普通工廠就是把簡(jiǎn)單工廠中具體的工廠類,劃分成兩層:抽象工廠層+具體的工廠子類層。(一般->特殊)
面條工廠(抽象工廠類),作用就是生產(chǎn)面條:
public abstract class NoodlesFactory {public abstract INoodles create(); }蘭州拉面工廠 (具體工廠子類):
public class LzFactory extends NoodlesFactory {@Overridepublic INoodles create() {return new LzNoodles();} }泡面工廠 (具體工廠子類):
public class PaoFactory extends NoodlesFactory {@Overridepublic INoodles create() {return new PaoNoodles();} }最愛的干扣面工廠 (具體工廠子類):
public class GankouFactory extends NoodlesFactory {@Overridepublic INoodles create() {return new GankouNoodles();} }使用時(shí):
/*** 普通工廠方法:*/System.out.println("===========================普通工廠方法==============================" +"\n 這種要多寫一個(gè)類,不過更面向?qū)ο蟀?= = ,實(shí)際中我更傾向于使用【模仿Executor類】的方式");NoodlesFactory factory1 = new GankouFactory();INoodles gk3 = factory1.create();gk3.desc();輸出:
===========================普通工廠方法============================== 這種要多寫一個(gè)類,不過更面向?qū)ο蟀?= = ,實(shí)際中我更傾向于使用【模仿Executor類】的方式 還是家里的干扣面好吃 6塊一碗普通工廠與簡(jiǎn)單工廠模式的區(qū)別:
可以看出,普通工廠模式特點(diǎn):不僅僅做出來的產(chǎn)品要抽象, 工廠也應(yīng)該需要抽象。
工廠方法使一個(gè)產(chǎn)品類的實(shí)例化延遲到其具體工廠子類.
工廠方法的好處就是更擁抱變化。當(dāng)需求變化,只需要增刪相應(yīng)的類,不需要修改已有的類。
而簡(jiǎn)單工廠需要修改工廠類的create()方法,多方法靜態(tài)工廠模式需要增加一個(gè)靜態(tài)方法。
缺點(diǎn):
引入抽象工廠層后,每次新增一個(gè)具體產(chǎn)品類,也要同時(shí)新增一個(gè)具體工廠類,所以我更青睞 多方法靜態(tài)工廠。
六 抽象工廠:
以上介紹的工廠都是單產(chǎn)品系的。抽象工廠是多產(chǎn)品系 (貌似也有產(chǎn)品家族的說法)。
舉個(gè)例子來說,每個(gè)店(工廠)不僅僅賣面條,還提供飲料賣。
提供飲料賣,飲料是產(chǎn)品,先抽象一個(gè)產(chǎn)品類,飲料:
然后實(shí)現(xiàn)兩個(gè)具體產(chǎn)品類:
可樂:
屌絲還是多喝水吧:
public class WaterDrinks extends IDrinks {@Overridepublic void prices() {System.out.println("和我一樣的窮鬼都喝水,不要錢~!");} }抽象飯店,無外乎吃喝(抽象工廠類):
public abstract class AbstractFoodFactory {/*** 生產(chǎn)面條** @return*/public abstract INoodles createNoodles();/*** 生產(chǎn)飲料*/public abstract IDrinks createDrinks(); }蘭州大酒店(具體工廠類):
public class LzlmFoodFactory extends AbstractFoodFactory {@Overridepublic INoodles createNoodles() {return new LzNoodles();//賣蘭州拉面}@Overridepublic IDrinks createDrinks() {return new WaterDrinks();//賣水} }KFC(具體工廠類):
public class KFCFoodFactory extends AbstractFoodFactory {@Overridepublic INoodles createNoodles() {return new PaoNoodles();//KFC居然賣泡面}@Overridepublic IDrinks createDrinks() {return new ColaDrinks();//賣可樂} }使用:
/*** 抽象工廠方法:*/System.out.println("==============================抽象方法==============================" +"\n 老實(shí)說,以我這一年的水平我體會(huì)不到抽象工廠有何巨大優(yōu)勢(shì),所以在我這里我沒有想到很好的使用場(chǎng)景。希望以后在慢慢體會(huì)吧。");AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();abstractFoodFactory1.createDrinks().prices();abstractFoodFactory1.createNoodles().desc();abstractFoodFactory1= new LzlmFoodFactory();abstractFoodFactory1.createDrinks().prices();abstractFoodFactory1.createNoodles().desc();輸出:
==============================抽象方法============================== 老實(shí)說,以我這一年的水平我體會(huì)不到抽象工廠有何巨大優(yōu)勢(shì),所以在我這里我沒有想到很好的使用場(chǎng)景。希望以后在慢慢體會(huì)吧。 可樂三塊五 泡面好吃 可不要貪杯 和我一樣的窮鬼都喝水,不要錢~! 蘭州拉面 上海的好貴 家里才5 6塊錢一碗小結(jié):
將工廠也抽象了,在使用時(shí),工廠和產(chǎn)品都是面向接口編程,OO(面向?qū)ο?#xff09;的不得了。
缺點(diǎn)
但是將工廠也抽象后,有個(gè)顯著問題,就是類爆炸了。而且每次拓展新產(chǎn)品種類,例如不僅賣吃賣喝,我還想賣睡,提供床位服務(wù),這需要修改抽象工廠類,因此所有的具體工廠子類,都被牽連,需要同步被修改。
老實(shí)說,以我這一年的水平我體會(huì)不到抽象工廠有何巨大優(yōu)勢(shì),所以在我這里我沒有想到很好的使用場(chǎng)景。希望以后在慢慢體會(huì)吧。如有您知道,希望不吝賜教。
七 個(gè)人總結(jié)和使用場(chǎng)景
一句話總結(jié)工廠模式:方便創(chuàng)建 同種產(chǎn)品類型的 復(fù)雜參數(shù) 對(duì)象
工廠模式重點(diǎn)就是適用于 構(gòu)建同產(chǎn)品類型(同一個(gè)接口 基類)的不同對(duì)象時(shí),這些對(duì)象new很復(fù)雜,需要很多的參數(shù),而這些參數(shù)中大部分都是固定的,so,懶惰的程序員便用工廠模式封裝之。
(如果構(gòu)建某個(gè)對(duì)象很復(fù)雜,需要很多參數(shù),但這些參數(shù)大部分都是“不固定”的,應(yīng)該使用Builder模式)
為了適應(yīng)程序的擴(kuò)展性,擁抱變化,便衍生出了 普通工廠、抽象工廠等模式。
代碼傳送門:
github:
https://github.com/mcxtzhang/Demos/tree/master/libfactorypattern
學(xué)習(xí)參考:
http://blog.csdn.net/zhangerqing/article/details/8194653
http://blog.csdn.net/column/details/code-design.html
Android源碼設(shè)計(jì)模式書籍
Java設(shè)計(jì)模式深入研究書籍
一些搜 工廠模式 出來的文章….
總結(jié)
以上是生活随笔為你收集整理的设计模式(一) 工厂模式 五种写法总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UE4 循环滚动Led效果材质
- 下一篇: Java利用dom4j解析XML任意节点