設(shè)計(jì)原則:
要依賴抽象,不要依賴具體類
目錄
本文的結(jié)構(gòu)如下:
- 什么是簡(jiǎn)單工廠模式
- 為什么要該模式
- 模式的結(jié)構(gòu)
- 代碼示例
- 優(yōu)點(diǎn)和缺點(diǎn)
- 適用環(huán)境
- 模式應(yīng)用
- 總結(jié)
一由桌、前言
工廠模式是一個(gè)比較復(fù)雜的模式伶授,可以分為三類:
- 簡(jiǎn)單工廠模式
- 工廠方法模式
- 抽象工廠模式
準(zhǔn)確說(shuō)贵少,簡(jiǎn)單工廠模式并不屬于GoF 23種設(shè)計(jì)模式组砚,但在軟件開發(fā)中應(yīng)用較為頻繁峻呕,通常將它作為學(xué)習(xí)其他工廠模式的入門利职。
二、什么是簡(jiǎn)單工廠模式
簡(jiǎn)單工廠模式(Simple Factory Pattern):又稱為靜態(tài)工廠方法(Static Factory Method)模式瘦癌,它屬于類創(chuàng)建型模式猪贪。在簡(jiǎn)單工廠模式中,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例讯私。簡(jiǎn)單工廠模式專門定義一個(gè)類來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例热押,被創(chuàng)建的實(shí)例通常都具有共同的父類。
三斤寇、為什么要用該模式
假設(shè)你經(jīng)過(guò)深思熟慮桶癣,走到老板的辦公司,微笑著對(duì)老板說(shuō):“老板娘锁,我是來(lái)辦理離職的牙寞。”老板再三挽留莫秆,不舍放走你這個(gè)人才碎税,給你漲三倍薪資,你依然面不改色馏锡,毅然拒絕了雷蹂,因?yàn)槟阋呀?jīng)在市中心買下了一家店面,正準(zhǔn)備做自己的老板--賣蛋糕杯道。
辭職后匪煌,你找我寫訂蛋糕的相關(guān)代碼责蝠,(現(xiàn)在只有四種蛋糕,芝士蛋糕萎庭,水果蛋糕霜医,奶油蛋糕及默認(rèn)什么也不加的蛋糕),于是我這樣寫:
/**
* Created by w1992wishes on 2017/10/31.
*/
public class CakeStore {
public Cake orderCake(String type) {
Cake cake;
if ("cheese".equals(type)) {
cake = new CheeseCake();
} else if ("fruit".equals(type)) {
cake = new FruitCake();
} else if ("cream".equals(type)) {
cake = new CreamCake();
} else {
cake = new DefaultCake();
}
cake.bake();
cake.box();
return cake;
}
}
你新開的蛋糕店賣的很好驳规,想新增加一些品種肴敛,比如巧克力蛋糕,于是我需要這樣修改:
我把代碼修改后給了你吗购,出于負(fù)責(zé)的態(tài)度医男,你百忙中抽空看了一遍,你驚呆了,你說(shuō),“你就是一堆狗屎疑故,這種代碼都能寫出來(lái)?”报账。
你張大了嘴對(duì)我咆哮,口水噴了我一臉埠偿,但作為前程序猿透罢,你有著專業(yè)的精神,你壓著洶涌的怒火冠蒋,教導(dǎo)我說(shuō):
CakeStore類是一個(gè)“巨大的”類羽圃,在該類的設(shè)計(jì)中存在如下幾個(gè)問(wèn)題:
- 在CakeStore類orderCake()中包含很多“if…else…”代碼塊,代碼相當(dāng)冗長(zhǎng)浊服,代碼越長(zhǎng)统屈,閱讀難度胚吁、維護(hù)難度和測(cè)試難度也越大牙躺;而且大量條件語(yǔ)句的存在還將影響系統(tǒng)的性能,程序在執(zhí)行過(guò)程中需要做大量的條件判斷腕扶。
- CakeStore類orderCake()的職責(zé)過(guò)重孽拷,它負(fù)責(zé)初始化所有的蛋糕對(duì)象,并且還要進(jìn)行bake()和box()半抱,將各種蛋糕對(duì)象的初始化代碼和bake()脓恕、box()集中在一個(gè)方法中實(shí)現(xiàn),違反了“單一職責(zé)原則”窿侈,不利于方法的重用和維護(hù)炼幔。
- 當(dāng)需要增加新類型的蛋糕時(shí),必須修改CakeStore類的源代碼史简,違反了“開閉原則”乃秀。
- 如果新開一家蛋糕店,則必須重復(fù)寫一堆“if...else”代碼塊用來(lái)創(chuàng)建Cake對(duì)象,重新寫了一堆重復(fù)代碼跺讯。
- ......
憤怒的你把我辭退枢贿,掏出還裹著面粉的手,重操舊業(yè)刀脏,10分鐘局荚,不,只用了2分鐘愈污,你就改好了耀态。于是你拍了拍腦袋,懊惱說(shuō):“我當(dāng)時(shí)為什么要找那堆狗屎钙畔?我可是專業(yè)的茫陆。”
/**
* Created by w1992wishes on 2017/10/31.
*/
public class SimpleCakeFacroty {
public static Cake createCake(String type){
Cake cake;
if ("cheese".equals(type)) {
cake = new CheeseCake();
} else if ("fruit".equals(type)) {
cake = new FruitCake();
} else if ("cream".equals(type)) {
cake = new CreamCake();
} else {
cake = new DefaultCake();
}
return cake;
}
}
你將創(chuàng)建蛋糕的過(guò)程挪到了一個(gè)叫SimpleCakeFactory類中擎析,于是CakeStore便這樣了:
public class CakeStore {
public Cake orderCake(String type) {
Cake cake;
cake = SimpleCakeFacroty.createCake(type);
cake.bake();
cake.box();
return cake;
}
}
當(dāng)然看上去只是將創(chuàng)建Cake對(duì)象從CakeStore挪到了SimpleCakeFactory簿盅,似乎并沒(méi)有太多變化。如果新增蛋糕似乎還需要新增else if揍魂,破壞了“對(duì)擴(kuò)展開放桨醋,對(duì)修改關(guān)閉”的原則。
但簡(jiǎn)單工廠的加入使蛋糕的生產(chǎn)和蛋糕的使用分開了现斋,同時(shí)新開蛋糕店時(shí)喜最,也可以重新復(fù)用SimpleCakeFactory中的方法,減少了大量重復(fù)代碼庄蹋,而且每次新增蛋糕瞬内,只需要修改SimpleCakeFactory一個(gè)類就好了,這些都是優(yōu)點(diǎn)呢限书。
四虫蝶、模式的結(jié)構(gòu)
有了上面的蛋糕店的例子后,再來(lái)總結(jié)一下簡(jiǎn)單工廠模式的結(jié)構(gòu)倦西。
在簡(jiǎn)單工廠模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- SimpleFactory(工廠角色):工廠角色即工廠類能真,它是簡(jiǎn)單工廠模式的核心,負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有產(chǎn)品實(shí)例的內(nèi)部邏輯扰柠;工廠類可以被外界直接調(diào)用粉铐,創(chuàng)建所需的產(chǎn)品對(duì)象;在工廠類中提供了靜態(tài)的工廠方法createProduct()卤档,它的返回類型為抽象產(chǎn)品類型Product蝙泼。
- Product(抽象產(chǎn)品角色):它是工廠類所創(chuàng)建的所有對(duì)象的父類,封裝了各種產(chǎn)品對(duì)象的公有方法劝枣,它的引入將提高系統(tǒng)的靈活性汤踏,使得在工廠類中只需定義一個(gè)通用的工廠方法倡缠,因?yàn)樗袆?chuàng)建的具體產(chǎn)品對(duì)象都是其子類對(duì)象。
- ConcreteProduct(具體產(chǎn)品角色):它是簡(jiǎn)單工廠模式的創(chuàng)建目標(biāo)茎活,所有被創(chuàng)建的對(duì)象都充當(dāng)這個(gè)角色的某個(gè)具體類的實(shí)例昙沦。每一個(gè)具體產(chǎn)品角色都繼承了抽象產(chǎn)品角色,需要實(shí)現(xiàn)在抽象產(chǎn)品中聲明的抽象方法载荔。
在簡(jiǎn)單工廠模式中盾饮,客戶端通過(guò)工廠類來(lái)創(chuàng)建一個(gè)產(chǎn)品類的實(shí)例,而無(wú)須直接使用new關(guān)鍵字來(lái)創(chuàng)建對(duì)象懒熙,它是工廠模式家族中最簡(jiǎn)單的一員丘损。
五、代碼示例
上面蛋糕的例子可以看作一個(gè)簡(jiǎn)單示例工扎,這里簡(jiǎn)單再列一個(gè):
典型的抽象產(chǎn)品類代碼
/**
* Created by w1992wishes on 2017/11/1.
*/
public abstract class Product {
public void methodSame() {
//公共方法的實(shí)現(xiàn)
}
//聲明抽象業(yè)務(wù)方法
public abstract void methodDiff();
}
在具體產(chǎn)品類中實(shí)現(xiàn)了抽象產(chǎn)品類中聲明的抽象業(yè)務(wù)方法徘钥,不同的具體產(chǎn)品類可以提供不同的實(shí)現(xiàn),典型的具體產(chǎn)品類代碼
/**
* Created by w1992wishes on 2017/11/1.
*/
public class ConcreteProductA extends Product {
@Override
public void methodDiff() {
//業(yè)務(wù)方法的實(shí)現(xiàn)
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class ConcreteProductB extends Product {
@Override
public void methodDiff() {
//業(yè)務(wù)方法的實(shí)現(xiàn)
}
}
簡(jiǎn)單工廠模式的核心是工廠類肢娘,在沒(méi)有工廠類之前呈础,客戶端一般會(huì)使用new關(guān)鍵字來(lái)直接創(chuàng)建產(chǎn)品對(duì)象,而在引入工廠類之后橱健,客戶端可以通過(guò)工廠類來(lái)創(chuàng)建產(chǎn)品而钞,在簡(jiǎn)單工廠模式中,工廠類提供了一個(gè)靜態(tài)工廠方法供客戶端使用拘荡,根據(jù)所傳入的參數(shù)不同可以創(chuàng)建不同的產(chǎn)品對(duì)象臼节,典型的工廠類代碼
/**
* Created by w1992wishes on 2017/11/1.
*/
public class SimpleFactory {
//靜態(tài)工廠方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalsIgnoreCase("A")) {
product = new ConcreteProductA();
//初始化設(shè)置product
}
else if (arg.equalsIgnoreCase("B")) {
product = new ConcreteProductB();
//初始化設(shè)置product
}
return product;
}
}
在客戶端代碼中,我們通過(guò)調(diào)用工廠類的工廠方法即可得到產(chǎn)品對(duì)象珊皿,典型代碼
class Client {
public static void main(String args[]) {
Product product;
product = Factory.getProduct("A"); //通過(guò)工廠類創(chuàng)建產(chǎn)品對(duì)象
product.methodSame();
product.methodDiff();
}
}
六网缝、優(yōu)點(diǎn)和缺點(diǎn)
6.1、優(yōu)點(diǎn)
- 工廠類含有必要的判斷邏輯蟋定,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)產(chǎn)品類的實(shí)例粉臊,客戶端可以免除直接創(chuàng)建產(chǎn)品對(duì)象的責(zé)任,而僅僅“消費(fèi)”產(chǎn)品溢吻;簡(jiǎn)單工廠模式通過(guò)這種做法實(shí)現(xiàn)了對(duì)責(zé)任的分割维费,它提供了專門的工廠類用于創(chuàng)建對(duì)象果元。
- 客戶端無(wú)須知道所創(chuàng)建的具體產(chǎn)品類的類名促王,只需要知道具體產(chǎn)品類所對(duì)應(yīng)的參數(shù)即可,對(duì)于一些復(fù)雜的類名而晒,通過(guò)簡(jiǎn)單工廠模式可以減少使用者的記憶量蝇狼。
- 通過(guò)引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產(chǎn)品類倡怎,在一定程度上提高了系統(tǒng)的靈活性迅耘。
6.2贱枣、缺點(diǎn)
- 由于工廠類集中了所有產(chǎn)品創(chuàng)建邏輯,一旦不能正常工作颤专,整個(gè)系統(tǒng)都要受到影響纽哥。
- 使用簡(jiǎn)單工廠模式將會(huì)增加系統(tǒng)中類的個(gè)數(shù),在一定程序上增加了系統(tǒng)的復(fù)雜度和理解難度栖秕。
- 系統(tǒng)擴(kuò)展困難春塌,一旦添加新產(chǎn)品就不得不修改工廠邏輯,同樣破壞了“開閉原則”簇捍;在產(chǎn)品類型較多時(shí)只壳,有可能造成工廠邏輯過(guò)于復(fù)雜,不利于系統(tǒng)的擴(kuò)展和維護(hù)暑塑。
- 簡(jiǎn)單工廠模式由于使用了靜態(tài)工廠方法,造成工廠角色無(wú)法形成基于繼承的等級(jí)結(jié)構(gòu)事格。
七惕艳、適用環(huán)境
在以下情況下可以使用簡(jiǎn)單工廠模式:
- 工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少:由于創(chuàng)建的對(duì)象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過(guò)復(fù)雜驹愚。
- 客戶端只知道傳入工廠類的參數(shù)尔艇,對(duì)于如何創(chuàng)建對(duì)象不關(guān)心:客戶端既不需要關(guān)心創(chuàng)建細(xì)節(jié),甚至連類名都不需要記住么鹤,只需要知道類型所對(duì)應(yīng)的參數(shù)终娃。
八、模式應(yīng)用
- JDK類庫(kù)中廣泛使用了簡(jiǎn)單工廠模式蒸甜,如工具類java.text.DateFormat棠耕,它用于格式化一個(gè)本地日期或者時(shí)間。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);
- 獲取不同加密算法的密鑰生成器柠新。
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
- 創(chuàng)建密碼器窍荧。
Cipher cp=Cipher.getInstance("DESede");
九、總結(jié)
- 創(chuàng)建型模式對(duì)類的實(shí)例化過(guò)程進(jìn)行了抽象恨憎,能夠?qū)?duì)象的創(chuàng)建與對(duì)象的使用過(guò)程分離蕊退。
- 簡(jiǎn)單工廠模式又稱為靜態(tài)工廠方法模式,它屬于類創(chuàng)建型模式憔恳。在簡(jiǎn)單工廠模式中瓤荔,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例。簡(jiǎn)單工廠模式專門定義一個(gè)類來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例钥组,被創(chuàng)建的實(shí)例通常都具有共同的父類输硝。
- 簡(jiǎn)單工廠模式包含三個(gè)角色:工廠角色負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有實(shí)例的內(nèi)部邏輯;抽象產(chǎn)品角色是所創(chuàng)建的所有對(duì)象的父類程梦,負(fù)責(zé)描述所有實(shí)例所共有的公共接口点把;具體產(chǎn)品角色是創(chuàng)建目標(biāo)橘荠,所有創(chuàng)建的對(duì)象都充當(dāng)這個(gè)角色的某個(gè)具體類的實(shí)例。
- 簡(jiǎn)單工廠模式的要點(diǎn)在于:當(dāng)你需要什么郎逃,只需要傳入一個(gè)正確的參數(shù)哥童,就可以獲取你所需要的對(duì)象,而無(wú)須知道其創(chuàng)建細(xì)節(jié)褒翰。
- 簡(jiǎn)單工廠模式最大的優(yōu)點(diǎn)在于實(shí)現(xiàn)對(duì)象的創(chuàng)建和對(duì)象的使用分離如蚜,將對(duì)象的創(chuàng)建交給專門的工廠類負(fù)責(zé),但是其最大的缺點(diǎn)在于工廠類不夠靈活影暴,增加新的具體產(chǎn)品需要修改工廠類的判斷邏輯代碼错邦,而且產(chǎn)品較多時(shí),工廠方法代碼將會(huì)非常復(fù)雜型宙。
- 簡(jiǎn)單工廠模式適用情況包括:工廠類負(fù)責(zé)創(chuàng)建的對(duì)象比較少撬呢;客戶端只知道傳入工廠類的參數(shù),對(duì)于如何創(chuàng)建對(duì)象不關(guān)心妆兑。