顧名思義,就是一種很簡單的工廠模式。在業(yè)務比較簡單的情況下使用。
簡單工廠模式類圖如下:
上述類圖中有如下三種角色:
工廠類角色(Creator):這是本模式的核心,含有一定的業(yè)務邏輯和判斷邏輯,在Java中往往由一個具體類實現(xiàn)。工廠類依賴于具體產(chǎn)品。
抽象產(chǎn)品角色(Product):一般是具體產(chǎn)品繼承的父類或者實現(xiàn)的接口,在Java中由接口或者抽象類實現(xiàn)。
具體產(chǎn)品角色(ConcreteProduct):工廠類所創(chuàng)建的對象就是此角色的實例,在Java中由一個具體類來實現(xiàn)。具體產(chǎn)品繼承于抽象產(chǎn)品。
功能:工廠是用來造東西的。在Java里面,通常情況下用它來造接口,但是也可以造抽象類,甚至是一個具體類實例。可以利用簡單工廠來創(chuàng)建接口、抽象類或者普通類的實例。
靜態(tài)工廠:當使用簡單工廠的時候,通常不用創(chuàng)建簡單工廠類的類實例,因為沒有必要,因此可以把簡單工廠類實現(xiàn)成一個工具類,直接使用靜態(tài)方法就可以。也就是說簡單工廠的方法通常都是靜態(tài)的,所以也被腸胃靜態(tài)工廠,如果要防止客戶端誤創(chuàng)建簡單工廠類實例,可以把構造方法私有化。
萬能工廠:一個簡單工廠可以包含很多用來構造東西的方法,這些方法可以創(chuàng)造不同的接口、抽象類或者類實例,一個簡單工廠,理論上可以構造任何東西,所以又被稱為萬能工廠。在工廠里可以有一個或者多個創(chuàng)造方法。
范圍:理論上簡單工廠什么都能創(chuàng)造。但是對于簡單工程可創(chuàng)建對象的范圍來說,通常建議不要太大,建議將其控制在一個獨立的組件級別或者一個模塊級別,否則這個簡單工廠會職責不明,有點大雜燴的感覺。
命名建議:類名建議寫成“模塊名稱+Factory”,比如用戶模塊的工廠為 UserFactory。方法名通常為 get+接口名稱,或者是 create+接口名稱,比如有個接口名為User,那么方法名通常為 getUser或者createUser。還有一些人習慣為? new+接口名稱,比如newUser,通常不建議這種方式,因為new在Java中代表特定的含義,而且通過簡單工廠來獲取對象的實例,并不是每次都new,因為使用new可能會造成誤會錯覺。
簡單工廠的思想源于Java中的接口,Java中接口是一種特殊的抽象類,跟一般的抽象類相比,接口中所有的方法都是抽象的,接口中所有的屬性通常都是常量,也就是說接口里只有定義沒有實現(xiàn)。
(1)接口用來干什么
通常用接口來定義實現(xiàn)類的外觀,也就是定義實現(xiàn)類的行為,也可以說用來約束實現(xiàn)類必須實現(xiàn)的行為。但是通常實現(xiàn)類除了實現(xiàn)接口定義的方法外,還可以根據(jù)需要實現(xiàn)一些其它的功能,也就是說實現(xiàn)類的功能包含但是不僅限于接口約束的功能。通過使用接口可以讓不相關的類實現(xiàn)相同的行為,而不需要考慮這些類之間的層次,接口就是實現(xiàn)類對外的外觀。
(2)接口的思想
根據(jù)接口的作用和用途,簡單來說,接口的思想就是封裝隔離。一般的封裝是封裝數(shù)據(jù),接口的封裝是封裝行為,也可以說是職責。隔離指的是外部調(diào)用和內(nèi)部實現(xiàn)的隔離,外部調(diào)用只能通過接口調(diào)用,而不知道內(nèi)部具體實現(xiàn)。
(3)使用接口的好處
由于外部調(diào)用和內(nèi)部實現(xiàn)被隔離了,那么只要接口本身不變,內(nèi)部實現(xiàn)的變化就不會影響到外部調(diào)用的代碼,從而使得系統(tǒng)更靈活,更具有擴展性和可維護行,也就是常說的接口是系統(tǒng)可插拔性的保證。
(4)接口和抽象類的選擇
接口是一種特殊的抽象類,在開發(fā)中如何選擇,一般遵循兩點:
》優(yōu)先選用接口
》如果既要定義子類的行為,又要為子類提供功公共的功能,要選擇抽象類
除了客戶端調(diào)用代碼,簡單工廠模式大致可以分為三種代碼,一是定義客戶端所需要功能的接口,二是工廠類,用來選擇合適的實現(xiàn)類創(chuàng)建接口對象,三是各種實現(xiàn)類。
接下來舉個簡單的例子,可以把運動員看成一個總的接口,分別有籃球運動員和足球運動員兩個實現(xiàn)類,要想成為運動員必須在聯(lián)盟注冊,球員聯(lián)盟可以看成一個工廠。
首先看一下運動員的接口代碼:
可以看到接口中只定義了一個注冊方法,傳入了一個字符串,注冊方法就是所有運動員都必須有的行為。
接下來看足球運動員和籃球運動員兩個具體實現(xiàn)類:
可以看到兩個實現(xiàn)類也非常簡單,在實現(xiàn)方法中只是打印了身份和傳入的字符串。
接下來看一下工廠類的具體內(nèi)容:
工廠類定義了一個create接口對象的方法,根據(jù)傳入的條件判斷使用哪個實現(xiàn)類創(chuàng)建實例,內(nèi)容也很簡單。
上面四個類構成了工廠模式的全部要素。使用的時候分兩步,首先通過工廠類根據(jù)傳入的條件返回一個運動員實例,然后運行注冊方法就可以看到成功創(chuàng)建了實例,調(diào)用代碼如下:
運行結果如下:
這樣可以讓工廠對創(chuàng)建實例而不再由具體的類去創(chuàng)建。從調(diào)用代碼看,我們也不關心具體的實現(xiàn)是什么,也不知道如何具體實現(xiàn)。事實上簡單工廠幫助我們真正開始面向接口編程,以前的做法只是接口多態(tài)的功能,最重要的封裝隔離性并沒有體現(xiàn)出來。讓調(diào)用者不知道內(nèi)部具體實現(xiàn)。
上面的代碼雖然僅僅是把new操作放到了工廠方法里面,但是卻真正實現(xiàn)了封裝和隔離,讓調(diào)用代碼無法了解具體的實現(xiàn)內(nèi)容,而工廠和實現(xiàn)封裝在一起是沒有關系的。雖然僅僅是new操作的位置變了,卻發(fā)生了質(zhì)的變化。
上面的簡單工廠也有不足,如果接口和實現(xiàn)類不變,新增了一個實現(xiàn)類,那么工廠類的方法必須做出修改,在一定程度上增加了維護成本。要想做到增加了實現(xiàn)類而不修改任何代碼,一個簡單的思路是利用配置文件和反射來實現(xiàn)動態(tài)新增創(chuàng)建,新建一個配置類simple.properties,內(nèi)容如下:
可以看到定義了傳入條件和具體實現(xiàn)類的鍵值對。
接下來我們要對工廠類的創(chuàng)建方法做出修改:
可以看到方法根據(jù)傳入的條件動態(tài)獲取配置文件中的類路徑,然后利用反射創(chuàng)建對應實例,這樣新增實現(xiàn)類只需要增加一行配置即可,不需要修改任何代碼。
首先不新增實現(xiàn)類,運行前面的客戶端代碼,
可以看到結果正確。然后新增一個實現(xiàn)類,
然后新增一行配置:
修改客戶端代碼并運行:
可以看到實現(xiàn)類增加實現(xiàn)類而無需修改代碼。
簡單工廠模式的總結:
優(yōu)點
(1)實現(xiàn)了組件封裝,讓組件外部真正面向接口編程
(2)實現(xiàn)了客戶端和具體實現(xiàn)類的真正解耦
缺點
(1)根據(jù)參數(shù)決定創(chuàng)建的具體實例,勢必讓客戶端理解參數(shù)所代表的含義,也暴露了部分內(nèi)部實現(xiàn)。
(2)不方便通過工廠的子類改變創(chuàng)建接口的方法行為,不過一般也不需要。
簡單工廠的本質(zhì)就是,選擇實現(xiàn),屏蔽具體類。
使用建議:靈活傳遞選擇條件,可以使用配置文件,也可以讀取運行期間內(nèi)存的某個值。