首先是簡單工廠模式。
一句話描述就是用一個工廠類去封裝new的工廠,返回新建的一個對象的引用。如果這個方法是靜態的,就稱之為靜態工廠。一般來說這種做法很常見,尤其在一些JAVA工具類中。它的好處和壞處網上的分析都很多,但我覺得沒必要分的這么細。最最最直接的好處是用起來簡單,方便和不容易出錯。
工廠方法模式則復雜一點。它的做法是在父類聲明一個final方法用來真正被外部調用(在子類中被繼承但是不允許覆蓋)。在這個方法中調用一個抽象方法去具體實現新建對象,可是這個抽象方法本身由子類實現。
舉個例子就是:
1 //主類 2 public class Main { 3 public static void main(String[] args) { 4 FruitFactory factory = new AppleFactory(); 5 Fruit product = factory.getProduct(); 6 if(product.getClass().equals(Apple.class)) 7 System.out.println("生產了一個蘋果"); 8 } 9 } 10 11 //工廠繼承體系 12 abstract class FruitFactory { 13 public final Fruit getProduct() { 14 Fruit o = getSpecificProduct(); 15 if(o != null) 16 System.out.println("水果檢查合格!"); 17 return o; 18 } 19 protected abstract Fruit getSpecificProduct(); 20 } 21 22 class AppleFactory extends FruitFactory { 23 @Override 24 protected Fruit getSpecificProduct() { 25 return new Apple(); 26 } 27 } 28 29 class BananaFactory extends FruitFactory { 30 @Override 31 protected Fruit getSpecificProduct() { 32 return new Banana(); 33 } 34 } 35 36 //產品繼承體系 37 abstract class Fruit { 38 39 } 40 41 class Apple extends Fruit { 42 43 } 44 45 class Banana extends Fruit { 46 47 }
首先,這個地方的方法調用過程是:
蘋果工廠對象調用了繼承下來的getProduct()方法。而這個方法是final的,換句話說最終會執行其父類FruitFactory的這個方法(寫成final就是為了防止子類覆蓋)。接着父類的這個方法調用了一個抽象方法,這個抽象方法在子類具體實現。所以最終還是生產了一個Apple。然后會父類方法執行這個繼承體系下所有工廠返回的結果都要執行的部分:檢查。
看到這的一個疑惑是:所有的東西都在AppleFactory里面實現的,那為什么要先去找父類,再把皮球踢回子類呢?
其實這個例子比較簡單,所以看上去沒啥必要,可是在實際使用中有兩種容易出現的情況:
1,創建實例對象的過程中有很多“機械而重復”的操作。
2,有很多操作必須這個體系下的所有工廠都去做。
其實這是一個問題的不同角度,也就是有的代碼需要復用,有的代碼需要分用。把那些可以封裝的相同代碼都寫到父類的方法里面(比如檢查是否合格),而那些各自不同的底層實現由底層各自實現(比如具體到底是生產什么東西)。
同時介紹一個設計原則:依賴倒置原則:要依賴抽象,不要依賴具體類。
前面我的“產品”的靜態類型(這個詞的意思是聲明的對象類型)都是Fruit,而不是具體的Apple等,哪怕方法返回的明明就是一個Apple。
這是解耦合的重要一步,其實也可以說是面向對象思想中的最重要的一點。如果沒有這種向上轉型成基礎接口(這個地方的接口未必一定是一個Interface)從而使用多態的做法,那么會出現這個問題:所有地方的靜態類型和實際類型一致,程序之間一環扣一環,耦合相當大。
那么抽象工廠方法呢?其實就是先定義一個基礎接口,然后用不同的工廠實現這個接口。在具體的類需要創建的時候,傳入一個向上轉型的工廠,通過調用這個工廠的方法實現創建對象。和前面的繼承來實現相比,這里通過組合來實現了解耦合。同時,可以在抽象工廠中創建多個需要的對象。這個做法其實很常見,這里算是個總結。
