【創(chuàng)建型模式一】簡單工廠(Simple Factory)

1 場景問題#

大家都知道伤极,在Java應(yīng)用開發(fā)中蛹找,要“面向接口編程”。那么什么是接口塑荒?接口有什么作用熄赡?接口如何使用?一起來回顧一下齿税。

1.1 接口回顧##

  1. Java中接口的概念

在Java中接口是一種特殊的抽象類彼硫,跟一般的抽象類相比,接口里面的所有方法都是抽象方法凌箕,接口里面的所有屬性都是常量拧篮。也就是說,接口里面是只有方法定義而不會有任何方法實現(xiàn)牵舱。

  1. 接口用來干什么

通常用接口來定義實現(xiàn)類的外觀串绩,也就是實現(xiàn)類的行為定義,用來約束實現(xiàn)類的行為芜壁。接口就相當(dāng)于一份契約礁凡,根據(jù)外部應(yīng)用需要的功能,約定了實現(xiàn)類應(yīng)該要實現(xiàn)的功能慧妄,但是具體的實現(xiàn)類除了實現(xiàn)接口約定的功能外顷牌,還可以根據(jù)需要實現(xiàn)一些其它的功能,這是允許的塞淹,也就是說實現(xiàn)類的功能包含但不僅限于接口約束的功能窟蓝。

通過使用接口,可以實現(xiàn)不相關(guān)類的相同行為饱普,而不需考慮這些類之間的層次關(guān)系运挫,接口就是實現(xiàn)類對外的外觀

  1. 接口的思想

根據(jù)接口的作用和用途套耕,濃縮下來谁帕,接口的思想就是“封裝隔離”

通常提到封裝是指對數(shù)據(jù)的封裝箍铲,但是這里的封裝是指“對被隔離體的行為的封裝”雇卷,或者是“對被隔離體的職責(zé)的封裝”;而隔離指的是外部調(diào)用和內(nèi)部實現(xiàn)颠猴,外部調(diào)用只能通過接口進行調(diào)用关划,而外部調(diào)用是不知道內(nèi)部具體實現(xiàn)的,也就是說外部調(diào)用和內(nèi)部實現(xiàn)是被接口隔離開的翘瓮。

  1. 接口的好處

由于外部調(diào)用和內(nèi)部實現(xiàn)被接口隔離開了贮折,那么只要接口不變,內(nèi)部實現(xiàn)的變化就不會影響到外部應(yīng)用资盅,從而使得系統(tǒng)更靈活调榄,具有更好的擴展性和可維護性踊赠,這也就是所謂“接口是系統(tǒng)可插拔性的保證”這句話的意思。

  1. 接口和抽象類的選擇

既然接口是一種特殊的抽象類每庆,那么在開發(fā)中筐带,何時選用接口,何時選用抽象類呢缤灵?對于它們的選擇伦籍,在開發(fā)中是一個很重要的問題,特別總結(jié)兩句話給大家:優(yōu)先選用接口腮出。在如下情況應(yīng)選擇抽象類:既要定義子類的行為帖鸦,又要為子類提供公共的功能

1.2 面向接口編程##

面向接口編程是Java編程中的一個重要原則胚嘲。

在Java 程序設(shè)計里面作儿,非常講究層的劃分和模塊的劃分。通常按照三層來劃分Java程序馋劈,分別是表現(xiàn)層攻锰、邏輯層、數(shù)據(jù)層妓雾,它們之間都要通過接口來通訊口注。

在每一個層里面,又有很多個小模塊君珠,一個小模塊對外也應(yīng)該是一個整體,那么一個模塊對外也應(yīng)該提供接口娇斑,其它地方需要使用到這個模塊的功能策添,都應(yīng)該通過此接口來進行調(diào)用。這也就是常說的“接口是被其隔離部分的外觀”毫缆∥ㄖ瘢基本的三層結(jié)構(gòu)如圖1所示:

基本的三層結(jié)構(gòu)

在一個層內(nèi)部的各個模塊交互也要通過接口,如圖所示:

層內(nèi)部各個模塊交互

上面頻頻提到“組件”苦丁,那么什么是組件呢浸颓?先簡單的名詞解釋一下:

