在設計原則中有這樣一句話“我們應該針對接口編程,而不是正對實現編程”。但是我們還是在一直使用new關鍵字來創建一個對象,這不就是在針對實現編程么?
針對接口編程,可以隔離掉以后系統可能發生的一大堆改變。如果代碼是針對接口而寫,那么可以通過多態,它可以與任何新類實現該接口。但是,當代碼使用一大堆的具體類時,等于是自找麻煩,因為一旦加入新的具體類,就必須要改變代碼。在這里我們希望能夠調用一個簡單的方法,我傳遞一個參數過去,就可以返回給我一個相應的具體對象,這個時候我們就可以使用簡單工廠模式。
# 引入
1)還沒有工廠時代:假如還沒有工業革命,如果一個客戶要一款寶馬車,一般的做法是客戶去創建一款寶馬車,然后拿來用。
2)簡單工廠模式:后來出現工業革命。用戶不用去創建寶馬車。因為客戶有一個工廠來幫他創建寶馬.想要什么車,這個工廠就可以建。比如想要320i系列車。工廠就創建這個系列的車。即工廠可以創建產品。
3)工廠方法模式時代:為了滿足客戶,寶馬車系列越來越多,如320i,523i,30li等系列一個工廠無法創建所有的寶馬系列。于是由單獨分出來多個具體的工廠。每個具體工廠創建一種系列。即具體工廠類只能創建一個具體產品。但是寶馬工廠還是個抽象。你需要指定某個具體的工廠才能生產車出來。
4)抽象工廠模式時代:隨著客戶的要求越來越高,寶馬車必須配置空調。于是這個工廠開始生產寶馬車和需要的空調。
最終是客戶只要對寶馬的銷售員說:我要523i空調車,銷售員就直接給他523i空調車了。而不用自己去創建523i空調車寶馬車.
這就是工廠模式。
# 分類
工廠模式主要是為創建對象提供過渡接口,以便將創建對象的具體過程屏蔽隔離起來,達到提高靈活性的目的。
工廠模式可以分為三類:
1)簡單工廠模式(Simple Factory)
2)工廠方法模式(Factory Method)
3)抽象工廠模式(Abstract Factory)
這三種模式從上到下逐步抽象,并且更具一般性。
GOF在《設計模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。
> 將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。
## 簡單工廠模式
### 基本定義
簡單工廠模式又稱之為靜態工廠方法,屬于創建型模式。在簡單工廠模式中,可以根據傳遞的參數不同,返回不同類的實例。簡單工廠模式定義了一個類,這個類專門用于創建其他類的實例,這些被創建的類都有一個共同的父類。
### 模式結構

### 組成
* Factory:工廠角色。專門用于創建實例類的工廠,提供一個方法,該方法根據傳遞的參數不同返回不同類的具體實例。
* Product:抽象產品角色。為所有產品的父類。
* ConcreteProduct:具體的產品角色。
簡單工廠模式將對象的創建和對象本身業務處理分離了,可以降低系統的耦合度,使得兩者修改起來都相對容易些。當以后實現改變時,只需要修改工廠類即可。
### 代碼實現
模式場景:在一個披薩店中,要根據不同客戶的口味,生產不同的披薩,如素食披薩、希臘披薩等披薩。
該例的UML結構圖如下:

