新同事說工廠模式有啥用,別學了
工廠模式有啥用啊,我的項目沒使用工廠模式也照樣運行
這是我聽過最令人哭笑不得的吐槽,這個程序猿的頭發(fā)不知道有沒有被自己薅禿
的確,項目中不使用工廠模式并不會影響項目的運行
但是,當項目后期需要二次開發(fā)時,代碼的維護和修改的復(fù)雜度,絕對能讓你恨不得把自己頭發(fā)都薅禿
下面我們就來盤一盤工廠模式能解決哪些問題
簡單工廠模式
實際案例
假如客戶有這樣一個需求,做一個用戶訂購手機來玩游戲的項目
項目中可以生產(chǎn)華為和小米的手機,生產(chǎn)的手機只能用來玩游戲,用戶可以通過京東和淘寶來訂購手機
需求中的一個前置條件是手機只能用來玩游戲,不能做別的事情。這就類似于一個規(guī)范,所有的手機都要遵守這個規(guī)范。
制定規(guī)范是java中接口的特性,所以我們要定義一個接口基類,叫做Phone,里面有一個玩游戲的方法play()。
還要有華為手機和小米手機的類,分別叫做Huwei和Xiaomi,這兩個手機類要遵循手機只能用來玩游戲這個規(guī)范,所以它們要實現(xiàn)Phone類,并完成play()方法

用java代碼實現(xiàn)分別如下
phone基類
public interface Phone {
void play();
}
華為手機類Huawei
public class Huawei implements Phone {
@Override
public void play() {
System.out.println("華為手機玩游戲");
}
}
小米手機類Xiaomi
public class Xiaomi implements Phone {
@Override
public void play() {
System.out.println("小米手機玩游戲");
}
}
用戶可以通過京東和淘寶來訂購手機,所以還要有京東和淘寶的類,分別叫做Jingdong和Taobao。類里面各有一個訂購方法叫做order(),可以根據(jù)用戶的喜好來訂購不同的手機
如果用戶喜歡華為則訂購華為手機來玩游戲,如果用戶喜歡小米則訂購小米手機來玩游戲,其他的用戶就不能玩游戲

因為京東類和淘寶類的代碼邏輯一摸一樣,這里只貼一下京東類的代碼
public class Jingdong {
public void order(String type) {
Phone phone = null;
if ("huawei".equals(type)) {
phone = new Huawei();
phone.play();
} else if ("xiaomi".equals(type)) {
phone = new Xiaomi();
phone.play();
} else {
System.out.println("暫不支持");
}
}
}
這樣我們就實現(xiàn)了客戶的需求,而且沒有使用任何設(shè)計模式。
項目雖然可以完美的運行,但是有一個問題值得我們思考,假如又增加了蘋果手機,這時候我們的代碼該怎么修改
首先,我們肯定是要增加蘋果手機類IPhone,并且實現(xiàn)Phone基類

京東類中訂購方法的邏輯需要做出相應(yīng)的修改

淘寶類中訂購方法的邏輯也需要做出相應(yīng)的修改,修改的地方和京東類一模一樣。這時我們就發(fā)現(xiàn)同樣的代碼需要修改兩次。

如果訂購類有很多,除了京東、淘寶,還有拼多多、微信商城等等。那就意味著同樣的代碼不止要修改兩次,有多少個訂購類就需要修改多少次
這個工作量是很大的,而且極其容易出錯,就問你頭禿不頭禿
這時候就需要使用簡單工廠模式來優(yōu)化我們的代碼
簡單工廠模式就是創(chuàng)建一個工廠類,由這個類來封裝實例化對象的行為
套用到這個例子中就是:創(chuàng)建一個工廠類,由工廠類來封裝創(chuàng)建手機的邏輯。訂購類從工廠類中獲取手機對象直接使用,不再關(guān)心手機創(chuàng)建的過程

工廠類PhoneFactory用代碼實現(xiàn)就是這樣
public class PhoneFactory {
public static Phone createPhone(String type) {
Phone phone = null;
if ("huawei".equals(type)) {
phone = new Huawei();
} else if ("xiaomi".equals(type)) {
phone = new Xiaomi();
} else if ("apple".equals(type)) {
phone = new IPhone();
} else {
System.out.println("暫不支持");
}
return phone;
}
}
京東、淘寶等訂購類從工廠類里面獲取手機對象后直接使用,不再關(guān)心手機的創(chuàng)建過程(京東和淘寶類的代碼一樣,這里只貼京東類的代碼)
public class Jingdong {
public void order(String type) {
Phone phone = PhoneFactory.createPhone(type);
if (phone != null) {
phone.play();
}
}
}
這就實現(xiàn)了簡單工廠模式,以后再需要增加手機型號時只需要修改工廠類就行了,其他代碼不用修改
簡單工廠模式總結(jié)
簡單工廠模式就是創(chuàng)建一個工廠類,根據(jù)傳入的參數(shù)類型來創(chuàng)建具體的產(chǎn)品對象,并返回產(chǎn)品對象的實例
主要適用于調(diào)用者不知道應(yīng)該創(chuàng)建哪個具體的對象,只能根據(jù)傳入的條件返回相應(yīng)對象的場景
比如案例中,訂購類是不知道要創(chuàng)建哪個手機對象的,只能根據(jù)用戶提供的條件才能知道需要哪個手機對象
簡單工廠模式的好處在于將對象的創(chuàng)建過程和使用過程進行解耦,減少新增具體產(chǎn)品時修改代碼的復(fù)雜度
就像上面的例子一樣,對象的創(chuàng)建過程由工廠類負責,訂購類不需要關(guān)心對象是怎么創(chuàng)建的,只需要從工廠類獲取對象來使用即可
當需要增加手機對象時,只需要修改工廠類,而不需要對每一個訂購類進行修改
簡單工廠模式的缺點在于每次新增具體產(chǎn)品時,都需要修改工廠類,這違背了設(shè)計模式中的開閉原則。而且當具體的產(chǎn)品比較多時,工廠類的if-else判斷就會比較多,代碼不美觀
工廠方法模式
實際案例
基于剛才用戶訂購手機玩游戲的需求,我們稍微改動一下。
為了實現(xiàn)精準營銷,京東、淘寶等商城分別上線了華為專賣店、小米專賣店和蘋果專賣店
當用戶進入華為專賣店,就默認用戶要訂購華為手機;當用戶進入小米專賣店,就默認用戶要訂購小米手機;當用戶進入蘋果專賣店,就默認用戶要訂購蘋果手機

這個需求用剛才我們講的簡單工廠模式也可以實現(xiàn)
但是簡單工廠的缺點也很明顯,當新增粉絲類型時需要修改工廠類,當粉絲類型過多時工廠類的邏輯就會比較繁雜
比如新增了vivo粉絲,工廠類就需要新增判斷條件去創(chuàng)建vivo手機對象;新增了oppo手機,工廠類就要新增判斷條件去創(chuàng)建oppo手機對象。一直不斷的新增下去的話,就會導致工廠類的中的判斷過多,代碼很長,后期不容易維護
而且,簡單工廠模式是適用于調(diào)用者不知道應(yīng)該創(chuàng)建哪種對象的場景。
在這個需求中,京東等訂購類中為不同的粉絲提供了專賣店,假如專賣店是訂購類中的一個方法的話,在專賣店這個方法中是知道應(yīng)該創(chuàng)建什么樣的對象的。比如,在華為手機的訂購方法中,是知道要創(chuàng)建華為手機對象的
所以,這個需求可以用工廠方法模式來實現(xiàn)
工廠方法模式和簡單工廠模式相似,也需要有一個工廠類。不過在工廠方法模式中,工廠類不再負責創(chuàng)建對象。因為在每個訂購方法中已經(jīng)知道應(yīng)該創(chuàng)建哪個手機對象,所以創(chuàng)建對象的邏輯下沉到訂購類的方法中

工廠類只負責制定規(guī)范,來約束每個產(chǎn)品的具體行為,所以工廠類是一個接口基類。每個對應(yīng)的產(chǎn)品需要有一個自己的工廠,來繼承這個基類,達到約束自身行為的目的
這個需求中工廠基類規(guī)定每個工廠只能有一個方法,這個方法的作用就是創(chuàng)建手機對象

工廠基類PhoneFactory用代碼實現(xiàn)
public interface PhoneFactory {
Phone createPhone();
}
華為工廠類用代碼實現(xiàn)
public class HuaweiFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new Huawei();
}
}
小米工廠類用代碼實現(xiàn)
public class XiaomiFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new Xiaomi();
}
}
在訂購類中,不同的訂購方法調(diào)用不同的工廠獲取對象。比如京東訂購類的代碼如下(淘寶訂購類處理邏輯類似,這里不再貼淘寶類的代碼)
public class Jingdong {
// 華為粉絲訂購華為手機
public void orderHuawei() {
PhoneFactory phoneFactory = new HuaweiFactory();
Phone phone = phoneFactory.createPhone();
phone.play();
}
// 小米粉絲訂購小米手機
public void orderXiaomi() {
PhoneFactory phoneFactory = new XiaomiFactory();
Phone phone = phoneFactory.createPhone();
phone.play();
}
}
這樣我們就用工廠方法模式實現(xiàn)了為不同粉絲訂購不同手機的需求
工廠方法模式總結(jié)
工廠方法模式是定義一個工廠接口基類,基類中定義一個創(chuàng)建產(chǎn)品的抽象方法。每個產(chǎn)品需要有自己的工廠來實現(xiàn)這個基類,并完成創(chuàng)建對應(yīng)產(chǎn)品實例的方法,由具體的產(chǎn)品調(diào)用該方法來創(chuàng)建對象
它主要適用于調(diào)用者已經(jīng)明確知道需要創(chuàng)建哪一個具體產(chǎn)品的場景
比如,針對華為的粉絲,已經(jīng)明確知道要創(chuàng)建華為的手機產(chǎn)品
工廠方法模式的優(yōu)勢在于完全符合了開閉原則,在新增產(chǎn)品時不需要再改動已存在的代碼,使工廠類和產(chǎn)品類的代碼完全解耦,更利于程序的擴展
他的缺點也很明顯,當新增產(chǎn)品時,需要同時新增產(chǎn)品類和工廠類,導致系統(tǒng)中的類是成對增加,增加了系統(tǒng)的復(fù)雜度
抽象工廠模式
實際案例
基于工廠方法模式的案例,我們再進一步擴展
用戶不單單想訂購手機來玩游戲,還想訂購ipad和電腦
可以用剛才講的工廠方法模式來實現(xiàn):我們不僅需要提供手機工廠的基類,還需要提供ipad工廠基類和電腦工廠基類,并且為每個工廠基類提供具體的工廠實現(xiàn)類

訂購類方法中,根據(jù)不同的需求來創(chuàng)建不同的產(chǎn)品供用戶使用

這樣實現(xiàn)的代碼沒有問題,但是不符合我們真實開發(fā)中的業(yè)務(wù)場景
在實際業(yè)務(wù)場景中,京東商城的華為專賣店想要訂購手機不需要到華為公司的手機部門去訂購吧?想要訂購ipad不需要到華為公司的ipad部門訂購吧?想要訂購電腦也不需要到華為公司的電腦部門訂購吧?
京東商城的華為專賣店應(yīng)該只負責和華為公司對接,和具體的業(yè)務(wù)部門沒關(guān)系。專賣店想要訂購某個產(chǎn)品去告訴華為公司,由公司去給具體的業(yè)務(wù)部門溝通
所以,從實際的使用場景出發(fā),我們的代碼應(yīng)該這樣設(shè)計

不再使用單獨的手機工廠、ipad工廠和PC工廠,而是把同一個廠家作為工廠,由工廠分別創(chuàng)建不同的產(chǎn)品
Factory基類實現(xiàn)代碼如下
public interface Factory {
Phone createPhone();
IPad createIPad();
PC createPC();
}
華為工廠類實現(xiàn)代碼如下
public class HuaweiFactory implements Factory {
@Override
public Phone createPhone() {
return new HuaweiPhone();
}
@Override
public IPad createIPad() {
return new HuaweiIPad();
}
@Override
public PC createPC() {
return new HuaweiPC();
}
}
小米工廠實現(xiàn)代碼如下
public class XiaomiFactory implements Factory {
@Override
public Phone createPhone() {
return new XiaomiPhone();
}
@Override
public IPad createIPad() {
return new XiaomiIPad();
}
@Override
public PC createPC() {
return new XiaomiPC();
}
}
在京東訂購類中,我們只需要創(chuàng)建對應(yīng)的工廠對象,由工廠對象創(chuàng)建不同的產(chǎn)品
public class Jingdong {
// 華為粉絲訂購手機、ipad、電腦
public void orderHuawei() {
Factory factory = new HuaweiFactory();
Phone phone = factory.createPhone();
phone.play();
IPad ipad = factory.createIPad();
ipad.play();
PC pc = factory.createPC();
pc.play();
}
// 小米粉絲訂購手機、ipad、電腦
public void orderXiaomi() {
Factory factory = new XiaomiFactory();
Phone phone = factory.createPhone();
phone.play();
IPad ipad = factory.createIPad();
ipad.play();
PC pc = factory.createPC();
pc.play();
}
}
這樣我們就用抽象工廠模式實現(xiàn)了用戶訂購手機、ipad和電腦的需求
抽象工廠模式總結(jié)
抽象工廠模式是將具有一定共性的產(chǎn)品封裝到一塊,由工廠類分別為這些產(chǎn)品提供創(chuàng)建對象的方法,調(diào)用者可以根據(jù)不同的需求調(diào)用工廠類的具體方法來獲得產(chǎn)品實例
比如案例中華為的手機、ipad和電腦都屬于華為公司產(chǎn)品,所以可以由華為工廠類來負責分別創(chuàng)建不同的對象
它的優(yōu)勢在于將具有一定共性的產(chǎn)品集合封裝到一起,在實際開發(fā)中更符合具體的業(yè)務(wù)場景
他的缺點就是降低了系統(tǒng)的擴展性,當新增產(chǎn)品時需要修改工廠類,在工廠類的基類和實現(xiàn)類中都需要增加對應(yīng)的方法
比如說,用戶也想訂購VR眼鏡來玩游戲。那么工廠基類中需要增加創(chuàng)建VR眼鏡的方法,所有的工廠實現(xiàn)類中都需要增加對該方法的實現(xiàn),系統(tǒng)擴展性比較差
— 【 THE END 】— 本公眾號全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲??! 最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點“在看”,關(guān)注公眾號并回復(fù) PDF 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。
謝謝支持喲 (*^__^*)