所謂組件:從設(shè)計上講,組件就是能完成一定功能的封裝體旺拉。小到一個類产上,大到一個系統(tǒng),都可以稱為組件蛾狗,因為一個小系統(tǒng)放到更大的系統(tǒng)里面去晋涣,也就當(dāng)個組件而已。事實上沉桌,從設(shè)計的角度看谢鹊,系統(tǒng)算吩、子系統(tǒng)、模塊佃扼、組件等說的其實是同一回事情偎巢,都是完成一定功能的封裝體,只不過功能多少不同而已兼耀。

繼續(xù)剛才的思路压昼,大家會發(fā)現(xiàn),不管是一層還是一個模塊或者一個組件翠订,都是一個被接口隔離的整體巢音,那么下面我們就不去區(qū)分它們,統(tǒng)一認為都是接口隔離體即可尽超,如圖所示:

接口隔離體

既然在Java中需要面向接口編程官撼,那么在程序中到底如何使用接口,來做到真正的面向接口編程呢似谁?

1.3 不用模式的解決方案##

回憶一下傲绣,以前是如何使用接口的呢,假設(shè)有一個接口叫Api巩踏,然后有一個實現(xiàn)類Impl實現(xiàn)了它秃诵,在客戶端怎么用這個接口呢?

通常都是在客戶端創(chuàng)建一個Impl的實例塞琼,把它賦值給一個Api接口類型的變量菠净,然后客戶端就可以通過這個變量來操作接口的功能了,此時具體的結(jié)構(gòu)圖如圖:

基本的接口和實現(xiàn)
  1. 先定義接口Api彪杉,示例代碼如下:
/**
   * 某個接口(通用的毅往、抽象的、非具體的功能)
   */  
public interface Api {  
     /**
      * 某個具體的功能方法的定義派近,用test1來演示一下攀唯。
      * 這里的功能很簡單,把傳入的s打印輸出即可
      * @param s 任意想要打印輸出的字符串
      */  
     public void test1(String s);  
}  
  1. 既然有了接口渴丸,自然就要有實現(xiàn)侯嘀,定義實現(xiàn)Impl,示例代碼如下:
/**
   * 對接口的實現(xiàn)
   */  
public class Impl implements Api{  
    public void test1(String s) {  
        System.out.println("Now In Impl. The input s=="+s);  
    }  
}  
  1. 那么此時的客戶端怎么寫呢谱轨?按照Java的知識戒幔,接口不能直接使用,需要使用接口的實現(xiàn)類碟嘴,示例代碼如下:
/**
   * 客戶端:測試使用Api接口
   */  
public class Client {  
    public static void main(String[] args) {  
        Api api = new Impl();  
        api.test1("哈哈溪食,不要緊張,只是個測試而已娜扇!");  
    }  
}

1.4 有何問題##

上面寫得沒錯吧错沃,在Java的基礎(chǔ)知識里面就是這么學(xué)的栅组,難道這有什么問題嗎?請仔細看位于客戶端的下面這句話:

Api api = new Impl(); 

然后再想想接口的功能和思想枢析,發(fā)現(xiàn)什么了玉掸?仔細再想想?

你會發(fā)現(xiàn)在客戶端調(diào)用的時候醒叁,客戶端不但知道了接口司浪,同時還知道了具體的實現(xiàn)就是Impl。而接口的思想是“封裝隔離”把沼,而Impl這個實現(xiàn)類啊易,應(yīng)該是被接口Api封裝并同客戶端隔離開的,也就是說饮睬,客戶端根本就不應(yīng)該知道具體的實現(xiàn)類是Impl租谈。

有朋友說,那好捆愁,我就把Impl從客戶端拿掉割去,讓Api真正的對實現(xiàn)進行“封裝隔離”,然后我們還是面向接口來編程昼丑∩肽妫可是,新的問題出現(xiàn)了菩帝,當(dāng)他把“new Impl()”去掉過后咖城,發(fā)現(xiàn)他無法得到Api接口對象了,怎么辦呢呼奢?

把這個問題描述一下:

在Java編程中酒繁,出現(xiàn)只知接口而不知實現(xiàn),該怎么辦控妻?

就像現(xiàn)在的Client,它知道要使用Api接口揭绑,但是不知由誰實現(xiàn)弓候,也不知道如何實現(xiàn),從而得不到接口對象他匪,就無法使用接口菇存,該怎么辦呢?

2 解決方案#

2.1 簡單工廠來解決問題##

