設計模式- 簡單工廠模式、工廠方法模式及其比較

前言
工大有許多同窗是作java的,你們都知道java最大的優勢是它的徹底OO化和它在多年的發展過程當中吸取和總結了許多先進的框架與模式,其中工廠模式就是最經常使用的模式之一。下面我想將我在學習和實踐過程當中對工廠模式的認識與瞭解介紹給你們。因爲筆者能力限制,在實踐中也沒參與過什麼大的項目,筆者參與過的項目用到的工廠模式主要是簡單工廠模式(Simple Factory)和工廠方法模式(Factory Method),因此筆者在本文主要介紹的是這兩種模式。



準備知識
在OO設計領域,咱們知道前人總結了很多的經驗,許多的經驗在現代軟件工程過程當中已經被認爲是原則來遵照。下面筆者摘抄幾項下文涉及到的OO原則的定義。

OCP(開閉原則,Open-Closed Principle):一個軟件的實體應當對擴展開放,對修改關閉。個人理解是,對於一個已有的軟件,若是須要擴展,應當在不需修改已有代碼的基礎上進行。

DIP(依賴倒轉原則,Dependence Inversion Principle):要針對接口編程,不要針對實現編程。個人理解是,對於不一樣層次的編程,高層次暴露給低層次的應當只是接口,而不是它的具體類。

LoD(迪米特法則,Law of Demeter):只與你直接的朋友通訊,而避免和陌生人通訊。衆所周知類(或模塊)之間的通訊越少,耦合度就越低,從而更有利於咱們對軟件的宏觀管理。老子論「聖人之治」有相同的思想,《老子》雲:「是以聖人之治,虛其心,實其腹,弱其志,常使民無知無慾。」,又云:「小國寡民,鄰國相望,雞犬之聲相聞,民至老死,不相往來。」。佩服咱們的老祖宗,N千年前就想到了西方N千年後纔想到的東西,同時也佩服《java與模式》的做者閻宏,能夠用中國傳統哲學思想這麼生動的說明這一軟件設計原則。



簡單工廠模式及實例
簡單工廠模式又叫靜態工廠模式,顧名思義,它是用來實例化目標類的靜態類。下面我主要經過一個簡單的實例說明簡單工廠及其優勢。

好比有個國家的運動員協會,他們是負責登記與註冊職業運動員的(就好像咱們國家的體育總局,呵呵,不管足球籃球仍是乒乓球的運動員都必須在這裏註冊才能拿到咱們國家職業運動員牌照)。一家體育俱樂部(好比籃球的廣東宏遠,足球的深圳健力寶)想得到球員爲本身俱樂部效力,就必須經過這個運動員協會。

根據DIP咱們能夠設計一個「運動員」接口,「足球運動員」和「籃球運動員」(還有其餘運動員)都實現「運動員」這個接口。而「運動員協會」就是一個簡單工廠類,它負責實例化「運動員」。咱們這裏的「俱樂部」就是一個客戶端(Client),不一樣的「俱樂部」就是不一樣的客戶端。具體以下圖表示:




對於不一樣的俱樂部對象(不管是八一仍是深圳健力寶),他們都是面向「運動員」接口編程,而不用管是「足球運動員」仍是「籃球運動員」,也就是說實現了「運動員」接口的具體類「足球運動員」無需暴露給客戶端。這也知足了DIP。但具體的俱樂部(好比足球的深圳健力寶)如何確保本身獲取的是本身想要的運動員(健力寶俱樂部須要的固然是足球運動員)呢?這就須要「運動員協會」這一工廠類了。俱樂部經過調用「運動員協會」的具體方法,返回不一樣的實例。這同時也知足了LoD,也就是「深圳健力寶足球俱樂部」對象不直接與「足球運動員:李毅」對象通訊,而是經過他們共同的「朋友」——「國家體育總局」通訊。

下面給出各個類的程序,會有助於讀者更好的瞭解筆者以前的介紹。

  Code: [Copy to clipboard]  
