【設計模式】工廠方法模式

工廠方法模式

介紹

簡單工廠模式雖然簡單当纱,并且實現(xiàn)了創(chuàng)建和使用分離的功能呛每,但存在一個很嚴重的問題。當系統(tǒng)中需要引入新產品時坡氯,由于靜態(tài)工廠方法通過所傳入?yún)?shù)的不同來創(chuàng)建不同的產品晨横,這必定要修改工廠類的源代碼洋腮,將違背“開閉原則”,并且工廠類可能過于龐大手形,出現(xiàn)大量if...else...的語句徐矩,導致維護和測試難度增大,如何實現(xiàn)增加新產品而不影響已有代碼叁幢?工廠方法模式應運而生,本文將介紹第二種工廠模式——工廠方法模式坪稽。

在簡單工廠模式中只提供一個工廠類曼玩,該工廠類處于對產品類進行實例化的中心位置,它需要知道每一個產品對象的創(chuàng)建細節(jié)窒百,并決定何時實例化哪一個產品類黍判。簡單工廠模式最大的缺點是當有新產品要加入到系統(tǒng)中時,必須修改工廠類篙梢,需要在其中加入必要的業(yè)務邏輯顷帖,這違背了“開閉原則”。此外渤滞,在簡單工廠模式中贬墩,所有的產品都由同一個工廠創(chuàng)建,工廠類職責較重妄呕,業(yè)務邏輯較為復雜陶舞,具體產品與工廠類之間的耦合度高,嚴重影響了系統(tǒng)的靈活性和擴展性绪励,而工廠方法模式則可以很好地解決這一問題肿孵。

在工廠方法模式中,我們不再提供一個統(tǒng)一的工廠類來創(chuàng)建所有的產品對象疏魏,而是針對不同的產品提供不同的工廠停做,系統(tǒng)提供一個與產品等級結構對應的工廠等級結構。

工廠方法模式定義

定義一個用于創(chuàng)建對象的接口大莫,讓子類決定將哪一個類實例化。工廠方法模式讓一個類的實例化延遲到其子類只厘。工廠方法模式又簡稱為工廠模式(Factory Pattern),又可稱作虛擬構造器模式(Virtual Constructor Pattern)或多態(tài)工廠模式(Polymorphic Factory Pattern)懈凹。工廠方法模式是一種類創(chuàng)建型模式。

工廠方法模式UML類圖
角色介紹
  • Product(抽象產品):它是定義產品的接口介评,是工廠方法模式所創(chuàng)建對象的超類型库北,也就是產品對象的公共父類爬舰。
  • ConcreteProduct(具體產品):它實現(xiàn)了抽象產品接口寒瓦,某種類型的具體產品由專門的具體工廠創(chuàng)建情屹,具體工廠和具體產品之間一一對應杂腰。
  • Factory(抽象工廠):在抽象工廠類中垃你,聲明了工廠方法(Factory Method),用于返回一個產品喂很。抽象工廠是工廠方法模式的核心惜颇,所有創(chuàng)建對象的工廠類都必須實現(xiàn)該接口。
  • ConcreteFactory(具體工廠):它是抽象工廠類的子類凌摄,實現(xiàn)了抽象工廠中定義的工廠方法,并可由客戶端調用锨亏,返回一個具體產品類的實例。
與簡單工廠模式的區(qū)別

最重要的區(qū)別就是引入了抽象工廠角色器予,抽象工廠可以是接口,也可以是抽象類或者具體類劣摇。
在抽象工廠中聲明了工廠方法但并未實現(xiàn)工廠方法,具體產品對象的創(chuàng)建由其子類負責末融,客戶端針對抽象工廠編程,可在運行時再指定具體工廠類勾习,具體工廠類實現(xiàn)了工廠方法,不同的具體工廠可以創(chuàng)建不同的具體產品巧婶。

interface Factory {
    public Product factoryMethod();
}

class ConcreteFactory implements Factory {
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}

// 客戶端創(chuàng)建產品
Factory factory;
factory = new ConcreteFactory(); //可通過配置文件實現(xiàn)
Product product;
product = factory.factoryMethod();
demo

使用工廠模式設計日志記錄器涂乌。

Logger接口充當抽象產品艺栈,其子類FileLogger和DatabaseLogger充當具體產品湾盒,LoggerFactory接口充當抽象工廠,其子類FileLoggerFactory和DatabaseLoggerFactory充當具體工廠罚勾。

//日志記錄器接口:抽象產品  
interface Logger {  
    public void writeLog();  
}  
  
//數(shù)據(jù)庫日志記錄器:具體產品  
class DatabaseLogger implements Logger {  
    public void writeLog() {  
        System.out.println("數(shù)據(jù)庫日志記錄吭狡。");  
    }  
}  
  
