




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、研磨設(shè)計模式之 工廠方法模式-1 做Java一晃就十年了,最近手癢癢,也決定跟隨一下潮流,整個博客,寫點東西,就算對自己的知識進行一個梳理和總結(jié),也跟朋友們交流交流,希望能堅持下去。 先寫寫設(shè)計模式方面的內(nèi)容吧,就是GoF的23個模式,先從大家最熟悉的工廠方法模式開始,這個最簡單,明白的人多,看看是否能寫出點跟別人不一樣的東西,歡迎大家來熱烈討論,提出建議或意見,并進行批評指正,一概虛心接受,在此先謝過了! 另外,大家也可以說說最想看到哪個模式,那我就先寫它,呵呵,大家感興趣,我才會
2、有動力寫下去!好了,言歸正傳,Now Go!工廠方法模式(Factory Method)1 場景問題1.1 導(dǎo)出數(shù)據(jù)的應(yīng)用框架 考慮這樣一個實際應(yīng)用:實現(xiàn)一個導(dǎo)出數(shù)據(jù)的應(yīng)用框架,來讓客戶選擇數(shù)據(jù)的導(dǎo)出方式,并真正執(zhí)行數(shù)據(jù)導(dǎo)出。 在一些實際的企業(yè)應(yīng)用中,一個公司的系統(tǒng)往往分散在很多個不同的地方運行,比如各個分公司或者是門市點,公司沒有建立全公司專網(wǎng)的實力,但是又不愿意讓業(yè)務(wù)數(shù)據(jù)實時的在廣域網(wǎng)上傳遞,一個是考
3、慮數(shù)據(jù)安全的問題,一個是運行速度的問題。 這種系統(tǒng)通常會有一個折中的方案,那就是各個分公司內(nèi)運行系統(tǒng)的時候是獨立的,是在自己分公司的局域網(wǎng)內(nèi)運行。然后在每天業(yè)務(wù)結(jié)束的時候,各個分公司會導(dǎo)出自己的業(yè)務(wù)數(shù)據(jù),然后把業(yè)務(wù)數(shù)據(jù)打包通過網(wǎng)絡(luò)傳送給總公司,或是專人把數(shù)據(jù)送到總公司,然后由總公司進行數(shù)據(jù)導(dǎo)入和核算。 通常這種系統(tǒng),在導(dǎo)出數(shù)據(jù)上,會有一些約定的方式,比如導(dǎo)出成:文本格式、數(shù)據(jù)庫備份形式、Excel格式、Xml格式等等。
4、; 現(xiàn)在就來考慮實現(xiàn)這樣一個應(yīng)用框架。在繼續(xù)之前,先來了解一些關(guān)于框架的知識。1.2 框架的基礎(chǔ)知識(1):框架是什么 簡單點說:框架就是能完成一定功能的半成品軟件。 就其本質(zhì)而言,框架是一個軟件,而且是一個半成品的軟件。所謂半成品,就是還不能完全實現(xiàn)用戶需要的功能,框架只是實現(xiàn)用戶需要的功能的一部分,還需要進一步加工,才能成為一個滿足用戶需要的
5、、完整的軟件。因此框架級的軟件,它的主要客戶是開發(fā)人員,而不是最終用戶。 有些朋友會想,既然框架只是個半成品,那何必要去學習和使用框架呢?學習成本也不算小,那就是因為框架能完成一定的功能,也就是這“框架已經(jīng)完成的一定的功能”在吸引著開發(fā)人員,讓大家投入去學習和使用框架。(2):框架能干什么 能完成一定功能,加快應(yīng)用開發(fā)進度 由于框架完成了一定的功
6、能,而且通常是一些基礎(chǔ)的、有難度的、通用的功能,這就避免我們在應(yīng)用開發(fā)的時候完全從頭開始,而是在框架已有的功能之上繼續(xù)開發(fā),也就是說會復(fù)用框架的功能,從而加快應(yīng)用的開發(fā)進度。 給我們一個精良的程序架構(gòu) 框架定義了應(yīng)用的整體結(jié)構(gòu),包括類和對象的分割,各部分的主要責任,類和對象怎么協(xié)作,以及控制流程等等。現(xiàn)在Java界大多數(shù)流行的框架,大都出自大師手筆,設(shè)計都很精良?;谶@樣的框架來開發(fā),一般會遵循框架已經(jīng)規(guī)劃好的結(jié)構(gòu)來進行開發(fā),從
7、而讓我們開發(fā)的應(yīng)用程序的結(jié)構(gòu)也相對變得精良了。(3):對框架的理解 基于框架來開發(fā),事情還是那些事情,只是看誰做的問題 對于應(yīng)用程序和框架的關(guān)系,可以用一個圖來簡單描述一下,如圖1所示: 圖1 應(yīng)用程序和框架的簡單關(guān)系示意圖 如果沒有框架,那么客戶要求的所有功能都由開發(fā)人員自己來開發(fā),沒問題,同樣可以
8、實現(xiàn)用戶要求的功能,只是開發(fā)人員的工作多點。 如果有了框架,框架本身完成了一定的功能,那么框架已有的功能,開發(fā)人員就可以不做了,開發(fā)人員只需要完成框架沒有的功能,最后同樣是完成客戶要求的所有功能,但是開發(fā)人員的工作就減少了。 也就是說,基于框架來開發(fā),軟件要完成的功能并沒有變化,還是客戶要求的所有功能,也就是“事情還是那些事情”的意思。但是有了框架過后,框架完成了一部分功能,然后開發(fā)人員再完成一部分功能,最后由框架和開發(fā)人員合起
9、來完成了整個軟件的功能,也就是看這些功能“由誰做”的問題。 基于框架開發(fā),可以不去做框架所做的事情,但是應(yīng)該明白框架在干什么,以及框架是如何實現(xiàn)相應(yīng)功能的 事實上,在實際開發(fā)中,應(yīng)用程序和框架的關(guān)系,通常都不會如上面講述的那樣,分得那么清楚,更為普遍的是相互交互的,也就是應(yīng)用程序做一部分工作,然后框架做一部分工作,然后應(yīng)用程序再做一部分工作,然后框架再做一部分工作,如此交錯,最后由應(yīng)用程序和框架組合起來完成用戶的功能
10、要求。 也用個圖來說明,如圖2所示: 圖2 應(yīng)用程序和框架的關(guān)系示意圖 如果把這個由應(yīng)用程序和框架組合在一起構(gòu)成的矩形,當作最后完成的軟件。試想一下,如果你不懂框架在干什么的話,相當于框架對你來講是個黑盒,也就是相當于在上面圖2中,去掉框架的兩塊,會發(fā)現(xiàn)什么?沒錯,剩下的應(yīng)用
11、程序是支離破碎的,是相互分隔開來的。 這會導(dǎo)致一個非常致命的問題,整個應(yīng)用是如何運轉(zhuǎn)起來的,你是不清楚的,也就是說對你而言,項目已經(jīng)失控了,從項目管理的角度來講,這是很危險的。 因此,在基于框架開發(fā)的時候,雖然我們可以不去做框架所做的事情,但是應(yīng)該搞明白框架在干什么,如果條件許可的話,還應(yīng)該搞清楚框架是如何實現(xiàn)相應(yīng)功能的,至少應(yīng)該把大致的實現(xiàn)思路和實現(xiàn)步驟搞清楚,這樣我們才能整體的掌控整個項目,才能盡量減少出現(xiàn)項目失控的情況。(
12、4):框架和設(shè)計模式的關(guān)系 設(shè)計模式比框架更抽象 框架已經(jīng)是實現(xiàn)出來的軟件了,雖然只是個半成品的軟件,但畢竟是已經(jīng)實現(xiàn)出來的了。而設(shè)計模式的重心還在于解決問題的方案上,也就是還停留在思想的層面。因此設(shè)計模式比框架更為抽象。 設(shè)計模式是比框架更小的體系結(jié)構(gòu)元素
13、160; 如上所述,框架是已經(jīng)實現(xiàn)出來的軟件,并實現(xiàn)了一系列的功能,因此一個框架,通常會包含多個設(shè)計模式的應(yīng)用。 框架比設(shè)計模式更加特例化 框架是完成一定功能的半成品軟件,也就是說,框架的目的很明確,就是要解決某一個領(lǐng)域的某些問題,那是很具體的功能,不同的領(lǐng)域?qū)崿F(xiàn)出來的框架是不一樣的。 而設(shè)計模式還停留在思想的層面,在不同的領(lǐng)域都可以應(yīng)用,只要相應(yīng)的問題適合
14、用某個設(shè)計模式來解決。因此框架總是針對特定領(lǐng)域的,而設(shè)計模式更加注重從思想上,從方法上來解決問題,更加通用化。1.3 有何問題 分析上面要實現(xiàn)的應(yīng)用框架,不管用戶選擇什么樣的導(dǎo)出格式,最后導(dǎo)出的都是一個文件,而且系統(tǒng)并不知道究竟要導(dǎo)出成為什么樣的文件,因此應(yīng)該有一個統(tǒng)一的接口,來描述系統(tǒng)最后生成的對象,并操作輸出的文件。 先把導(dǎo)出的文件對象的接口定義出來,示例代碼如下: /* * 導(dǎo)出的文件對象的接口
15、0;*/public interface ExportFileApi /* * 導(dǎo)出內(nèi)容成為文件 * param data 示意:需要保存的數(shù)據(jù) * return 是否導(dǎo)出成功 */ public boolean export(String data);
16、0; 對于實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象,它應(yīng)該根據(jù)需要來創(chuàng)建相應(yīng)的ExportFileApi的實現(xiàn)對象,因為特定的ExportFileApi的實現(xiàn)是與具體的業(yè)務(wù)相關(guān)的。但是對于實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象而言,它并不知道應(yīng)該創(chuàng)建哪一個ExportFileApi的實現(xiàn)對象,也不知道如何創(chuàng)建。 也就是說:對于實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象,它需要創(chuàng)建ExportFileApi的具體實例對象,但是它只知道ExportFileApi接口,而不知道其具體的實現(xiàn)。那該怎么辦呢?研磨設(shè)計模式之 工廠方法模式-2
17、;2 解決方案2.1 工廠方法模式來解決 用來解決上述問題的一個合理的解決方案就是工廠方法模式。那么什么是工廠方法模式呢?(1)工廠方法模式定義 定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類,F(xiàn)actory Method使一個類的實例化延遲到其子類。 (2)應(yīng)用工廠方法模式來解決的思路 仔細分析上面的問題
18、,事實上在實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象里面,根本就不知道究竟要使用哪一種導(dǎo)出文件的格式,因此這個對象本就不應(yīng)該和具體的導(dǎo)出文件的對象耦合在一起,它只需要面向?qū)С龅奈募ο蟮慕涌诰秃昧恕?#160; 但是這樣一來,又有新的問題產(chǎn)生了:接口是不能直接使用的,需要使用具體的接口實現(xiàn)對象的實例。 這不是自相矛盾嗎?要求面向接口,不讓和具體的實現(xiàn)耦合,但是又需要創(chuàng)建接口的具體實現(xiàn)對象的實例。怎么解決這個矛盾呢?
19、60; 工廠方法模式的解決思路很有意思,那就是不解決,采取無為而治的方式:不是需要接口對象嗎,那就定義一個方法來創(chuàng)建;可是事實上它自己是不知道如何創(chuàng)建這個接口對象的,沒有關(guān)系,那就定義成抽象方法就好了,自己實現(xiàn)不了,那就讓子類來實現(xiàn),這樣這個對象本身就可以只是面向接口編程,而無需關(guān)心到底如何創(chuàng)建接口對象了。2.2 模式結(jié)構(gòu)和說明 工廠方法模式的結(jié)構(gòu)如圖3所示:
20、; 圖3 工廠方法模式結(jié)構(gòu)示意圖Product: 定義工廠方法所創(chuàng)建的對象的接口,也就是實際需要使用的對象的接口。ConcreteProduct: 具體的Product接口的實現(xiàn)對象。Creator: 創(chuàng)
21、建器,聲明工廠方法,工廠方法通常會返回一個Product類型的實例對象,而且多是抽象方法。也可以在Creator里面提供工廠方法的默認實現(xiàn),讓工廠方法返回一個缺省的Product類型的實例對象。ConcreteCreator: 具體的創(chuàng)建器對象,覆蓋實現(xiàn)Creator定義的工廠方法,返回具體的Product實例。2.3 工廠方法模式示例代碼(1)先看看Product的定義,示例代碼如下:/* * 工廠方法所創(chuàng)建的對象的接口 */public interface Product &
22、#160; /可以定義Product的屬性和方法(2)再看看具體的Product的實現(xiàn)對象,示例代碼如下: /* * 具體的Product對象 */public class ConcreteProduct implements Product /實現(xiàn)Product要求的方法(3)接下來看看創(chuàng)建器的定義,示例代碼如下: /* * 創(chuàng)建器,聲明工廠方法 */public abstract class Creator /*
23、0; * 創(chuàng)建Product的工廠方法 * return Product對象 */ protected abstract Product factoryMethod(); /* * 示意方法,實現(xiàn)某些功能的方法 */ public void someOperation()
24、 /通常在這些方法實現(xiàn)中,需要調(diào)用工廠方法來獲取Product對象 Product product = factoryMethod(); (4)再看看具體的創(chuàng)建器實現(xiàn)對象,示例代碼如下: /* * 具體的創(chuàng)建器實現(xiàn)對象 */public class ConcreteCreator extends Creator protected Product f
25、actoryMethod() /重定義工廠方法,返回一個具體的Product對象 return new ConcreteProduct(); 2.4 使用工廠方法模式來實現(xiàn)示例 要使用工廠方法模式來實現(xiàn)示例,先來按照工廠方法模式的結(jié)構(gòu),對應(yīng)出哪些是被創(chuàng)建的Product,哪些是Creator。分析要求實現(xiàn)的功
26、能,導(dǎo)出的文件對象接口ExportFileApi就相當于是Product,而用來實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象就相當于Creator。把Product和Creator分開過后,就可以分別來實現(xiàn)它們了。 使用工廠模式來實現(xiàn)示例的程序結(jié)構(gòu)如圖4所示:
27、; 圖4 使用工廠模式來實現(xiàn)示例的程序結(jié)構(gòu)示意圖 下面一起來看看代碼實現(xiàn)。(1)導(dǎo)出的文件對象接口ExportFileApi的實現(xiàn)沒有變化,這里就不去贅述了(2)接下來看看接口ExportFileApi的實現(xiàn),為了示例簡單,只實現(xiàn)導(dǎo)出文本文件格式和數(shù)據(jù)庫備份文件兩種。先看看導(dǎo)出文本文件格式的實現(xiàn),示例代碼如下: /* * 導(dǎo)出成文本文件格式的對象 */public class ExportTxtFile implements Expor
28、tFileApi public boolean export(String data) /簡單示意一下,這里需要操作文件 System.out.println("導(dǎo)出數(shù)據(jù)"+data+"到文本文件"); return true; 再看看導(dǎo)出成數(shù)據(jù)庫備份文件形式的對象的實
29、現(xiàn),示例代碼如下: /* * 導(dǎo)出成數(shù)據(jù)庫備份文件形式的對象 */public class ExportDB implements ExportFileApi public boolean export(String data) /簡單示意一下,這里需要操作數(shù)據(jù)庫和文件 System.out.println("導(dǎo)出數(shù)據(jù)"+data+"到數(shù)據(jù)庫備
30、份文件"); return true; (3)Creator這邊的實現(xiàn),首先看看ExportOperate的實現(xiàn),示例代碼如下: /* * 實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象 */public abstract class ExportOperate /* * 導(dǎo)出文件 * param data 需要保存的數(shù)據(jù) &
31、#160; * return 是否成功導(dǎo)出文件 */ public boolean export(String data) /使用工廠方法 ExportFileApi api = factoryMethod(); return api.export(data); &
32、#160; /* * 工廠方法,創(chuàng)建導(dǎo)出的文件對象的接口對象 * return 導(dǎo)出的文件對象的接口對象 */ protected abstract ExportFileApi factoryMethod();(4)加入了兩個Creator實現(xiàn),先看看創(chuàng)建導(dǎo)出成文本文件格式的對象,示例代碼如下: /* * 具體的創(chuàng)建器實現(xiàn)對象,實現(xiàn)創(chuàng)建
33、導(dǎo)出成文本文件格式的對象 */public class ExportTxtFileOperate extends ExportOperate protected ExportFileApi factoryMethod() /創(chuàng)建導(dǎo)出成文本文件格式的對象 return new ExportTxtFile(); 再看看創(chuàng)建導(dǎo)出成數(shù)據(jù)庫備份文件形式的對象,示例代碼如下:
34、60; /* * 具體的創(chuàng)建器實現(xiàn)對象,實現(xiàn)創(chuàng)建導(dǎo)出成數(shù)據(jù)庫備份文件形式的對象 */public class ExportDBOperate extends ExportOperate protected ExportFileApi factoryMethod() /創(chuàng)建導(dǎo)出成數(shù)據(jù)庫備份文件形式的對象 return new ExportDB(); (
35、5)客戶端直接創(chuàng)建需要使用的Creator對象,然后調(diào)用相應(yīng)的功能方法,示例代碼如下: public class Client public static void main(String args) /創(chuàng)建需要使用的Creator對象 ExportOperate operate = new ExportDBOperate();
36、160; /調(diào)用輸出數(shù)據(jù)的功能方法 operate.export("測試數(shù)據(jù)"); 運行結(jié)果如下: 導(dǎo)出數(shù)據(jù)測試數(shù)據(jù)到數(shù)據(jù)庫備份文件 你還可以修改客戶端new的對象,切換成其它的實現(xiàn)對象,試試看會發(fā)生什么。看來應(yīng)用工廠方法模式是很簡單的,對吧。研磨設(shè)計模式之 工廠方法模式-33 模式講解3.1 認識工廠方法模式(1)模式的功能&
37、#160; 工廠方法的主要功能是讓父類在不知道具體實現(xiàn)的情況下,完成自身的功能調(diào)用,而具體的實現(xiàn)延遲到子類來實現(xiàn)。 這樣在設(shè)計的時候,不用去考慮具體的實現(xiàn),需要某個對象,把它通過工廠方法返回就好了,在使用這些對象實現(xiàn)功能的時候還是通過接口來操作,這非常類似于IoC/DI的思想,這個在后面給大家稍詳細點介紹一下。(2)實現(xiàn)成抽象類 工廠方法的實現(xiàn)中,通常父類會
38、是一個抽象類,里面包含創(chuàng)建所需對象的抽象方法,這些抽象方法就是工廠方法。 這里要注意一個問題,子類在實現(xiàn)這些抽象方法的時候,通常并不是真的由子類來實現(xiàn)具體的功能,而是在子類的方法里面做選擇,選擇具體的產(chǎn)品實現(xiàn)對象。 父類里面,通常會有使用這些產(chǎn)品對象來實現(xiàn)一定的功能的方法,而且這些方法所實現(xiàn)的功能通常都是公共的功能,不管子類選擇了何種具體的產(chǎn)品實現(xiàn),這些方法的功能總是能正確執(zhí)行。(3)實現(xiàn)成具體的類
39、60; 當然也可以把父類實現(xiàn)成為一個具體的類,這種情況下,通常是在父類中提供獲取所需對象的默認實現(xiàn)方法,這樣就算沒有具體的子類,也能夠運行。 通常這種情況還是需要具體的子類來決定具體要如何創(chuàng)建父類所需要的對象。也把這種情況稱為工廠方法為子類提供了掛鉤,通過工廠方法,可以讓子類對象來覆蓋父類的實現(xiàn),從而提供更好的靈活性。(4)工廠方法的參數(shù)和返回 工廠方法的實現(xiàn)中,可能需要參數(shù),以便
40、決定到底選用哪一種具體的實現(xiàn)。也就是說通過在抽象方法里面?zhèn)鬟f參數(shù),在子類實現(xiàn)的時候根據(jù)參數(shù)進行選擇,看看究竟應(yīng)該創(chuàng)建哪一個具體的實現(xiàn)對象。 一般工廠方法返回的是被創(chuàng)建對象的接口對象,當然也可以是抽象類或者一個具體的類的實例。(5)誰來使用工廠方法創(chuàng)建的對象 這里首先要搞明白一件事情,就是誰在使用工廠方法創(chuàng)建的對象? 事實上,在工廠方法模式里面,應(yīng)
41、該是Creator中的其它方法在使用工廠方法創(chuàng)建的對象,雖然也可以把工廠方法創(chuàng)建的對象直接提供給Creator外部使用,但工廠方法模式的本意,是由Creator對象內(nèi)部的方法來使用工廠方法創(chuàng)建的對象,也就是說,工廠方法一般不提供給Creator外部使用。 客戶端應(yīng)該是使用Creator對象,或者是使用由Creator創(chuàng)建出來的對象。對于客戶端使用Creator對象,這個時候工廠方法創(chuàng)建的對象,是Creator中的某些方法使用。對于使用那些由Creator創(chuàng)建出來的對象,這個時候工廠方法創(chuàng)建的對象,是構(gòu)成客戶端需
42、要的對象的一部分。分別舉例來說明。 客戶端使用Creator對象的情況 比如前面的示例,對于“實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象”的類ExportOperate,它有一個export的方法,在這個方法里面,需要使用具體的“導(dǎo)出的文件對象的接口對象” ExportFileApi,而ExportOperate是不知道具體的ExportFileApi實現(xiàn)的,那么怎么做的呢?就是定義了一個工廠方法,用來返回ExportFileApi的對象,然后export方法會使用這個工廠方法來獲取它所需要
43、的對象,然后執(zhí)行功能。 這個時候的客戶端是怎么做的呢?這個時候客戶端主要就是使用這個ExportOperate的實例來完成它想要完成的功能,也就是客戶端使用Creator對象的情況,簡單描述這種情況下的代碼結(jié)構(gòu)如下: /* * 客戶端使用Creator對象的情況下,Creator的基本實現(xiàn)結(jié)構(gòu) */public abstract class Creator /* * 工廠方法,一般不對外
44、; * return 創(chuàng)建的產(chǎn)品對象 */ protected abstract Product factoryMethod(); /* * 提供給外部使用的方法, * 客戶端一般使用Creator提供的這些方法來完成所需要的功能 */ public void s
45、omeOperation() /在這里使用工廠方法 Product p = factoryMethod(); 客戶端使用由Creator創(chuàng)建出來的對象 另外一種是由Creator向客戶端返回由“工廠方法創(chuàng)建的對象”來構(gòu)建的對象,這個時候工廠方法創(chuàng)建的對象,是構(gòu)成客戶端需要的對象的一部分。簡單描述這種情況下的代碼結(jié)構(gòu)如下:&
46、#160;/* * 客戶端使用Creator來創(chuàng)建客戶端需要的對象的情況下,Creator的基本實現(xiàn)結(jié)構(gòu) */public abstract class Creator /* * 工廠方法,一般不對外,創(chuàng)建一個部件對象 * return 創(chuàng)建的產(chǎn)品對象,一般是另一個產(chǎn)品對象的部件 */ protected abstract Product1 factoryMe
47、thod1(); /* * 工廠方法,一般不對外,創(chuàng)建一個部件對象 * return 創(chuàng)建的產(chǎn)品對象,一般是另一個產(chǎn)品對象的部件 */ protected abstract Product2 factoryMethod2(); /* * 創(chuàng)建客戶端需要的對象,客戶端主要使用產(chǎn)品對象來完成所需要的功能
48、 * return 客戶端需要的對象 */ public Product createProduct() /在這里使用工廠方法,得到客戶端所需對象的部件對象 Product1 p1 = factoryMethod1(); Product2 p2 = f
49、actoryMethod2(); /工廠方法創(chuàng)建的對象是創(chuàng)建客戶端對象所需要的 Product p = new ConcreteProduct(); p.setProduct1(p1); p.setProduct2(p2); &
50、#160; return p; 小結(jié)一下:在工廠方法模式里面,客戶端要么使用Creator對象,要么使用Creator創(chuàng)建的對象,一般客戶端不直接使用工廠方法。當然也可以直接把工廠方法暴露給客戶端操作,但是一般不這么做。(6)工廠方法模式的調(diào)用順序示意圖 由于客戶端使用Creator對象有兩種典型的情況,因此調(diào)用的順序示意圖也分做兩
51、種情況,先看看客戶端使用由Creator創(chuàng)建出來的對象情況的調(diào)用順序示意圖,如圖5所示: 圖5 客戶端使用由Creator創(chuàng)建出來的對象的調(diào)用順序示意圖接下來看看客戶端使用Creator對象時候的調(diào)用順序示意圖,如圖6所示: 圖6 客戶端使用Creator對象的調(diào)用順序示意圖
52、研磨設(shè)計模式之 工廠方法模式-43.2 工廠方法模式與IoC/DI IoCInversion of Control 控制反轉(zhuǎn) DIDependency Injection 依賴注入1:如何理解IoC/DI 要想理解上面兩個概念,就必須搞清楚如下的問題:· 參與者都有誰? · 依賴:誰依賴于誰?為什么需要依賴? · 注入:誰注入于誰?到底注入什么? · 控制反轉(zhuǎn):誰控制誰?控制什么?為何叫反轉(zhuǎn)(
53、有反轉(zhuǎn)就應(yīng)該有正轉(zhuǎn)了)? · 依賴注入和控制反轉(zhuǎn)是同一概念嗎? 下面就來簡要的回答一下上述問題,把這些問題搞明白了,IoC/DI也就明白了。(1)參與者都有誰: 一般有三方參與者,一個是某個對象;一個是IoC/DI的容器;另一個是某個對象的外部資源。 又要名詞解釋一下,某個對象指的就是任意的、普通的Java對象; IoC/DI的容器簡
54、單點說就是指用來實現(xiàn)IoC/DI功能的一個框架程序;對象的外部資源指的就是對象需要的,但是是從對象外部獲取的,都統(tǒng)稱資源,比如:對象需要的其它對象、或者是對象需要的文件資源等等。(2)誰依賴于誰: 當然是某個對象依賴于IoC/DI的容器(3)為什么需要依賴: 對象需要IoC/DI的容器來提供對象需要的外部資源(4)誰注入于誰:
55、 很明顯是IoC/DI的容器 注入 某個對象(5)到底注入什么: 就是注入某個對象所需要的外部資源(6)誰控制誰: 當然是IoC/DI的容器來控制對象了(7)控制什么: 主要是控制對象實例的創(chuàng)建(8)為何叫反轉(zhuǎn): 反轉(zhuǎn)是相對
56、于正向而言的,那么什么算是正向的呢?考慮一下常規(guī)情況下的應(yīng)用程序,如果要在A里面使用C,你會怎么做呢?當然是直接去創(chuàng)建C的對象,也就是說,是在A類中主動去獲取所需要的外部資源C,這種情況被稱為正向的。那么什么是反向呢?就是A類不再主動去獲取C,而是被動等待,等待IoC/DI的容器獲取一個C的實例,然后反向的注入到A類中。 用圖例來說明一下,先看沒有IoC/DI的時候,常規(guī)的A類使用C類的示意圖,如圖7所示:
57、; 圖7 常規(guī)A使用C示意圖當有了IoC/DI的容器后,A類不再主動去創(chuàng)建C了,如圖8所示:
58、160; 圖8 A類不再主動創(chuàng)建C而是被動等待,等待IoC/DI的容器獲取一個C的實例,然后反向的注入到A類中,如圖9所示:
59、0; 圖9 有IoC/DI容器后程序結(jié)構(gòu)示意圖(9)依賴注入和控制反轉(zhuǎn)是同一概念嗎? 根據(jù)上面的講述,應(yīng)該能看出來,依賴注入和控制反轉(zhuǎn)是對同一件事
60、情的不同描述,從某個方面講,就是它們描述的角度不同。依賴注入是從應(yīng)用程序的角度在描述,可以把依賴注入描述完整點:應(yīng)用程序依賴容器創(chuàng)建并注入它所需要的外部資源;而控制反轉(zhuǎn)是從容器的角度在描述,描述完整點:容器控制應(yīng)用程序,由容器反向的向應(yīng)用程序注入應(yīng)用程序所需要的外部資源。(10)小結(jié)一下: 其實IoC/DI對編程帶來的最大改變不是從代碼上,而是從思想上,發(fā)生了“主從換位”的變化。應(yīng)用程序原本是老大,要獲取什么資源都是主動出擊,但是在IoC/DI思想中,應(yīng)用程序就變成被動的了,被動的等待IoC/DI容器來創(chuàng)建并注
61、入它所需要的資源了。 這么小小的一個改變其實是編程思想的一個大進步,這樣就有效的分離了對象和它所需要的外部資源,使得它們松散耦合,有利于功能復(fù)用,更重要的是使得程序的整個體系結(jié)構(gòu)變得非常靈活。2:工廠方法模式和IoC/DI有什么關(guān)系呢? 從某個角度講,它們的思想很類似。 上面講了,有了IoC/DI過后,應(yīng)用程序就不再主動了,而是被動等待由容器來注
62、入資源,那么在編寫代碼的時候,一旦要用到外部資源,就會開一個窗口,讓容器能注入進來,也就是提供給容器使用的注入的途徑,當然這不是我們的重點,就不去細細講了,用setter注入來示例一下,看看使用IoC/DI的代碼是什么樣子,示例代碼如下: public class A /* * 等待被注入進來 */ private C c = null; /*
63、 * 注入資源C的方法 * param c 被注入的資源 */ public void setC(C c) this.c = c; public void t1() /這里需要使用C,可是又不讓主動去創(chuàng)建C了,怎么辦?
64、; /反正就要求從外部注入,這樣更省心, /自己不用管怎么獲取C,直接使用就好了 c.tc(); 接口C的示例代碼如下: public interface C public void tc(); 從上面的示例代碼可以看出,現(xiàn)在在
65、A里面寫代碼的時候,凡是碰到了需要外部資源,那么就提供注入的途徑,要求從外部注入,自己只管使用這些對象。 再來看看工廠方法模式,如何實現(xiàn)上面同樣的功能,為了區(qū)分,分別取名為A1和C1。這個時候在A1里面要使用C1對象,也不是由A1主動去獲取C1對象,而是創(chuàng)建一個工廠方法,就類似于一個注入的途徑;然后由子類,假設(shè)叫A2吧,由A2來獲取C1對象,在調(diào)用的時候,替換掉A1的相應(yīng)方法,相當于反向注入回到A1里面,示例代碼如下: public abstract class A1
66、0; /* * 工廠方法,創(chuàng)建C1,類似于從子類注入進來的途徑 * return C1的對象實例 */ protected abstract C1 createC1(); public void t1() /這里需要使用C1類,可是不知道究竟是用哪一個
67、 /也就不主動去創(chuàng)建C1了,怎么辦? /反正會在子類里面實現(xiàn),這里不用管怎么獲取C1,直接使用就好了 createC1().tc(); 子類的示例代碼如下: public class A2 extends A1 protected C1 createC1() /真正的選擇具體
68、實現(xiàn),并創(chuàng)建對象 return new C2(); C1接口和前面C接口是一樣的,C2這個實現(xiàn)類也是空的,只是演示一下,因此就不去展示它們的代碼了。 仔細體會上面的示例,對比它們的實現(xiàn),尤其是從思想層面上,會發(fā)現(xiàn)工廠方法模式和IoC/DI的思想是相似的,都是“主動變被動”,進行了“主從換位”,從而獲得了更靈活
69、的程序結(jié)構(gòu)。研磨設(shè)計模式之 工廠方法模式-53.3 平行的類層次結(jié)構(gòu)(1)什么是平行的類層次結(jié)構(gòu)呢? 簡單點說,假如有兩個類層次結(jié)構(gòu),其中一個類層次中的每個類在另一個類層次中都有一個對應(yīng)的類的結(jié)構(gòu),就被稱為平行的類層次結(jié)構(gòu)。舉個例子來說,硬盤對象有很多種,如分成臺式機硬盤和筆記本硬盤,在臺式機硬盤的具體實現(xiàn)上面,又有希捷、西數(shù)等不同品牌的實現(xiàn),同樣在筆記本硬盤上,也有希捷、日立、IBM等不同品牌的實現(xiàn);硬盤對象具有自己的行為,如硬盤能存儲數(shù)據(jù),也能從硬盤上獲取數(shù)據(jù),不同的硬盤對象對應(yīng)的行為對象是不一樣的,因為不同的硬盤對象,它的行為的實現(xiàn)方式是不一樣的。如果把硬盤對象和硬
70、盤對象的行為分開描述,那么就構(gòu)成了如圖10所示的結(jié)構(gòu):圖10 平行的類層次結(jié)構(gòu)示意圖硬盤對象是一個類層次,硬盤的行為這邊也是一個類層次,而且兩個類層次中的類是對應(yīng)的。臺式機西捷硬盤對象就對應(yīng)著硬盤行為里面的臺式機西捷硬盤的行為;筆記本IBM硬盤就對應(yīng)著筆記本IBM硬盤的行為,這就是一種典型的平行的類層次結(jié)構(gòu)。這種平行的類層次結(jié)構(gòu)用來干什么呢?主要用來把一個類層次中的某些行為分離出來,讓類層次中的類把原本屬于自己的職責,委托給分離出來的類去實現(xiàn),從而使得類層次本身變得更簡單,更容易擴展和復(fù)用。一般來講,分離出去的這些類的行為,會對應(yīng)著類層次結(jié)構(gòu)來組織,從而形成一個新的類層次結(jié)構(gòu),相當
71、于原來對象的行為的這么一個類層次結(jié)構(gòu),而這個層次結(jié)構(gòu)和原來的類層次結(jié)構(gòu)是存在對應(yīng)關(guān)系的,因此被稱為平行的類層次結(jié)構(gòu)。(2)工廠方法模式跟平行的類層次結(jié)構(gòu)有何關(guān)系呢?可以使用工廠方法模式來連接平行的類層次??瓷厦娴氖纠龍D10,在每個硬盤對象里面,都有一個工廠方法createHDOperate,通過這個工廠方法,客戶端就可以獲取一個跟硬盤對象相對應(yīng)的行為對象。在硬盤對象的子類里面,會覆蓋父類的工廠方法createHDOperate,以提供跟自身相對應(yīng)的行為對象,從而自然的把兩個平行的類層次連接起來使用。3.4 參數(shù)化工廠方法所謂參數(shù)化工廠方法指的就是:通過給工廠方法傳遞參數(shù),讓工廠方法
72、根據(jù)參數(shù)的不同來創(chuàng)建不同的產(chǎn)品對象,這種情況就被稱為參數(shù)化工廠方法。當然工廠方法創(chuàng)建的不同的產(chǎn)品必須是同一個Product類型的。來改造前面的示例,現(xiàn)在有一個工廠方法來創(chuàng)建ExportFileApi這個產(chǎn)品的對象,但是ExportFileApi接口的具體實現(xiàn)很多,為了方便創(chuàng)建的選擇,直接從客戶端傳入一個參數(shù),這樣在需要創(chuàng)建ExportFileApi對象的時候,就把這個參數(shù)傳遞給工廠方法,讓工廠方法來實例化具體的ExportFileApi實現(xiàn)對象。還是看看代碼示例會比較清楚。(1)先來看Product的接口,就是ExportFileApi接口,跟前面的示例沒有任何變化,為了方便大家查看,這里重復(fù)
73、一下,示例代碼如下: /* * 導(dǎo)出的文件對象的接口 */public interface ExportFileApi /* * 導(dǎo)出內(nèi)容成為文件 * param data 示意:需要保存的數(shù)據(jù) * return 是否導(dǎo)出成功 */ public boolean export(String data);
74、 (2)同樣提供保存成文本文件和保存成數(shù)據(jù)庫備份文件的實現(xiàn),跟前面的示例沒有任何變化,示例代碼如下: public class ExportTxtFile implements ExportFileApi public boolean export(String data) /簡單示意一下,這里需要操作文件 System.out.println("導(dǎo)出數(shù)據(jù)"+data+&qu
75、ot;到文本文件"); return true; public class ExportDB implements ExportFileApi public boolean export(String data) /簡單示意一下,這里需要操作數(shù)據(jù)庫和文件 System.out.println(&qu
76、ot;導(dǎo)出數(shù)據(jù)"+data+"到數(shù)據(jù)庫備份文件"); return true; (3)接下來該看看ExportOperate類了,這個類的變化大致如下:· ExportOperate類中的創(chuàng)建產(chǎn)品的工廠方法,通常需要提供默認的實現(xiàn),不抽象了,也就是變成正常方法 · ExportOperate類也不再定義成抽象類了,因為有了默認的實現(xiàn),客戶端可能需要直接使用這個對象 · 設(shè)置一個導(dǎo)出類型的參數(shù),通過export方法從客
77、戶端傳入 看看代碼吧,示例代碼如下: /* * 實現(xiàn)導(dǎo)出數(shù)據(jù)的業(yè)務(wù)功能對象 */public class ExportOperate /* * 導(dǎo)出文件 * param type 用戶選擇的導(dǎo)出類型 * param data 需要保存的數(shù)據(jù) * return 是否成功導(dǎo)出文件 */
78、; public boolean export(int type,String data) /使用工廠方法 ExportFileApi api = factoryMethod(type); return api.export(data); /*
79、160;* 工廠方法,創(chuàng)建導(dǎo)出的文件對象的接口對象 * param type 用戶選擇的導(dǎo)出類型 * return 導(dǎo)出的文件對象的接口對象 */ protected ExportFileApi factoryMethod(int type) ExportFileApi api = null;
80、0; /根據(jù)類型來選擇究竟要創(chuàng)建哪一種導(dǎo)出文件對象 if(type=1) api = new ExportTxtFile(); else if(type=2) api = new Export
81、DB(); return api; (4)此時的客戶端,非常簡單,直接使用ExportOperate類,示例代碼如下: public class Client public static void main(String args) /創(chuàng)建需要使用的Creator對象 &
82、#160; ExportOperate operate = new ExportOperate(); /調(diào)用輸出數(shù)據(jù)的功能方法,傳入選擇到處類型的參數(shù) operate.export(1,"測試數(shù)據(jù)"); 測試看看,然后修改一下客戶端的參數(shù),體會一下通過參數(shù)來選擇具體的導(dǎo)出實現(xiàn)的過程。這是一種很常見的參數(shù)化工廠方法的實現(xiàn)方式,但
83、是也還是有把參數(shù)化工廠方法實現(xiàn)成為抽象的,這點要注意,并不是說參數(shù)化工廠方法就不能實現(xiàn)成為抽象類了。只是一般情況下,參數(shù)化工廠方法,在父類都會提供默認的實現(xiàn)。(5)擴展新的實現(xiàn)使用參數(shù)化工廠方法,擴展起來會非常容易,已有的代碼都不會改變,只要新加入一個子類來提供新的工廠方法實現(xiàn),然后在客戶端使用這個新的子類即可。這種實現(xiàn)方式還有一個有意思的功能,就是子類可以選擇性覆蓋,不想覆蓋的功能還可以返回去讓父類來實現(xiàn),很有意思。先擴展一個導(dǎo)出成xml文件的實現(xiàn),試試看,示例代碼如下: /* * 導(dǎo)出成xml文件的對象 */public class ExportXml imp
84、lements ExportFileApi public boolean export(String data) /簡單示意一下 System.out.println("導(dǎo)出數(shù)據(jù)"+data+"到XML文件"); return true; 然后擴展Expo
85、rtOperate類,來加入新的實現(xiàn),示例代碼如下: /* * 擴展ExportOperate對象,加入可以導(dǎo)出XML文件 */public class ExportOperate2 extends ExportOperate /* * 覆蓋父類的工廠方法,創(chuàng)建導(dǎo)出的文件對象的接口對象 * param type 用戶選擇的導(dǎo)出類型 * return 導(dǎo)出的文件對象的接口對象 */ protected ExportFileA
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- Module7 Unit1 He spent about twenty-one hours in space(教學設(shè)計)-2023-2024學年外研版(三起)英語六年級下冊
- 華北理工大學冀唐學院《研究型建筑設(shè)計》2023-2024學年第二學期期末試卷
- 山西國際商務(wù)職業(yè)學院《計算機組成原理理論》2023-2024學年第二學期期末試卷
- 哈爾濱鐵道職業(yè)技術(shù)學院《班級活動的組織》2023-2024學年第二學期期末試卷
- 遼寧民族師范高等專科學?!稒C電系統(tǒng)設(shè)計與控制》2023-2024學年第二學期期末試卷
- 廣州東華職業(yè)學院《海洋生物技術(shù)綜合實驗》2023-2024學年第二學期期末試卷
- 南昌大學科學技術(shù)學院《新編大學生安全教育》2023-2024學年第二學期期末試卷
- 河北科技師范學院《西方財務(wù)會計雙語》2023-2024學年第二學期期末試卷
- 共青科技職業(yè)學院《學前兒童保育學》2023-2024學年第二學期期末試卷
- 陜西理工大學《數(shù)字信號處理》2023-2024學年第二學期期末試卷
- 醫(yī)師定期考核人文醫(yī)學考試題庫500題(含參考答案)
- 三年級全一冊《勞動與技術(shù)》第二單元 活動1《包書皮》課件
- 讀書分享課件:《一句頂一萬句》
- 物業(yè)消防安全管理培訓(xùn)【共54張課件】
- 空心杯電機基礎(chǔ)知識
- DL-T+5839-2021土石壩安全監(jiān)測系統(tǒng)施工技術(shù)規(guī)范
- 歷年交管12123駕照學法減分復(fù)習題庫帶答案下載
- 人教鄂教版-科學-三年級下冊-知識點
- 2024-2034年中國注射用賴氨匹林行業(yè)市場競爭格局及投資前景展望報告
- 供應(yīng)鏈可持續(xù)采購實踐
- 菌菇智慧方艙栽培及食用菌菌包中心生產(chǎn)基地項目可行性研究報告
評論
0/150
提交評論