### 一、模式定義
工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多態工廠(Polymorphic Factory)模式,它屬于類創建型模式。在工廠方法模式中,工廠父類負責定義創建產品對象的公共接口,而工廠子類則負責生成具體的產品對象,這樣做的目的是將產品類的實例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該實例化哪一個具體產品類。
### 二、模式結構

我們可以看到工廠方法模式一共分為四個部分:
- 抽象工廠(AbstractCreator)角色:擔任這個角色的是工廠方法模式的核心,它是與應用程序無關的。任何在模式中創建對象的工廠類必須繼承或者實現這個接口,在實際的系統中,這個角色常常有Java抽象類來實現。
- 具體工廠(ConcreteCreator)角色:擔任這個角色的是實現了抽象工廠接口的具體Java類。具體工廠角色含有與應用密切相關的邏輯,并且受到應用程序的調用以創建產品對象。
- 抽象產品(AbstractProduct)角色:工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的接口。在實際應用中這個角色常常由Java的抽象類來實現。
- 具體產品(ConcreteProduct)角色:這個角色實現了抽象產品角色所聲明的接口,工廠方法所創建的每一個對象都是某個具體產品角色的實例。
### 三、模式動機
1、解決簡單工廠模式中存在的“上帝類”的問題,將具體的生產任務放到子類中去。
2、解決簡單工廠模式工廠角度不符合“開-閉”原則的問題。
3、解決簡單工廠模式不能使用繼承的問題。

### 四、實例分析
我們繼續來分析上一篇博客中土豪開車的問題,我們使用了簡單工廠模式后,程序好像已經很合理了,并且從實際生活來看也是比較符合邏輯的。但是可能會存在以下的問題。
1、如果土豪家里又買了很多車,那么Driver(工廠類)將會非常大,每次買一輛新的車都需要在這個工廠類中添加一個if-else語句,如果有的車壞了不能開了,還需要將對應的if-else語句刪除,所以很麻煩,也不符合“開-閉”原則。
2、所有的產品創建都集中在Driver類中,一旦寫錯一個標點,所有的產品便都不能生產了。(可以想象成,只有一位司機師傅,這位司機師傅會開所有的車,一旦這一位司機師傅生病了,那么所有的汽車就都不能開了)。
所以我們可以使用工廠方法模式以下的改進。
首先,定義一個抽象的工廠類(Driver),這個抽象工廠類中有定義好的抽象方法getCar();用來告訴具體的工廠如何來獲得汽車。
~~~
package com.myfactory.factory;
public abstract class Driver {
public abstract Car getCar();
}
~~~
然后,每一種車都配一個專門的司機,這個司機只負責或者一種車。
~~~
package com.myfactory.factory;
public class BenzDriver extends Driver{
public Car getCar() {
return new Benz();
}
}
~~~
這里只寫了Benz車的類,其他的車的類和這個類基本相同,不再貼出代碼,最后會附上源碼。
在客戶端中,創建對應的Driver類,讓特定的Driver來生產特定的汽車。
~~~
package com.myfactory.factory;
public class Main {
public static void main(String[] args) throws Exception {
//奔馳車司機
Driver driver = new BenzDriver();
//今天想做奧迪車
Car car = driver.getCar();
//開車
car.drive();
}
}
~~~
通過改進,我們看到不再存在“上帝類”,有具體的工廠類來負責生產特定的產品,以后想要添加新的車的時候不必修改現有的工廠和產品的代碼,只需要添加新的產品類和具體工廠類就可以了,不管是產品角度還是工廠角度都更加符合“開-閉”原則。
### 五、模式優點
- 在工廠方法模式中,工廠方法用來創建客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被實例化這一細節,用戶只需要關心所需產品對應的工廠,無須關心創建細節,甚至無須知道具體產品類的類名。
- 基于工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定創建何種產品對象,而如何創建這個對象的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多態工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
- 使用工廠方法模式的另一個優點是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就可以了。這樣,系統的可擴展性也就變得非常好,完全符合“開閉原則”。
### 六、模式缺點
- 在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的復雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷。
- 由于考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。
### 七、適用場景
在以下情況下可以使用工廠方法模式:
1、一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
2、一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對于抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
3、將創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中。
### 八、模式在JDK中的應用
1、Object中的toString方法:Object僅僅定義了返回String,由Object的子類來決定如何生成這個字符串。
2、抽象類java.util.Calendar的getInstance方法將根據不同的情況返回不同的Calendar子類的對象。
源碼下載:[http://download.csdn.net/detail/xingjiarong/9294193](http://download.csdn.net/detail/xingjiarong/9294193)
- 前言
- 設計原則(一)"開-閉"原則(OCP)
- 設計原則(二)里氏替換原則(LSP)
- 設計原則(三)組合復用原則
- 設計原則(四)依賴倒置原則(DIP)
- 設計模式(一)簡單工廠模式
- 設計模式(二)工廠方法模式
- 設計模式(三)抽象工廠模式
- 設計模式(四)單例模式
- 設計模式(五)創建者模式(Builder)
- 設計模式(六)原型模式
- 設計模式(七)門面模式(Facade Pattern 外觀模式)
- 設計模式(八)橋梁模式(Bridge)
- 設計模式(九)裝飾模式(Decorator)
- 設計模式(十)適配器模式
- 設計模式(十一)策略模式
- 設計模式(十二)責任鏈模式
- 設計模式之UML(一)類圖以及類間關系(泛化 、實現、依賴、關聯、聚合、組合)
- 設計模式之橋梁模式和策略模式的區別