//文件日志記錄器:具體產品  
class FileLogger implements Logger {  
    public void writeLog() {  
        System.out.println("文件日志記錄。");  
    }  
}  
  
//日志記錄器工廠接口:抽象工廠  
interface LoggerFactory {  
    public Logger createLogger();  
}  
  
//數(shù)據(jù)庫日志記錄器工廠類:具體工廠  
class DatabaseLoggerFactory implements LoggerFactory {  
    public Logger createLogger() {  
            //連接數(shù)據(jù)庫划煮,代碼省略  
            //創(chuàng)建數(shù)據(jù)庫日志記錄器對象  
            Logger logger = new DatabaseLogger();   
            //初始化數(shù)據(jù)庫日志記錄器缔俄,代碼省略  
            return logger;  
    }     
}  
  
//文件日志記錄器工廠類:具體工廠  
class FileLoggerFactory implements LoggerFactory {  
    public Logger createLogger() {  
            //創(chuàng)建文件日志記錄器對象  
            Logger logger = new FileLogger();   
            //創(chuàng)建文件弛秋,代碼省略  
            return logger;  
    }     
}  

客戶端使用代碼:

class Client {  
    public static void main(String args[]) {  
        LoggerFactory factory;  
        Logger logger;  
        factory = new FileLoggerFactory(); //可引入配置文件實現(xiàn)  
        logger = factory.createLogger();  
        logger.writeLog();  
    }  
}  
優(yōu)化代碼
動態(tài)加載工廠類

為了讓系統(tǒng)具有更好的靈活性和可擴展性俐载,不再使用new關鍵字來創(chuàng)建工廠對象,而是將具體工廠類名存儲在配置文件中瞎疼,通過讀取配置文件獲取類名字符串,再反射出具體的類壁畸。

重載工廠方法

工廠方法可能會存在多種途徑來創(chuàng)建產品贼急,比如日志記錄系統(tǒng),可以從文件中讀取捏萍,也可以從數(shù)據(jù)庫讀取太抓。
在抽象工廠中定義多個重載的工廠方法,在具體工廠中實現(xiàn)了這些工廠方法令杈,這些方法可以包含不同的業(yè)務邏輯走敌,以滿足對不同產品對象的需求。

引入重載方法后逗噩,抽象工廠LoggerFactory的代碼修改如下:

interface LoggerFactory {  
    public Logger createLogger();  
    public Logger createLogger(String args);  
    public Logger createLogger(Object obj);  
}  
工廠方法的隱藏

有時候,為了進一步簡化客戶端的使用异雁,還可以對客戶端隱藏工廠方法赃额,此時李剖,在工廠類中將直接調用產品類的業(yè)務方法锭部,客戶端無須調用工廠方法創(chuàng)建產品,直接通過工廠即可使用所創(chuàng)建的對象中的業(yè)務方法拌禾。

//改為抽象類  
abstract class LoggerFactory {  
    //在工廠類中直接調用日志記錄器類的業(yè)務方法writeLog()  
    public void writeLog() {  
        Logger logger = this.createLogger();  
        logger.writeLog();  
    }  
      
    public abstract Logger createLogger();    
} 
class Client {  
    public static void main(String args[]) {  
        LoggerFactory factory;  
        factory = (LoggerFactory)XMLUtil.getBean();  
        factory.writeLog(); //直接使用工廠對象來調用產品對象的業(yè)務方法  
    }  
}  
Cocoa框架中的使用
NSNumber

盡管NSNumber可以使用常見的alloc init方式創(chuàng)建NSNumber展哭,但這樣創(chuàng)建出來的對象仍然是nil扼菠,只能使用預先定義的工廠方法來創(chuàng)建有意義的實例。例如

[NSNumber numberWithBool:YES]

此消息會得到一個NSNumber的子類NSCFBoolean的一個實例循榆。

NSNumber就是工廠模式的一個變體墨坚,NSNumber本身是一個抽象父類,直接由抽象類生成具體子類

總結
優(yōu)點
  • 在工廠方法模式中泽篮,工廠方法用來創(chuàng)建客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被實例化這一細節(jié)帽撑,用戶只需要關心所需產品對應的工廠,無須關心創(chuàng)建細節(jié)亏拉,甚至無須知道具體產品類的類名。
  • 基于工廠角色和產品角色的多態(tài)性設計是工廠方法模式的關鍵莽使。它能夠讓工廠可以自主確定創(chuàng)建何種產品對象,而如何創(chuàng)建這個對象的細節(jié)則完全封裝在具體工廠內部芳肌。工廠方法模式之所以又被稱為多態(tài)工廠模式肋层,就正是因為所有的具體工廠類都具有同一抽象父類亿笤。
  • 使用工廠方法模式的另一個優(yōu)點是在系統(tǒng)中加入新產品時栋猖,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端掂铐,也無須修改其他的具體工廠和具體產品罕拂,而只要添加一個具體工廠和具體產品就可以了全陨,這樣,系統(tǒng)的可擴展性也就變得非常好辱姨,完全符合“開閉原則”。