運動員.java
public interface 運動員 {        
        public void 跑();
        public void 跳();
}

足球運動員.java
public class 足球運動員 implements 運動員 {

        public void 跑(){
                //跑啊跑
        }
        
        public void 跳(){
                //跳啊跳
        }
}

籃球運動員.java
public class 籃球運動員 implements 運動員 {

        public void 跑(){
                //do nothing
        }
        
        public void 跳(){
                //do nothing
        }
}

體育協會.java
public class 體育協會 {
        
        public static 運動員 註冊足球運動員(){
                return new 足球運動員();
        }
        
        public static 運動員 註冊籃球運動員(){
                return new 籃球運動員();
        }

}

俱樂部.java
public class 俱樂部 {
        private 運動員 守門員;
        private 運動員 後衛;
        private 運動員 前鋒;

        public void test() {
                this.前鋒 = 體育協會.註冊足球運動員();
                this.後衛 = 體育協會.註冊足球運動員();
                this.守門員 = 體育協會.註冊足球運動員();
                
                守門員.跑();
                後衛.跳();
        }
}

以上就是簡單工廠模式的一個簡單實例,讀者應該想象不用接口不用工廠而把具體類暴露給客戶端的那種混亂情形吧(就好像沒了體育總局,各個俱樂部在市場上本身胡亂的尋找仔細須要的運動員),簡單工廠就解決了這種混亂。

咱們用OCP看看簡單工廠,會發現若是要對系統進行擴展的話治須要增長實現產品接口的產品類(上例表現爲「足球運動員」,「籃球運動員」類,好比要增長個「乒乓球運動員」類),而無需對原有的產品類進行修改。這咋一看好像知足OCP,可是實際上仍是須要修改代碼的——對,就是修改工廠類。上例中若是增長「乒乓球運動員」產品類,就必須相應的修改「體育協會」工廠類,增長個「註冊乒乓球運動員」方法。因此能夠看出,簡單工廠模式是不知足OCP的。



工廠方法模式及其實例
談了簡單工廠模式,下面繼續談談工廠方法模式。前一節的最末點明瞭簡單工廠模式最大的缺點——不徹底知足OCP。爲了解決這一缺點,設計師們提出了工廠方法模式。工廠方法模式和簡單工廠模式最大的不一樣在於,簡單工廠模式只有一個(對於一個項目或者一個獨立模塊而言)工廠類,而工廠方法模式有一組實現了相同接口的工廠類。下面咱們經過修改上一節的實例來介紹工廠方法模式。

咱們在不改變產品類(「足球運動員」類和「籃球運動員」類)的狀況下,修改下工廠類的結構,以下圖所示:




相關代碼以下:

  Code: [Copy to clipboard]  
運動員.java
public interface 運動員 {        
        public void 跑();
        public void 跳();
}

足球運動員.java
public class 足球運動員 implements 運動員 {

        public void 跑(){
                //跑啊跑
        }
        
        public void 跳(){
                //跳啊跳
        }
}

籃球運動員.java
public class 籃球運動員 implements 運動員 {

        public void 跑(){
                //do nothing
        }
        
        public void 跳(){
                //do nothing
        }
}

體育協會.java
public interface 體育協會 {
        public 運動員 註冊();
}

足球協會.java
public class 足球協會 implements 體育協會 {
        public 運動員 註冊(){
                return new 足球運動員();
        }
}

籃球協會.java
public class 籃球協會 implements 體育協會 {
        public 運動員 註冊(){
                return new 籃球運動員();
        }
}

俱樂部.java
public class 俱樂部 {
        private 運動員 守門員;
        private 運動員 後衛;
        private 運動員 前鋒;

        public void test() {
                體育協會 中國足協 = new 足球協會();
                
                this.前鋒 = 中國足協.註冊();
                this.後衛 = 中國足協.註冊();

                守門員.跑();
                後衛.跳();
        }
}


很明顯能夠看到,「體育協會」工廠類變成了「體育協會」接口,而實現此接口的分別是「足球協會」「籃球協會」等等具體的工廠類。