用來解決上述問題的一個合理的解決方案就是簡單工廠邦蜜,那么什么是簡單工廠呢依鸥?

  1. 簡單工廠定義
簡單工廠定義
  1. 應(yīng)用簡單工廠來解決的思路

分析上面的問題,雖然不能讓模塊外部知道模塊內(nèi)的具體實現(xiàn)悼沈,但是模塊內(nèi)部是可以知道實現(xiàn)類的贱迟,而且創(chuàng)建接口是需要具體實現(xiàn)類的姐扮。

那么干脆在模塊內(nèi)部新建一個類,在這個類里面來創(chuàng)建接口衣吠,然后把創(chuàng)建好的接口返回給客戶端茶敏,這樣外部應(yīng)用就只需要根據(jù)這個類來獲取相應(yīng)的接口對象,然后就可以操作接口定義的方法了缚俏。把這樣的對象稱為簡單工廠惊搏,就叫Factory吧。

這樣一來忧换,客戶端就可以通過這個Factory來獲取需要的接口對象恬惯,然后調(diào)用接口的方法來實現(xiàn)需要的功能,而且客戶端也不用再關(guān)心具體實現(xiàn)了亚茬。

2.2 簡單工廠結(jié)構(gòu)和說明##

簡單工廠的結(jié)構(gòu)如圖所示:

簡單工廠的結(jié)構(gòu)如圖

Api:定義客戶所需要的功能接口

Impl:具體實現(xiàn)Api的實現(xiàn)類酪耳,可能會有多個

Factory:工廠,選擇合適的實現(xiàn)類來創(chuàng)建Api接口對象

Client:客戶端才写,通過Factory去獲取Api接口對象葡兑,然后面向Api接口編程

2.3 簡單工廠示例代碼##

  1. 先看看Api的定義,示例代碼如下:
/**
   * 接口的定義赞草,該接口可以通過簡單工廠來創(chuàng)建
   */  
public interface Api {  
     /**
      * 示意讹堤,具體的功能方法的定義
      * @param s 示意,需要的參數(shù)
      */  
     public void operation(String s);  
}  
  1. 定義了接口厨疙,該來實現(xiàn)它了洲守,ImplA的示例代碼如下:
/**
   * 接口的具體實現(xiàn)對象A
   */  
public class ImplA implements Api{  
     public void operation(String s) {  
         //實現(xiàn)功能的代碼,示意一下  
         System.out.println("ImplA s=="+s);  
     }  
}  

/**
   * 接口的具體實現(xiàn)對象B
   */  
public class ImplB implements Api{  
     public void operation(String s) {  
         // 實現(xiàn)功能的代碼沾凄,示意一下  
         System.out.println("ImplB s=="+s);  
     }  
}  
  1. 該來看看簡單工廠的實現(xiàn)梗醇,示例代碼如下:
/**
   * 工廠類,用來創(chuàng)造Api對象
   */  
public class Factory {  
     /**
      * 具體的創(chuàng)造Api對象的方法
      * @param condition 示意撒蟀,從外部傳入的選擇條件
      * @return 創(chuàng)造好的Api對象
      */  
     public static Api createApi(int condition){  
         //應(yīng)該根據(jù)某些條件去選擇究竟創(chuàng)建哪一個具體的實現(xiàn)對象叙谨,  
         //這些條件可以從外部傳入,也可以從其它途徑獲取保屯。  
         //如果只有一個實現(xiàn)手负,可以省略條件,因為沒有選擇的必要姑尺。  

         //示意使用條件  
         Api api = null;  
         if(condition == 1) {  
             api = new ImplA();  
         } else if(condition == 2) {  
             api = new ImplB();  
         }  
         return api;  
     }  
}  
  1. 再來看看客戶端的示意竟终,示例代碼如下:
/**
   * 客戶端,使用Api接口
   */  
public class Client {  
     public static void main(String[] args) {  
         //通過簡單工廠來獲取接口對象  
         Api api = Factory.createApi(1);  
         api.operation("正在使用簡單工廠");  
     }  
}  

2.4 使用簡單工廠重寫示例##

要使用簡單工廠來重寫前面的示例切蟋,主要就是要創(chuàng)建一個簡單工廠對象统捶,讓簡單工廠來負責(zé)創(chuàng)建接口對象。然后讓客戶端通過工廠來獲取接口對象,而不再由客戶端自己去創(chuàng)建接口的對象了喘鸟。此時系統(tǒng)的結(jié)構(gòu)如圖所示:

系統(tǒng)結(jié)構(gòu)如圖
  1. 接口Api和實現(xiàn)類Impl都和前面的示例一樣匆绣,就不去贅述了。

  2. 新創(chuàng)建一個簡單工廠的對象迷守,示例代碼如下:

/**
   * 工廠類犬绒,用來創(chuàng)造Api對象
   */  
public class Factory {  
     /**
      * 具體的創(chuàng)造Api對象的方法
      * @return 創(chuàng)造好的Api對象
      */  
     public static Api createApi(){  
         //由于只有一個實現(xiàn),就不用條件判斷了  
         return new Impl();  
     }  
}  
  1. 客戶端如何使用簡單工廠提供的功能呢兑凿?這個時候凯力,客戶端就不用再自己去創(chuàng)建接口的對象了,應(yīng)該使用工廠來獲取礼华,經(jīng)過改造咐鹤,客戶端代碼如下:
/**
   * 客戶端:測試使用Api接口
   */  
public class Client {  
     public static void main(String[] args) {  
         //重要改變,沒有new Impl()了圣絮,取而代之Factory.createApi()  
         Api api = Factory.createApi();  
         api.test1("哈哈祈惶,不要緊張,只是個測試而已扮匠!");  
     }  
}  

就如同上面的示例捧请,客戶端通過簡單工廠創(chuàng)建了一個實現(xiàn)接口的對象,然后面向接口編程棒搜,從客戶端來看疹蛉,它根本就不知道具體的實現(xiàn)是什么,也不知道是如何實現(xiàn)的力麸,它只知道通過工廠獲得了一個接口對象可款,然后就能通過這個接口來獲取想要的功能

事實上克蚂,簡單工廠能幫助我們真正開始面向接口編程闺鲸,像以前的做法,其實只是用到了接口的多態(tài)那部分的功能埃叭,最重要的“封裝隔離性”并沒有體現(xiàn)出來摸恍。

3 模式講解#

3.1 典型疑問##

首先來解決一個常見的疑問:可能有朋友會認為,上面示例中的簡單工廠看起來不就是把客戶端里面的“new Impl()”移動到簡單工廠里面嗎赤屋?不還是一樣通過new一個實現(xiàn)類來得到接口嗎误墓?把“new Impl()”這句話放到客戶端和放到簡單工廠里面有什么不同嗎?

理解這個問題的重點就在于理解簡單工廠所處的位置益缎。

根據(jù)前面的學(xué)習(xí),我們知道接口是用來封裝隔離具體的實現(xiàn)的然想,目標(biāo)就是不要讓客戶端知道封裝體內(nèi)部的具體實現(xiàn)莺奔。簡單工廠的位置是位于封裝體內(nèi)的,也就是簡單工廠是跟接口和具體的實現(xiàn)在一起的,算是封裝體內(nèi)部的一個類令哟,所以簡單工廠知道具體的實現(xiàn)類是沒有關(guān)系的恼琼。整理一下簡單工廠的結(jié)構(gòu)圖,新的圖如圖所示:

簡單工廠的結(jié)構(gòu)圖

圖中虛線框屏富,就好比是一個組件的包裝邊界晴竞,表示接口、實現(xiàn)類和工廠類組合成了一個組件狠半,在這個封裝體里面噩死,只有接口和工廠是對外的,也就是讓外部知道并使用的神年,所以故意漏了一些在虛線框外已维,而具體的實現(xiàn)類是不對外的,被完全包含在虛線框內(nèi)已日。

對于客戶端而言垛耳,只是知道了接口Api和簡單工廠Factory,通過Factory就可以獲得Api了飘千,這樣就達到了讓Client在不知道具體實現(xiàn)類的情況下獲取接口Api堂鲜。所以看似簡單的把“new Impl()”這句話從客戶端里面移動到了簡單工廠里面,其實是有了質(zhì)的變化的护奈。

3.2 認識簡單工廠##

  1. 簡單工廠的功能

工廠嘛缔莲,就是用來造東西的。在Java里面逆济,通常情況下是用來造接口的酌予,但是也可以造抽象類,甚至是一個具體的類實例奖慌。一定要注意抛虫,雖然前面的示例是利用簡單工廠來創(chuàng)建的接口,但是也是可以用簡單工廠來創(chuàng)建抽象類或者是普通類的實例的简僧。

  1. 靜態(tài)工廠

使用簡單工廠的時候建椰,通常不用創(chuàng)建簡單工廠類的類實例,沒有創(chuàng)建實例的必要岛马。因此可以把簡單工廠類實現(xiàn)成一個工具類棉姐,直接使用靜態(tài)方法就可以了,也就是說簡單工廠的方法通常都是靜態(tài)的啦逆,所以也被稱為靜態(tài)工廠伞矩。如果要防止客戶端無謂的創(chuàng)造簡單工廠實例,還可以把簡單工廠的構(gòu)造方法私有化了夏志。

  1. 萬能工廠

一個簡單工廠可以包含很多用來構(gòu)造東西的方法乃坤,這些方法可以創(chuàng)造不同的接口、抽象類或者是類實例,一個簡單工廠理論上可以構(gòu)造任何東西湿诊,所以又稱之為“萬能工廠”狱杰。

雖然上面的實例中,在簡單工廠里面只有一個方法厅须,但事實上仿畸,是可以有很多這樣創(chuàng)建方法的,這點要注意朗和。

  1. 簡單工廠創(chuàng)建對象的范圍

雖然從理論上講错沽,簡單工廠什么都能造,但對于簡單工廠可創(chuàng)建對象的范圍例隆,通常不要太大甥捺,建議控制在一個獨立的組件級別或者一個模塊級別,也就是一個組件或模塊一個簡單工廠镀层。否則這個簡單工廠類會職責(zé)不明镰禾,有點大雜燴的感覺。

  1. 簡單工廠的調(diào)用順序示意圖
簡單工廠的調(diào)用順序示意圖
  1. 簡單工廠命名的建議

類名建議為“模塊名稱+Factory”唱逢,比如:用戶模塊的工廠就稱為:UserFactory

方法名稱通常為“get+接口名稱”或者是“create+接口名稱”吴侦,比如:有一個接口名稱為UserEbi,那么方法名稱通常為:getUserEbi 或者是 createUserEbi坞古。

當(dāng)然备韧,也有一些朋友習(xí)慣于把方法名稱命名為“new+接口名稱”州叠,比如:newUserEbi,我們不是很建議着绷。因為new在Java中代表特定的含義宏粤,而且通過簡單工廠的方法來獲取對象實例驶冒,并不一定每次都是要new一個新的實例。如果使用newUserEbi滞时,這會給人錯覺贼陶,好像每次都是new一個新的實例一樣饵沧。

3.3 簡單工廠中方法的寫法##

雖然說簡單工廠的方法多是用來造接口的吃粒,但是仔細分析就會發(fā)現(xiàn)潦俺,真正能實現(xiàn)功能的是具體的實現(xiàn)類,這些實現(xiàn)類是已經(jīng)做好的徐勃,并不是真的靠簡單工廠來創(chuàng)造出來的事示,簡單工廠的方法無外乎就是:實現(xiàn)了選擇一個合適的實現(xiàn)類來使用

所以簡單工廠方法的內(nèi)部主要實現(xiàn)的功能是“選擇合適的實現(xiàn)類”來創(chuàng)建實例對象僻肖。既然要實現(xiàn)選擇肖爵,那么就需要選擇的條件或者是選擇的參數(shù),選擇條件或者是參數(shù)的來源通常又有幾種:

來源于客戶端臀脏,由Client來傳入?yún)?shù)

來源于配置文件劝堪,從配置文件獲取用于判斷的值

來源于程序運行期的某個值法挨,比如從緩存中獲取某個運行期的值

下面來看個示例,看看由客戶端來傳入?yún)?shù)幅聘,如何寫簡單工廠中的方法。

  1. 在剛才的示例上再添加一個實現(xiàn)窃植,稱為Impl2帝蒿,示例代碼如下:
/**
   * 對接口的一種實現(xiàn)
   */  
public class Impl2 implements Api{  
     public void test1(String s) {  
         System.out.println("Now In Impl The input s=="+s);  
     }  
}
  1. 現(xiàn)在對Api這個接口,有了兩種實現(xiàn)巷怜,那么工廠類該怎么辦呢葛超?到底如何選擇呢?不可能兩個同時使用吧延塑,看看新的工廠類绣张,示例代碼如下:
/**
   * 工廠類,用來創(chuàng)造Api的
   */  
public class Factory {  
     /**
      * 具體的創(chuàng)造Api的方法关带,根據(jù)客戶端的參數(shù)來創(chuàng)建接口
      * @param type 客戶端傳入的選擇創(chuàng)造接口的條件
      * @return 創(chuàng)造好的Api對象
      */  
     public static Api createApi(int type){  
         //這里的type也可以不由外部傳入侥涵,而是直接讀取配置文件來獲取  
         //為了把注意力放在模式本身上,這里就不去寫讀取配置文件的代碼了  

         //根據(jù)type來進行選擇宋雏,當(dāng)然這里的1和2應(yīng)該做成常量  
         Api api = null;  
         if(type==1){  
             api = new Impl();  
         }else if(type==2){  
             api = new Impl2();  
         }  
         return api;  
     }  
}  
  1. 客戶端沒有什么變化芜飘,只是在調(diào)用Factory的createApi方法的時候需要傳入?yún)?shù),示例代碼如下:
public class Client {  
     public static void main(String[] args) {  
         //注意這里傳遞的參數(shù)磨总,修改參數(shù)就可以修改行為嗦明,試試看吧  
         Api api = Factory.createApi(2);  
         api.test1("哈哈,不要緊張蚪燕,只是個測試而已娶牌!");  
     }  
}  
  1. 要注意這種方法有一個缺點

由于是從客戶端在調(diào)用工廠的時候,傳入選擇的參數(shù)馆纳,這就說明客戶端必須知道每個參數(shù)的含義诗良,也需要理解每個參數(shù)對應(yīng)的功能處理。這就要求必須在一定程度上厕诡,向客戶暴露一定的內(nèi)部實現(xiàn)細節(jié)累榜。

3.4 可配置的簡單工廠##

現(xiàn)在已經(jīng)學(xué)會通過簡單工廠來選擇具體的實現(xiàn)類了,可是還有問題灵嫌。比如:在現(xiàn)在的實現(xiàn)中壹罚,再新增加一種實現(xiàn),會怎樣呢寿羞?

那就需要修改工廠類猖凛,才能把新的實現(xiàn)添加到現(xiàn)有系統(tǒng)中。比如現(xiàn)在新加了一個實現(xiàn)Impl3绪穆,那么需要類似下面這樣來修改工廠類:

public class Factory {  
    public static Api createApi(int type){  
        Api api = null;  
        if(type==1){  
            api = new Impl();  
        }else if(type==2){  
            api = new Impl2();  
        }else if(type==3){  
            api = new Impl3();  
        }  
        return api;  
    }  
}  

每次新增加一個實現(xiàn)類都來修改工廠類的實現(xiàn)辨泳,肯定不是一個好的實現(xiàn)方式虱岂。那么現(xiàn)在希望新增加了實現(xiàn)類過后不修改工廠類,該怎么辦呢菠红?

一個解決的方法就是使用配置文件第岖,當(dāng)有了新的實現(xiàn)類過后,只要在配置文件里面配置上新的實現(xiàn)類就好了试溯,在簡單工廠的方法里面可以使用反射蔑滓,當(dāng)然也可以使用IoC/DI(控制反轉(zhuǎn)/依賴注入,這個不在這里討論)來實現(xiàn)遇绞。

看看如何使用反射加上配置文件键袱,來實現(xiàn)添加新的實現(xiàn)類過后,無須修改代碼摹闽,就能把這個新的實現(xiàn)類加入應(yīng)用中蹄咖。

  1. 配置文件用最簡單的properties文件,實際開發(fā)中多是xml配置付鹿。定義一個名稱為“FactoryTest.properties”的配置文件澜汤,放置到Factory同一個包下面,內(nèi)容如下:
ImplClass=cn.javass.dp.simplefactory.example5.Impl

如果新添加了實現(xiàn)類倘屹,修改這里的配置就可以了银亲,就不需要修改程序了。

  1. 此時的工廠類實現(xiàn)如下:
/**
   * 工廠類纽匙,用來創(chuàng)造Api對象
   */  
public class Factory {  
      /**
       * 具體的創(chuàng)造Api的方法务蝠,根據(jù)配置文件的參數(shù)來創(chuàng)建接口
       * @return 創(chuàng)造好的Api對象
       */  
      public static Api createApi(){  
          //直接讀取配置文件來獲取需要創(chuàng)建實例的類  
          //至于如何讀取Properties,還有如何反射這里就不解釋了  
          Properties p = new Properties();  
          InputStream in = null;  
          try {  
              in = Factory.class.getResourceAsStream("FactoryTest.properties");  
              p.load(in);  
          } catch (IOException e) {  
              System.out.println("裝載工廠配置文件出錯了烛缔,具體的堆棧信息如下:");  
              e.printStackTrace();  
          }finally{  
              try {  
                  in.close();  
              } catch (IOException e) {  
                  e.printStackTrace();  
              }  
          }  
          //用反射去創(chuàng)建馏段,那些例外處理等完善的工作這里就不做了  
          Api api = null;  
          try {  
              api = (Api)Class.forName(p.getProperty("ImplClass")).newInstance();  
          } catch (InstantiationException e) {  
              e.printStackTrace();  
          } catch (IllegalAccessException e) {  
              e.printStackTrace();  
          } catch (ClassNotFoundException e) {  
              e.printStackTrace();  
          }  
          return api;  
      }  
} 
  1. 此時的客戶端就變得很簡單了,不再需要傳入?yún)?shù)践瓷,代碼示例如下:
public class Client {  
      public static void main(String[] args) {  
          Api api = Factory.createApi();  
          api.test1("哈哈院喜,不要緊張,只是個測試而已晕翠!");  
      }  
}  

3.5 簡單工廠的優(yōu)缺點##

幫助封裝:簡單工廠雖然很簡單喷舀,但是非常友好的幫助我們實現(xiàn)了組件的封裝,然后讓組件外部能真正面向接口編程淋肾。

解耦:通過簡單工廠硫麻,實現(xiàn)了客戶端和具體實現(xiàn)類的解耦。如同上面的例子樊卓,客戶端根本就不知道具體是由誰來實現(xiàn)拿愧,也不知道具體是如何實現(xiàn)的,客戶端只是通過工廠獲取它需要的接口對象碌尔。

可能增加客戶端的復(fù)雜度:如果通過客戶端的參數(shù)來選擇具體的實現(xiàn)類浇辜,那么就必須讓客戶端能理解各個參數(shù)所代表的具體功能和含義券敌,這會增加客戶端使用的難度,也部分暴露了內(nèi)部實現(xiàn)柳洋,這種情況可以選用可配置的方式來實現(xiàn)待诅。

不方便擴展子工廠:私有化簡單工廠的構(gòu)造方法,使用靜態(tài)方法來創(chuàng)建接口熊镣,也就不能通過寫簡單工廠類的子類來改變創(chuàng)建接口的方法的行為了咱士。不過,通常情況下是不需要為簡單工廠創(chuàng)建子類的轧钓。

3.6 思考簡單工廠##

  1. 簡單工廠的本質(zhì)

簡單工廠的本質(zhì)是:選擇實現(xiàn)。

注意簡單工廠的重點在選擇锐膜,實現(xiàn)是已經(jīng)做好了的毕箍。就算實現(xiàn)再簡單,也要由具體的實現(xiàn)類來實現(xiàn)道盏,而不是在簡單工廠里面來實現(xiàn)而柑。簡單工廠的目的在于為客戶端來選擇相應(yīng)的實現(xiàn),從而使得客戶端和實現(xiàn)之間解耦荷逞,這樣一來媒咳,具體實現(xiàn)發(fā)生了變化,就不用變動客戶端了种远,這個變化會被簡單工廠吸收和屏蔽掉涩澡。

實現(xiàn)簡單工廠的難點就在于“如何選擇”實現(xiàn),前面講到了幾種傳遞參數(shù)的方法坠敷,那都是靜態(tài)的參數(shù)妙同,還可以實現(xiàn)成為動態(tài)的參數(shù)。比如:在運行期間膝迎,由工廠去讀取某個內(nèi)存的值粥帚,或者是去讀取數(shù)據(jù)庫中的值,然后根據(jù)這個值來選擇具體的實現(xiàn)等等限次。

  1. 何時選用簡單工廠

建議在如下情況中芒涡,選用簡單工廠:

如果想要完全封裝隔離具體實現(xiàn),讓外部只能通過接口來操作封裝體卖漫,那么可以選用簡單工廠费尽,讓客戶端通過工廠來獲取相應(yīng)的接口,而無需關(guān)心具體實現(xiàn)懊亡;

如果想要把對外創(chuàng)建對象的職責(zé)集中管理和控制依啰,可以選用簡單工廠,一個簡單工廠可以創(chuàng)建很多的店枣、不相關(guān)的對象速警,可以把對外創(chuàng)建對象的職責(zé)集中到一個簡單工廠來叹誉,從而實現(xiàn)集中管理和控制。

3.7 相關(guān)模式##

  1. 簡單工廠和抽象工廠模式

簡單工廠是用來選擇實現(xiàn)的闷旧,可以選擇任意接口的實現(xiàn)长豁,一個簡單工廠可以有多個用于選擇并創(chuàng)建對象的方法,多個方法創(chuàng)建的對象可以有關(guān)系也可以沒有關(guān)系忙灼。

抽象工廠模式是用來選擇產(chǎn)品簇的實現(xiàn)的匠襟,也就是說一般抽象工廠里面有多個用于選擇并創(chuàng)建對象的方法,但是這些方法所創(chuàng)建的對象之間通常是有關(guān)系的该园,這些被創(chuàng)建的對象通常是構(gòu)成一個產(chǎn)品簇所需要的部件對象酸舍。

所以從某種意義上來說,簡單工廠和抽象工廠是類似的里初,如果抽象工廠退化成為只有一個實現(xiàn)啃勉,不分層次,那么就相當(dāng)于簡單工廠了双妨。

  1. 簡單工廠和工廠方法模式

    簡單工廠和工廠方法模式也是非常類似的淮阐。工廠方法的本質(zhì)也是用來選擇實現(xiàn)的,跟簡單工廠的區(qū)別在于工廠方法是把選擇具體實現(xiàn)的功能延遲到子類去實現(xiàn)刁品。如果把工廠方法中選擇的實現(xiàn)放到父類直接實現(xiàn)泣特,那就等同于簡單工廠。

  2. 簡單工廠和能創(chuàng)建對象實例的模式

簡單工廠的本質(zhì)是選擇實現(xiàn)挑随,所以它可以跟其它任何能夠具體的創(chuàng)建對象實例的模式配合使用状您,比如:單例模式竞阐、原型模式、生成器模式等等骆莹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末担猛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子傅联,更是在濱河造成了極大的恐慌,老刑警劉巖蒸走,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異该溯,居然都是意外死亡岛抄,警方通過查閱死者的電腦和手機狈茉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門夫椭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人氯庆,你說我怎么就攤上這事蹭秋。” “怎么了堤撵?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵仁讨,是天一觀的道長。 經(jīng)常有香客問我实昨,道長陪竿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任屠橄,我火速辦了婚禮,結(jié)果婚禮上闰挡,老公的妹妹穿的比我還像新娘锐墙。我一直安慰自己,他們只是感情好长酗,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布溪北。 她就那樣靜靜地躺著,像睡著了一般夺脾。 火紅的嫁衣襯著肌膚如雪之拨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天咧叭,我揣著相機與錄音蚀乔,去河邊找鬼。 笑死菲茬,一個胖子當(dāng)著我的面吹牛吉挣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婉弹,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼睬魂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了镀赌?” 一聲冷哼從身側(cè)響起氯哮,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤喉钢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后穴肘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體评抚,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡慨代,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年侍匙,在試婚紗的時候發(fā)現(xiàn)自己被綠了想暗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片说莫。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡储狭,死狀恐怖辽狈,靈堂內(nèi)的尸體忽然破棺而出刮萌,到底是詐尸還是另有隱情娘扩,我是刑警寧澤畜侦,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布旋膳,位于F島的核電站,受9級特大地震影響擅羞,放射性物質(zhì)發(fā)生泄漏减俏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一奏夫、第九天 我趴在偏房一處隱蔽的房頂上張望历筝。 院中可真熱鬧梳猪,春花似錦、人聲如沸呛哟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜘渣。三九已至肺然,卻和暖如春际起,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背校翔。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工防症, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蔫敲,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓貌虾,卻偏偏與公主長得像尽狠,于是被迫代替她去往敵國和親晚唇。 傳聞我的和親對象是個殘疾皇子哩陕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容