缺點
  • 在添加新產品時枢舶,需要編寫新的具體產品類懦胞,而且還要提供與之對應的具體工廠類凉泄,系統(tǒng)中類的個數(shù)將成對增加,在一定程度上增加了系統(tǒng)的復雜度后众,有更多的類需要編譯和運行,會給系統(tǒng)帶來一些額外的開銷蒂誉。
  • 由于考慮到系統(tǒng)的可擴展性,需要引入抽象層括堤,在客戶端代碼中均使用抽象層進行定義,增加了系統(tǒng)的抽象性和理解難度悄窃,且在實現(xiàn)時可能需要用到DOM登夫、反射等技術广匙,增加了系統(tǒng)的實現(xiàn)難度恼策。
適用場景
  • 客戶端不知道它所需要的對象的類(編譯時無法準確預期要創(chuàng)建的對象的類)潮剪。在工廠方法模式中,客戶端不需要知道具體產品類的類名抗碰,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創(chuàng)建弧蝇,可將具體工廠類的類名存儲在配置文件或數(shù)據(jù)庫中。
  • 抽象工廠類通過其子類來指定創(chuàng)建哪個對象(類想讓其子類決定在運行時創(chuàng)建什么)沙峻。在工廠方法模式中两芳,對于抽象工廠類只需要提供一個創(chuàng)建產品的接口摔寨,而由其子類來確定具體要創(chuàng)建的對象怖辆,利用面向對象的多態(tài)性和里氏代換原則删顶,在程序運行時淑廊,子類對象將覆蓋父類對象,從而使得系統(tǒng)更容易擴展蒋纬。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜀备,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌输虱,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宪睹,死亡現(xiàn)場離奇詭異蚕钦,居然都是意外死亡,警方通過查閱死者的電腦和手機嘶居,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來整袁,“玉大人,你說我怎么就攤上這事坐昙∮蠓蓿” “怎么了炸客?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵戈钢,是天一觀的道長。 經(jīng)常有香客問我蝶溶,道長,這世上最難降的妖魔是什么抖所? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮田轧,結果婚禮上,老公的妹妹穿的比我還像新娘每窖。我一直安慰自己,他們只是感情好窒典,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布稽莉。 她就那樣靜靜地躺著,像睡著了一般污秆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上良拼,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音常侦,去河邊找鬼。 笑死刮吧,一個胖子當著我的面吹牛湖饱,可吹牛的內容都是我干的。 我是一名探鬼主播蚓庭,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼仅仆,長吁一口氣:“原來是場噩夢啊……” “哼器赞!你這毒婦竟也來了墓拜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤夏醉,失蹤者是張志新(化名)和其女友劉穎爽锥,沒想到半個月后畔柔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡腮考,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年玄捕,在試婚紗的時候發(fā)現(xiàn)自己被綠了踩蔚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枚粘。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赌结,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情拟杉,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布搬设,位于F島的核電站撕捍,受9級特大地震影響拿穴,放射性物質發(fā)生泄漏忧风。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一腿宰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吃度,春花似錦、人聲如沸椿每。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兑牡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間均函,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工洛勉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人收毫。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓殷勘,卻偏偏與公主長得像此再,于是被迫代替她去往敵國和親玲销。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內容

  • 設計原則: 要依賴抽象策吠,不要依賴具體類 目錄 本文的結構如下: 什么是工廠方法模式 為什么要用該模式 模式的結構 ...
    w1992wishes閱讀 1,347評論 0 6
  • 設計模式匯總 一瘩绒、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用猴抹、多...
    MinoyJet閱讀 3,948評論 1 15
  • 工廠方法模式——軟件設計模式 例子:女媧造人 涉及到3個對象:女媧锁荔、八卦爐、三種不同膚色的人(白人堕战、黑人拍霜、黃種人)...
    書筆年華閱讀 518評論 0 0
  • 前言 Android的設計模式系列文章介紹:歡迎關注,持續(xù)更新中: Android的設計模式-設計模式的六大原則一...
    四月葡萄閱讀 10,022評論 2 20
  • 有些話以我現(xiàn)在的年紀來講越驻,不免會有點裝作少年老成的格調,但當下的情感卻也不是霎時的感悟缀旁,而是一年年的積累记劈。從...
    錢十四閱讀 320評論 0 0