這樣作有什麼好處呢?很明顯,這樣作就徹底OCP了。若是須要再加入(或擴展)產品類(好比加多個「乒乓球運動員」)的話就再也不須要修改工廠類了,而只需相應的再添加一個實現了工廠接口(「體育協會」接口)的具體工廠類。



簡單工廠模式與工廠方法模式大PK
從以上對兩種模式的介紹能夠了解到,工廠方法模式是爲了克服簡單工廠模式的缺點(主要是爲了知足OCP)而設計出來的。可是,工廠方法模式就必定比簡單工廠模式好呢?筆者的答案是不必定。下面筆者將詳細比較兩種模式。

1. 結構複雜度
從這個角度比較,顯然簡單工廠模式要佔優。簡單工廠模式只需一個工廠類,而工廠方法模式的工廠類隨着產品類個數增長而增長,這無疑會使類的個數愈來愈多,從而增長告終構的複雜程度。

2.代碼複雜度
代碼複雜度和結構複雜度是一對矛盾,既然簡單工廠模式在結構方面相對簡潔,那麼它在代碼方面確定是比工廠方法模式複雜的了。簡單工廠模式的工廠類隨着產品類的增長鬚要增長不少方法(或代碼),而工廠方法模式每一個具體工廠類只完成單一任務,代碼簡潔。

3.客戶端編程難度
工廠方法模式雖然在工廠類結構中引入了接口從而知足了OCP,可是在客戶端編碼中須要對工廠類進行實例化。而簡單工廠模式的工廠類是個靜態類,在客戶端無需實例化,這無疑是個吸引人的優勢。

4.管理上的難度
這是個關鍵的問題。
咱們先談擴展。衆所周知,工廠方法模式徹底知足OCP,即它有很是良好的擴展性。那是否就說明了簡單工廠模式就沒有擴展性呢?答案是否認的。簡單工廠模式一樣具有良好的擴展性——擴展的時候僅須要修改少許的代碼(修改工廠類的代碼)就能夠知足擴展性的要求了。儘管這沒有徹底知足OCP,但筆者認爲不須要太拘泥於設計理論,要知道,sun提供的java官方工具包中也有想到多沒有知足OCP的例子啊(java.util.Calendar這個抽象類就不知足OCP,具體緣由你們能夠分析下)。
而後咱們從維護性的角度分析下。假如某個具體產品類須要進行必定的修改,極可能須要修改對應的工廠類。當同時須要修改多個產品類的時候,對工廠類的修改會變得至關麻煩(對號入座已是個問題了)。反而簡單工廠沒有這些麻煩,當多個產品類須要修改是,簡單工廠模式仍然僅僅須要修改惟一的工廠類(不管怎樣都能改到知足要求吧?大不了把這個類重寫)。

由以上的分析,筆者認爲簡單工廠模式更好用更方便些。固然這只是筆者的我的見解而已,畢竟公認的,工廠方法模式比簡單工廠模式更「先進」。但有時過於先進的東西未必適合本身,這個見仁見智吧。


寫在最後
本文僅討論了兩個常見的工廠模式,筆者很主觀的分析了各自的優缺點,這一定會引發許多異議。若是對筆者觀點有意見的,很歡迎跟貼批評指出。

其實究竟哪一種模式更好,可能你會回答「都很差」,呵呵,別忘了,咱們還有更「先進」的「抽象工廠模式」。。。

最後交代下版權信息。本文許多定義性的內容引用自《java與模式》(電子工業出版社,閻宏),這是本好書,有時間有機會但願你們能夠好好看看,我在此書中是獲益良多。另外筆者在寫本文過程當中還參考了《UML基礎、案例與應用》(人民郵電出版社,Joseph Schmuller著,李虎、趙龍剛譯),除此外其他內容均爲原創,轉貼請註明「工大後院版權全部」。

轉貼請註明:轉載自 工大後院http://www.gdutbbs.comphp

相關文章
相關標籤/搜索