轉(zhuǎn)載請(qǐng)標(biāo)明出處: http://www.reibang.com/p/ee46b1a05297
本文出自:【張旭童的簡(jiǎn)書(shū)】 (http://www.reibang.com/users/8e91ff99b072/latest_articles)
代碼傳送門:喜歡的話,隨手點(diǎn)個(gè)star碳褒。多謝
https://github.com/mcxtzhang/Demos/tree/master/libfactorypattern
系列開(kāi)篇瞎BB
設(shè)計(jì)模式相關(guān)的文章學(xué)習(xí)與總結(jié)夜郁,一直有意為之茧吊,一直又覺(jué)得時(shí)機(jī)不到刹缝。
一 是怕自己代碼經(jīng)驗(yàn)還不夠问慎,學(xué)習(xí)了也不懂惫叛,強(qiáng)行理解沒(méi)有意義。
二 是怕自己學(xué)習(xí)了以后總結(jié)出來(lái)著觉,萬(wàn)一有不對(duì)的地方村生,誤人子弟。
而在現(xiàn)在的公司摸爬滾打半年后饼丘,感覺(jué)自己寫(xiě)代碼遇到了瓶頸趁桃,想寫(xiě)好寫(xiě)優(yōu)雅,卻不清楚這么寫(xiě)究竟是自以為優(yōu)雅 還是真的優(yōu)雅葬毫≌蚧裕或?qū)χ匆恍┫到y(tǒng)源碼、框架源碼時(shí)贴捡,不太理解他們這么寫(xiě)是為什么。
于是我開(kāi)始了學(xué)習(xí)之路村砂,從比較簡(jiǎn)單的工廠模式開(kāi)刀烂斋,看了大概10+篇資料,發(fā)現(xiàn)各位大大對(duì)工廠模式的各種寫(xiě)法叫法不一础废,理解也不一汛骂,而且沒(méi)有一篇是比較全的收錄各種寫(xiě)法的。so评腺,這也堅(jiān)定了我將它總結(jié)寫(xiě)出來(lái)的決心帘瞭,既然每個(gè)人的理解都有或多或少的缺失或沖突,那我也總結(jié)一份我的理解蒿讥,呈現(xiàn)出來(lái)蝶念,供各位看官參考 點(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)品的抽象類。
public abstract class INoodles {
/**
* 描述每種面條啥樣的
*/
public abstract void desc();
}
先來(lái)一份蘭州拉面(具體的產(chǎn)品類):
public class LzNoodles extends INoodles {
@Override
public void desc() {
System.out.println("蘭州拉面 上海的好貴 家里才5 6塊錢一碗");
}
}
程序員加班必備也要吃泡面(具體的產(chǎn)品類):
public class PaoNoodles extends INoodles {
@Override
public void desc() {
System.out.println("泡面好吃 可不要貪杯");
}
}
還有我最愛(ài)吃的家鄉(xiāng)的干扣面(具體的產(chǎn)品類):
public class GankouNoodles extends INoodles {
@Override
public void desc() {
System.out.println("還是家里的干扣面好吃 6塊一碗");
}
}
準(zhǔn)備工作做完了,我們來(lái)到一家“簡(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)品)钓丰,你說(shuō)你要啥,他就給你啥每币。這里我點(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è)人覺(jué)得不好存筏,因?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è)人覺(jué)得不好味榛,因?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)
這種寫(xiě)法粗看牛逼,細(xì)想之下略吨,不談reflection的效率還有以下問(wèn)題:
1 個(gè)人覺(jué)得不好集币,因?yàn)镃lass.forName(clz.getName()).newInstance()調(diào)用的是無(wú)參構(gòu)造函數(shù)生成對(duì)象,它和new Object()是一樣的性質(zhì)翠忠,而工廠方法應(yīng)該用于復(fù)雜對(duì)象的初始化 鞠苟,當(dāng)需要調(diào)用有參的構(gòu)造函數(shù)時(shí)便無(wú)能為力了,這樣像為了工廠而工廠秽之。
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ù)寫(xiě)法固定胁勺,前兩個(gè)參數(shù)可配置世澜,如下寫(xiě)。
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 {
@Override
public INoodles create() {
return new LzNoodles();
}
}
泡面工廠 (具體工廠子類):
public class PaoFactory extends NoodlesFactory {
@Override
public INoodles create() {
return new PaoNoodles();
}
}
最愛(ài)的干扣面工廠 (具體工廠子類):
public class GankouFactory extends NoodlesFactory {
@Override
public INoodles create() {
return new GankouNoodles();
}
}
使用時(shí):
/**
* 普通工廠方法:
*/
System.out.println("===========================普通工廠方法==============================" +
"\n 這種要多寫(xiě)一個(gè)類络拌,不過(guò)更面向?qū)ο蟀?= = ,實(shí)際中我更傾向于使用【模仿Executor類】的方式");
NoodlesFactory factory1 = new GankouFactory();
INoodles gk3 = factory1.create();
gk3.desc();
輸出:
===========================普通工廠方法==============================
這種要多寫(xiě)一個(gè)類回溺,不過(guò)更面向?qū)ο蟀?= = 春贸,實(shí)際中我更傾向于使用【模仿Executor類】的方式
還是家里的干扣面好吃 6塊一碗
普通工廠與簡(jiǎn)單工廠模式的區(qū)別:
可以看出,普通工廠模式特點(diǎn):不僅僅做出來(lái)的產(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)品家族的說(shuō)法)售躁。
舉個(gè)例子來(lái)說(shuō)坞淮,每個(gè)店(工廠)不僅僅賣面條,還提供飲料賣陪捷。
提供飲料賣回窘,飲料是產(chǎn)品,先抽象一個(gè)產(chǎn)品類揩局,飲料:
public abstract class IDrinks {
/**
* 描述每種飲料多少錢
*/
public abstract void prices();
}
然后實(shí)現(xiàn)兩個(gè)具體產(chǎn)品類:
可樂(lè):
public class ColaDrinks extends IDrinks {
@Override
public void prices() {
System.out.println("可樂(lè)三塊五");
}
}
屌絲還是多喝水吧:
public class WaterDrinks extends IDrinks {
@Override
public void prices() {
System.out.println("和我一樣的窮鬼都喝水毫玖,不要錢~!");
}
}
抽象飯店凌盯,無(wú)外乎吃喝(抽象工廠類):
public abstract class AbstractFoodFactory {
/**
* 生產(chǎn)面條
*
* @return
*/
public abstract INoodles createNoodles();
/**
* 生產(chǎn)飲料
*/
public abstract IDrinks createDrinks();
}
蘭州大酒店(具體工廠類):
public class LzlmFoodFactory extends AbstractFoodFactory {
@Override
public INoodles createNoodles() {
return new LzNoodles();//賣蘭州拉面
}
@Override
public IDrinks createDrinks() {
return new WaterDrinks();//賣水
}
}
KFC(具體工廠類):
public class KFCFoodFactory extends AbstractFoodFactory {
@Override
public INoodles createNoodles() {
return new PaoNoodles();//KFC居然賣泡面
}
@Override
public IDrinks createDrinks() {
return new ColaDrinks();//賣可樂(lè)
}
}
使用:
/**
* 抽象工廠方法:
*/
System.out.println("==============================抽象方法==============================" +
"\n 老實(shí)說(shuō)付枫,以我這一年的水平我體會(huì)不到抽象工廠有何巨大優(yōu)勢(shì),所以在我這里我沒(méi)有想到很好的使用場(chǎng)景驰怎。希望以后在慢慢體會(huì)吧阐滩。");
AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
abstractFoodFactory1.createDrinks().prices();
abstractFoodFactory1.createNoodles().desc();
abstractFoodFactory1= new LzlmFoodFactory();
abstractFoodFactory1.createDrinks().prices();
abstractFoodFactory1.createNoodles().desc();
輸出:
==============================抽象方法==============================
老實(shí)說(shuō),以我這一年的水平我體會(huì)不到抽象工廠有何巨大優(yōu)勢(shì)县忌,所以在我這里我沒(méi)有想到很好的使用場(chǎng)景掂榔。希望以后在慢慢體會(huì)吧。
可樂(lè)三塊五
泡面好吃 可不要貪杯
和我一樣的窮鬼都喝水症杏,不要錢~装获!
蘭州拉面 上海的好貴 家里才5 6塊錢一碗
小結(jié):
將工廠也抽象了,在使用時(shí)厉颤,工廠和產(chǎn)品都是面向接口編程穴豫,OO(面向?qū)ο螅┑牟坏昧恕?/p>
缺點(diǎn)
但是將工廠也抽象后,有個(gè)顯著問(wèn)題逼友,就是類爆炸了精肃。而且每次拓展新產(chǎn)品種類,例如不僅賣吃賣喝帜乞,我還想賣睡司抱,提供床位服務(wù),這需要修改抽象工廠類黎烈,因此所有的具體工廠子類习柠,都被牽連,需要同步被修改怨喘。
老實(shí)說(shuō)津畸,以我這一年的水平我體會(huì)不到抽象工廠有何巨大優(yōu)勢(shì),所以在我這里我沒(méi)有想到很好的使用場(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ì)模式書(shū)籍
Java設(shè)計(jì)模式深入研究書(shū)籍
一些搜 工廠模式 出來(lái)的文章....