Pizza制造工廠:SimplyPizzaFactory.java
~~~
/**
* 專門用于創建披薩的工廠類
*/
public class SimplePizzaFactory {
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}
else if(type.equals("clam")){
pizza = new ClamPizza();
}
else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
else if(type.equals("veggie")){
pizza = new VeggiePizze();
}
return pizza;
}
}
~~~
抽象披薩:Pizza.java
~~~
/**
* 抽象pizza類
*/
public abstract class Pizza {
public abstract void prepare();
public abstract void bake();
public abstract void cut();
public abstract void box();
}
~~~
具體披薩:CheesePizza.java
~~~
public class CheesePizza extends Pizza{
@Override
public void bake() {
System.out.println("bake CheesePizza ...");
}
@Override
public void box() {
System.out.println("box CheesePizza ...");
}
@Override
public void cut() {
System.out.println("cut CheesePizza ...");
}
@Override
public void prepare() {
System.out.println("prepare CheesePizza ...");
}
}
~~~
PizzaStore.java
~~~
public class PizzaStore {
SimplePizzaFactory factory; //SimplePizzaFactory的引用
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type); //使用工廠對象的創建方法,而不是直接new。這里不再使用具體實例化
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
~~~
### 優點
* 1、簡單工廠模式實現了對責任的分割,提供了專門的工廠類用于創建對象。
* 2、客戶端無須知道所創建的具體產品類的類名,只需要知道具體產品類所對應的參數即可,對于一些復雜的類名,通過簡單工廠模式可以減少使用者的記憶量。
* 3、通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。
### 缺點
* 1、由于工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
* 2、使用簡單工廠模式將會增加系統中類的個數,在一定程序上增加了系統的復雜度和理解難度。
* 3、系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,在產品類型較多時,有可能造成工廠邏輯過于復雜,不利于系統的擴展和維護。
* 4、簡單工廠模式由于使用了靜態工廠方法,造成工廠角色無法形成基于繼承的等級結構。
### 使用場景
* 1、 工廠類負責創建的對象比較少。
* 2、 客戶端只知道傳入工廠類的參數,對于如何創建對象不關心。
### 總結
* 1、 簡單工廠模式的要點就在于當你需要什么,只需要傳入一個正確的參數,就可以獲取你所需要的對象,而無須知道其創建細節。
* 2、 簡單工廠模式最大的優點在于實現對象的創建和對象的使用分離,但是如果產品過多時,會導致工廠代碼非常復雜。
## 工廠方法模式
### 引入
在披薩實例中,如果我想根據地域的不同生產出不同口味的披薩,如紐約口味披薩,芝加哥口味披薩。如果利用簡單工廠模式,我們需要兩個不同的工廠,NYPizzaFactory、ChicagoPizzaFactory。在該地域中有很多的披薩店,他們并不想依照總店的制作流程來生成披薩,而是希望采用他們自己的制作流程。這個時候如果還使用簡單工廠模式,因為簡單工廠模式是將披薩的制作流程完全承包了。那么怎么辦?
我們可以這樣解決:將披薩的制作方法交給各個披薩店完成,但是他們只能提供制作完成的披薩,披薩的訂單處理仍然要交給披薩工廠去做。也就是說,我們將createPizza()方法放回到PizzaStore中,其他的部分還是保持不變。
### 基本定義
工廠方法模式定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法模式讓實例化推遲到子類。
### 模式結構
工廠方法模式的UML結構圖:

Product:抽象產品。所有的產品必須實現這個共同的接口,這樣一來,使用這些產品的類既可以引用這個接口。而不是具體類。
ConcreteProduct:具體產品。
Creator:抽象工廠。它實現了所有操縱產品的方法,但不實現工廠方法。Creator所有的子類都必須要實現factoryMethod()方法。
ConcreteCreator:具體工廠。制造產品的實際工廠。它負責創建一個或者多個具體產品,只有ConcreteCreator類知道如何創建這些產品。
工廠方法模式是簡單工廠模式的延伸。在工廠方法模式中,核心工廠類不在負責產品的創建,而是將具體的創建工作交給子類去完成。也就是后所這個核心工廠僅僅只是提供創建的接口,具體實現方法交給繼承它的子類去完成。當我們的系統需要增加其他新的對象時,我們只需要添加一個具體的產品和它的創建工廠即可,不需要對原工廠進行任何修改,這樣很好地符合了“開閉原則”。
### 代碼實現
針對上面的解決方案,得到如下UML結構圖:

抽象產品類:Pizza.java
~~~
public abstract class Pizza {
protected String name; //名稱
protected String dough; //面團
protected String sause; //醬料
protected List<String> toppings = new ArrayList<String>(); //佐料
public void prepare() {
System.out.println("Preparing "+name);
System.out.println("Tossing dough");
System.out.println("Adding sause");
System.out.println("Adding toppings");
for(int i = 0;i < toppings.size();i++){
System.out.println(" "+toppings.get(i));
}
}
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName(){
return name;
}
}
~~~
具體產品類:NYStyleCheesePizza.java
~~~
public class NYStyleCheesePizza extends Pizza{
public NYStyleCheesePizza(){
name = "Ny Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sause = "Marinara Sauce";
toppings.add("Crated Reggiano Cheese");
}
}
~~~
ChicagoStyleCheesePizza.java
~~~
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza(){
name = "Chicago Style Deep Dish Cheese Pizza";
dough = "Extra Thick Crust Dough";
sause = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
public void cut(){
System.out.println("Cutting the Pizza into square slices");
}
}
~~~
抽象工廠:披薩總店。PizzaStore.java
~~~
public abstract class PizzaStore {
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/*
* 創建pizza的方法交給子類去實現
*/
abstract Pizza createPizza(String type);
}
~~~
具體工廠。披薩分店。NYPizzaStore.java
~~~
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String item) {
Pizza pizza = null;
if("cheese".equals(item)){
pizza = new NYStyleCheesePizza();
}
else if("veggie".equals(item)){
pizza = new NYStyleVeggiePizza();
}
else if("clam".equals(item)){
pizza = new NYStyleClamPizza();
}
else if("pepperoni".equals(item)){
pizza = new NYStylePepperoniPizza();
}
return pizza;
}
~~~
ChicagoPizzaStore.java
~~~
public class ChicagoPizzaStore extends PizzaStore {
Pizza createPizza(String type) {
Pizza pizza = null;
if("cheese".equals(type)){
pizza = new ChicagoStyleCheesePizza();
}
else if("clam".equals(type)){
pizza = new ChicagoStyleClamPizza();
}
else if("pepperoni".equals(type)) {
pizza = new ChicagoStylePepperoniPizza();
}
else if("veggie".equals(type)){
pizza = new ChicagoStyleVeggiePizza();
}
return pizza;
}
}
~~~
PizzaTestDrive.java
~~~
public class PizzaTestDrive {
public static void main(String[] args) {
System.out.println("---------Joel 需要的芝加哥的深盤披薩---------");
ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore(); //建立芝加哥的披薩店
Pizza joelPizza =chicagoPizzaStore.orderPizza("cheese"); //下訂單
System.out.println("Joel ordered a " + joelPizza.getName() + "\n");
System.out.println("---------Ethan 需要的紐約風味的披薩---------");
NYPizzaStore nyPizzaStore = new NYPizzaStore();
Pizza ethanPizza = nyPizzaStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + ethanPizza.getName() + "\n");
}
}
~~~
### 優點
* 1、 在工廠方法中,用戶只需要知道所要產品的具體工廠,無須關系具體的創建過程,甚至不需要具體產品類的類名。
* 2、 在系統增加新的產品時,我們只需要添加一個具體產品類和對應的實現工廠,無需對原工廠進行任何修改,很好地符合了“開閉原則”。
### 缺點
* 1、每次增加一個產品時,都需要增加一個具體類和對象實現工廠,是的系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。這并不是什么好事。
### 適用場景
* 1、一個類不知道它所需要的對象的類。在工廠方法模式中,我們不需要具體產品的類名,我們只需要知道創建它的具體工廠即可。
* 2、一個類通過其子類來指定創建那個對象。在工廠方法模式中,對于抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
* 3、將創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創建產品子類,需要時再動態指定。
### 總結
* 1、工廠方法模式完全符合“開閉原則”。
* 2、工廠方法模式使用繼承,將對象的創建委托給子類,通過子類實現工廠方法來創建對象。
* 3、工廠方法允許類將實例化延伸到子類進行。
* 4、工廠方法讓子類決定要實例化的類時哪一個。在這里我們要明白這并不是工廠來決定生成哪種產品,而是在編寫創建者類時,不需要知道實際創建的產品是哪個,選擇了使用哪個子類,就已經決定了實際創建的產品時哪個了。
* 5、在工廠方法模式中,創建者通常會包含依賴于抽象產品的代碼,而這些抽象產品是、由子類創建的,創建者不需要真的知道在制作哪種具體產品。
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux