第21次文章:工廠模式
- 2019 年 10 月 8 日
- 筆記
在我們GOFO23種設計模式中,上期着重介紹了創造型模式中的單例模式,這周我們着重介紹另一種創造型設計模式——工廠模式。
一、工廠模式
-實現了創建者和調用者的分離。
1、詳細分類
-簡單工廠模式
-工廠方法模式
-抽象工廠模式
2、面向對象設計的基本原則
(1)OCP(開閉原則,Open-Closed Principle)
一個軟件的實體,在後期的升級改造過程中,我們不應該通過修改其源代碼來完成我們的目的,而是通過在源代碼的基礎上增加新的模塊來完成我們需要的功能。
(2)DIP(依賴倒轉原則,Dependence Inversion Principle)
在完成每一個類的功能時,我們最好是通過實現接口的方式來實現每一個功能模塊的設計,也就是針對接口編程,不要針對實現編程。具體的實現類可以任意的被修改,如果針對具體是實現來編程,那麼很容易牽一髮而動全身,使得不同類之間耦合性太強。
(3)LoD(迪米特法則,Law of Demeter)
只與你直接的朋友通信,而避免和陌生人通信。這也屬於類與類之間的解耦性,其目的還是高內聚低耦合。
二、簡單工廠模式
1、要點
-簡單工廠模式也被稱為靜態工廠模式,就是工廠類一般是使用靜態方法,通過接受的參數的不同來返回不同的對象實例。
-對於增加新產品無能為力不修改代碼的話,是無法擴展的。
2、利用簡單工廠模式進行簡單的實現
為了模擬簡單工廠的設計模式,我們先講述一個生活中的簡單背景吧。比如現在建造汽車,對於一個用戶而言,並不需要知道建造汽車的每一個細節,僅僅需要知道自己需要什麼車就好了,比如「比亞迪」,「奧迪」等等。所以我們根據這種簡單工廠的設計模式,就可以將其進行實例化。具體如下所示:
(1)因為需要依據接口進行編程,所以先建立一個汽車的接口Car
public interface Car { void run();}
接口中的run方法,在可以根據後面的需要進行具體實現。
(2)依據我們的簡單假設,設計兩款車——比亞迪和奧迪。所以兩種不同的類就又產生了
public class Audi implements Car{ @Override public void run() { System.out.println("奧迪跑!"); }}
public class Byd implements Car{ @Override public void run() { System.out.println("比亞迪跑!"); }}
(3)假如我們不通過工廠模式進行建造這兩類車,那麼可以直接使用new方法進行創建
public static void main(String[] args) { Audi car1 = new Audi(); Byd car2 = new Byd(); car1.run(); car2.run(); }
tips:
這種情況屬於沒有使用工廠模式時的建造方法,用戶需要自己去構建相應的汽車,反映在代碼裏面的就是使用new方法來構建。這如果對比在現實生活中,就類似於用戶需要知道如何去構建一輛汽車。
(4)建造工廠類,通過工廠類實現用戶的需求
public class CarFactory { public static Car createCar(String type) { if("奧迪".equals(type)) { return new Audi(); }else if("比亞迪".equals(type)) { return new Byd(); }else { return null; } } }
tips:
在這裡我們將每一輛車的具體實現交給了一個工廠類——CarFactory,通過代碼我們也可以看到,對於不同的汽車,我們的工廠通過傳輸進來的參數進行區分。
(5)利用簡單工廠模式,我們再來觀察一下建造一個汽車的流程。
public static void main(String[] args) { Car car1 = CarFactory.createCar("奧迪"); Car car2 = CarFactory.createCar("比亞迪"); car1.run(); car2.run(); }
tips:
1:與普通模式(3)相比,在簡單工廠模式中,我們只需要調用工廠類CarFactory,並調用其中的建造方法——createCar,並傳遞用戶需要的參數就好了。對用戶而言,並不需要知道構造一輛汽車的具體方法和步驟。
2:雖然在(3)和(5)中,用戶需要構建一輛汽車的代碼數量並沒有減少,但是這僅僅是因為我們涉及的實例背景是十分簡單的一個例子。對比兩者的思想,普通模式(3)更加要求用戶對創建汽車細節的掌握,而簡單工廠模式(5),主要是將整個創建汽車的細節全部封裝在工廠中。假如遇到一個更加複雜的工藝流程,可以明顯的感受到簡單工廠模式的便利之處。
3:但是簡單工廠也有一個很明顯的缺點,就是不滿足我們之前提到的OCP開閉原則。假如我們需要新增一款汽車的建造方法,比如說「奔馳」,那麼我們就需要在CarFactory中新增「type」類型識別,以及相應的創建流程。而這些改動,都是在源代碼塊CarFactory中進行實現的,而不是直接進行擴展所得。
三、工廠方法模式
1、要點
-為了避免簡單工廠模式的缺點,不完全滿足OCP。
-工廠方法模式和簡單工廠模式最大的不同在於,簡單工廠模式只有一個(對於一個項目或者一個獨立模塊而言)工廠類,而工廠方法模式有一組實現了相同接口的工廠類。工廠方法模式屬於簡單工廠模式的一個升級版。
2、模式實現
在前面的簡單工廠模式中,不滿足OCP原則的核心在於,我們將CarFactory設計成為了一個「混亂」的類。每一種汽車的構建生產,我們都將其構造方法放入到CarFactory中,這樣就會導致所有不同汽車的構造都依賴於這一個類中進行實現,所以新增汽車的構造也需要在CarFactory中進行實現,這樣就大大的提升了每個類的耦合性,使得工廠中的功能太過於雜亂。所以針對這個問題,就有相應的工廠方法模式對其進行解決。工廠方法模式直接在簡單工廠模式的基礎上,更加細分CarFactory的每一種功能,將CarFactory也定義為一個接口,然後每一種不同的汽車,單獨創建一個工廠,並且實現CarFactory接口的功能。這樣使用一組單一汽車的製造工廠來代替一個混合汽車製造工廠。下面來具體的說明其中的改變。
(1)將CarFactory從實現類改為一個接口
public interface CarFactory { Car createCar();}
(2)針對每一款汽車,構造不一樣的製造工廠
public class AudiCarFactory implements CarFactory { @Override public Car createCar() { return new Audi(); }}
public class BydCarFactory implements CarFactory{ @Override public Car createCar() return new Byd(); }}
(3)此時我們在構建「比亞迪」和「奧迪」的時候,程序就成為下面這樣
public static void main(String[] args) { Car c1 = new AudiCarFactory().createCar(); Car c2 = new BydCarFactory().createCar(); c1.run(); c2.run(); }
tips:
1:在簡單工廠中,我們使用的createCar是CarFactory中的靜態方法,所以不需要new一個CarFactory對象,可以直接調用此方法。然而在工廠方法模式中,CarFactory是一個接口,其他的汽車構建類都是實現該接口來重寫創建方法,所以每種不同汽車的createCar方法都無法設置為靜態方法,在調用的時候需要依賴於一個實現類,所以需要new每一個汽車構建工廠類。
2:與此同時,如果我們現在需要新增一輛「奔馳」車的構建方式,那麼我們的做法也很簡單。在不改動源代碼的情況下,我們分別增加一個「奔馳」車類,和一個「奔馳」車的建造工廠就可以直接實現新增車輛的生產了。實現如下:
public class Benz implements Car { @Override public void run() { System.out.println("奔馳在跑"); }}
public class BenzCarFactory implements CarFactory{ @Override public Car createCar() { return new Benz(); }}
此時,用戶端的操作為
Car c3 = new BenzCarFactory().createCar();
通過這種方式,也就直接對生產工廠與不同的車種進行區分,完美的實現了OCP原則。
四、簡單工廠模式和工廠方法模式對比:
1、結構複雜度
簡單工廠模式佔優。簡單工廠模式只需要一個工廠類,而工廠模式的工廠類隨着產品類個數的增加而增加,這無疑會使類的個數越來越多,從而增加了結構的複雜程度。
2、代碼複雜度
簡單工廠模式在結構方面相對簡潔,在代碼方面就會比工廠方法模式複雜。簡單工廠模式的工廠類隨着產品類的增加需要增加很多方法(或代碼),而工廠方法模式每個具體工廠類只完成單一任務,代碼簡潔。
3、客戶端編程難度
工廠方法模式對染在工廠結構中引入了接口從而滿足了OCP ,但是在客戶端編碼中需要對工廠類進行實例化,而簡單工廠模式的工廠類是個靜態類,在客戶端無需實例化。
4、實際選擇
根據設計理論建議,我們應該選擇工廠方法模式,但是在實際上,我們一般都用簡單工廠模式。其實也很好理解,就好比通信中的七層網絡結構屬於理論中最優的,然而在實際的使用中,都是用的是TCP/IP四層結構,其實歸根到底,理論上的最優都是需要付出一定的代價,比如代碼的複雜程度,建設成本的增加等等,這些在實際的使用中都需要加以考慮。而在此處,我們通過對比也可以明顯的感覺到簡單工廠模式的簡潔方便之處,所以一般選擇簡單工廠模式。
五、抽象工廠模式
1、要點
-用來生產不同產品族的全部產品。(對於增加新的產品,無能為力;支持增加產品族)
-抽象工廠模式在有多個業務品種、業務分類時,通過抽象工廠模式產生需要的對象是一種非常好的解決方式。
2、具體實現
其實抽象工廠模式也是很具體的一個例子。以我們上面的建造汽車的過程而言,在我們現實生活中,我們可以粗略的將其分為低端汽車和高端汽車,每種汽車對應的發動機、座椅、輪胎等等都可以加以區分高端和低端。我們設置為高端汽車使用高端的部件,而低端汽車使用低端的部件。然後就可以通過每種汽車整體,來獲取其中的相關部件。例如:
(1)首先規定發動機、座椅、輪胎均有高端和低端兩種質量
public interface Engine { void run(); void start();} class LuxuryEngine implements Engine{ @Override public void run() { System.out.println("高端發動機轉的快!"); } @Override public void start() { System.out.println("高端發動機可以自啟動!"); }} class LowEngine implements Engine{ @Override public void run() { System.out.println("低端發動機轉的慢!"); } @Override public void start() { System.out.println("低端發動機不可以自啟動!"); }}
這裡僅僅將發動機的構建類代碼放在這裡,座椅和輪胎的構建與其相同,都是先定義一個座椅或者輪胎接口,然後再具體的實現高端和低端產品類。
(2)再建立一個相應汽車構建工廠接口
public interface CarFactory { Engine createEngine(); Seat createSeat(); Tyre createTyre();}
分別實現高端汽車和低端汽車的構建
public class LuxuryCarFactory implements CarFactory { @Override public Engine createEngine() { return new LuxuryEngine(); } @Override public Seat createSeat() { return new LuxurySeat(); } @Override public Tyre createTyre() { return new LuxuryTyre(); }}
這裡僅列舉出高端汽車的構建工廠,低端汽車構建工廠與之類似。
(3)我們在用戶端進行測試一下
public class Client { public static void main(String[] args) { CarFactory c1 = new LuxuryCarFactory(); Engine e1 = c1.createEngine(); Seat s1 = c1.createSeat(); Tyre t1 = c1.createTyre(); e1.run(); e1.start(); s1.massage(); t1.revolve(); System.out.println("####################"); CarFactory c2 = new LowCarFactory(); Engine e2 = c2.createEngine(); Seat s2 = c2.createSeat(); Tyre t2 = c2.createTyre(); e2.run(); e2.start(); s2.massage(); t2.revolve(); }}
檢查一下結果

tips:
如上所示,我們通過高端汽車工廠產生一個高端汽車對象,然後可以通過這個對象獲取一整個產品族。這就是我們抽象工廠模式的主要思想,這樣可以幫助在我們生產一系列雜亂的對象的時候,有一個更加清晰的思路,也便於我們操作。
六、工廠模式要點
-簡單工廠模式:雖然某種程度不符合設計原則,但實際使用最多。
-工廠方法模式:不修改已有類的前提下,通過增加新的工廠類實現擴展。
-抽象工廠模式:不可以增加產品,可以增加產品族!
七、對於工廠模式的一點理解
通過介紹工廠模式,其實仔細體會其中的邏輯,也就是我們社會進化的一種內在邏輯。
在最初的農耕社會,我們需要自己去了解如何種地,播種,收割,將農作物轉化為麵粉等等流程,每個人的知識面很寬廣,但是並不利於效率的提升。而在我們現在的社會中,生產方式被不斷的改進,種植人員僅需要知道種植,不負責後面的任何其他事情。這也就體現了社會分工明確的一種優勢,每個人知道的很少,但卻很專註其中某一項,這樣可以大大的提升效率。
就好比剛剛的簡單工廠模式,用戶在生產汽車的時候,甚至可以不知道new方法,只要知道createCar方法就夠了,在createCar方法背後發生了什麼,並不屬於用戶需要考慮的問題。在後面的工廠方法模式中,同樣是對耦合性的降低,只要做到少依賴同一個類,這樣的系統才會更加穩定牢固,這種思想又恰恰是現在社會進步的一種模式,世界的多極化背後,也同樣是基於這樣一個理論。所以設計模式的學習,更重要的應該是去理解模式背後的邏輯是什麼,應用的方面也就不止於編程上了!
