Head First 設計模式學習——簡單工廠方法-工廠方法模式-抽象工廠模式
設計模式是進階高階開發的必經之路。
工廠方法模式(Factory Method Pattern)是《Head First 設計模式》介紹的第四個模式,只要你認真讀完本篇博文,一定能很好地掌握此模式。
定義
工廠方法模式通過讓子類決定該建立的物件是什麼,來達到將物件建立的過程封裝的目的
分析
程式要執行,需要建立各種物件。而我們之前為了鬆耦合、高可用做的努力,最後在main方法裡,都需要例項化具體的類物件才行,比如:
Noodles noodle= new BeefNoodles();//例項化牛肉麵
Beverage beverage = new MilkyTea();//原味奶茶
我們之前提倡的原則:面向介面程式設計,不面向實現程式設計。可一旦new 具體物件,程式立馬有了瑕疵。假設需要例項化多種子類物件,呼叫方法裡就要寫各種new,呼叫類裡就需要引入所有依賴的子類:
import BeefNoodles;//引入所有以來的實現型別
import DuckLegNoodles;
public order(){
Noodles noodle = createNoodle("牛肉麵");
noodle.cost();//計算價格
noodle.send();//上菜
}
public createNoodle(面型別){
Noodles noodle;
//寫在客戶端程式碼裡的例項化
if(牛肉麵){
noodle = new BeefNoodles();//牛肉麵一份
}else (鴨腿面){
noodle = new DuckLegNoodles();//鴨腿面一份
}
}
noodle.doSomething();
若是以後需要的物件型別發生變化,此段程式碼必定要頻繁修改,造成隱患。根據第一原則:把變化的部分抽取、封裝。客戶端的邏輯可以簡化為:獲取物件——物件做些事情。我們把獲取物件的邏輯交給一個專門負責此類事情的高手去做:簡單工廠SimpleFactory。
老將出馬,一個頂倆
public class SimpleFactory{
public Noodles getNoodles(引數){//可以為static方法
//建立物件的邏輯讓SimpleFactory具體負責。
Noodles noodle;
if(牛肉麵){
noodle = new BeefNoodles();//牛肉麵一份
}else (鴨腿面){
noodle = new DuckLegNoodles();//鴨腿面一份
}
return noodle;
}
}
//客戶端呼叫
Noodles noodle = SimpleFactory.getNoodles(引數);
目前來看,我們只是把程式碼換了個位置,然後從客戶端呼叫,並沒有特別出彩的設計。不過有一點:客戶端已經不需要依賴各種具體實現了,因為不管什麼面都是面(超型別),客戶端獲得引用直接呼叫即可。但是,如果現在新增一個店也需要賣麵條。按照原來的方式,它需要寫一段例項化麵條的程式碼,並具有同樣的風險。現在,有了老司機:SimpleFactory,它只需將它的需求告訴簡單工廠,自然就能獲得自己想要的物件。這樣,以後不管多個客戶端,都可以複用簡單工廠。
這種把建立物件的邏輯封裝起來並提供簡單的對外介面,稱為簡單工廠模式(其實算不上一個模式,畢竟實在很簡單)。
客戶太多,老將扛不住
快樂的時光總是短暫的。
麵條太好吃,大家想開加盟店,開到北京上海,天津廣州雲南去。中國幅員遼闊,各地的口味不盡相同,為了適應當地風情,需要多款麵條產品,蘭州牛肉拉麵,北京牛肉擀麵,天津牛肉炒麵。。。(有沒有餓),大家向SimpleFactory請求拉麵,傳入品種標識,SimpleFactory裡的getNoodles()方法不斷增長:
public Noodles getNoodles(引數){//可以為static方法
//建立物件的邏輯讓SimpleFactory具體負責。
Noodles noodle;
if(牛肉麵){
noodle = new BeefNoodles();//牛肉麵一份
}else if(鴨腿面){
noodle = new DuckLegNoodles();//鴨腿面一份
}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}....
return noodle;
}
這肯定不是個好辦法,畢竟一個工廠產能有限,要開發生產太多品類,也不利用積累,分散了精力。只能多開幾個工廠,把加盟店劃分區域,一個區域一個工廠,負責此區域內加盟店的麵條需求。
//客戶端呼叫
Noodles noodle = BeijingFactory.getNoodles(引數);//北京工廠
Noodles noodle = LanzhouFactory.getNoodles(引數);//蘭州工廠
...
本著抽象、封裝的原則,我們可以把SimpleFactory抽象成介面,因為工廠都是負責做麵條而已。只不過不同工廠做的步驟、原料不一樣(實現不同,但上層並不關心)
public interface NoodlesFactory{
Noodles getNoodles(引數);
}
class BeijingFactory implements NoodlesFactory{
public Noodles getNoodles(引數){
Noodles noodle;
//根據引數,專門生產北京地區風味的麵條
return noodle;
}
}
class LanzhouFactory implements NoodlesFactory{
public Noodles getNoodles(引數){
Noodles noodle;
//根據引數,專門生產蘭州地區麵條
return noodle;
}
}
//客戶端:
持有一個麵條工廠引用:
class BeijingStory{
NoodlesFactory noodlesFactory;
public BeijingStory(){
noodlesFactory = new BeijingFactory();
}
//下單
public order(){
//執行時才能知道是哪個工廠來生產自己需要的麵條
Noodles noodle = noodlesFactory.getNoodles("牛肉麵");
noodle.cost();//計算價格
noodle.send();//上菜
}
}
蘭州、廣州店同樣如此,找一個能做本地口味麵條的工廠(實現NoodlesFactory介面),就能繼續用訂單系統,不用改動太多。此時把簡單工廠升級,變成了工廠方法模式。可以看到,工廠方法模式是在簡單工廠上的進一步抽象、封裝。讓下層有更大、更多的實現自由,提高設計彈性,保留複用(工廠可以供應好幾個門店)。
升級——抽象工廠模式
簡單工廠方法——>工廠方法模式——>抽象工廠模式 是一套頂尖功法,不用自宮,也可成功。
本著誠信經營、真材實料的原則,所有門店生意都很好。但世界永遠在變,不可能永遠只賣麵條,別人想吃黃燜雞、瓦罐煨湯等國家著名連鎖小吃的時候,就不來店裡去別家了。為了解決這個問題,門店需要增加商品種類。按照之前的工廠方法模式,我們可以新增黃燜雞工廠YellowChickFactory
和瓦罐煨湯工廠CrockSoupFactory
:
public interface YellowChickFactory{
YellowChick getYellowChick(String size);
}
class BeijingYellowChickFactory{
public YellowChick getYellowChick(String size){
//生產北京地區的黃燜雞,大份、中份、小份等
return new BeijingYellowChick(size);
}
}
//CrockSoupFactory 程式碼略...
這樣設計,遵循了我們之前的原則,帶來一定的好處。不過也有不足之處:畢竟麵條不是時時刻刻都需要,在有黃燜雞訂單時,黃燜雞工廠生產,意味著麵條工廠就處停工狀態(綜合來說是這樣,畢竟總客戶資源某種程度上看是固定的),而麵條工廠開工,則黃燜雞工廠停工。
兩個工廠的生產效率極大浪費。此時我們想到,能不能把連個工廠結合起來,即能生產麵條又能做黃燜雞,甚至瓦罐湯?
當然可以,我們可以抽象一個總的工廠介面即抽象工廠FoodFactory,裡面有生產麵條、黃燜雞、瓦罐湯和其他商品的介面。各地區的工廠只需要實現此介面,實現自己的生產流程,就能滿足所有的需求了:
public interface FoodFactory{
Noodles getNoodles(引數);
YellowChick getYellowChick(String size);
//瓦罐湯和其他類似,略
}
class BeijingFactory implements FoodFactory{
public Noodles getNoodles(引數){
Noodles noodle;
///...
//根據引數,專門生產北京地區麵條
return noodle;
}
public YellowChick getYellowChick(String size){
//生產北京地區的黃燜雞,大份、中份、小份等
return new BeijingYellowChick(size);
}
}
//蘭州、廣州的工廠同樣如此,只不過具體的實現邏輯不通
門店呼叫:
//北京門店:
持有一個抽象工廠引用:
class BeijingStory{
FoodFactory foodFactory;
public BeijingStory(){
foodFactory= new BeijingFactory();
}
//麵條下單
public noodlesOrder(){
Noodles noodle = foodFactory.getNoodles("牛肉麵");
...
}
//黃燜雞下單
public noodlesOrder(){
YellowChick chick= getYellowChick.getNoodles("小份");
...
}
}
此時:我們在工廠方法模式的基礎上,合併多個工廠,並抽象出一個抽象工廠。此抽象工廠包含了一組生產物件的方法,子類實現此介面,來滿足系統的呼叫。此模式就是:抽象工廠模式