簡單工廠模式和工廠方法模式及實例

 前言
       大家都知道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.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章