工廠方法模式(Factory Method Patter)是"創建對象的接口",讓子類決定實例化哪一個類,並使一個類的實例化延遲到其子類.工廠方法模式在我們的開發工作中,經常會用到.
下面以汽車制造為例,看看一般的工廠方法模式是如何實現的,代碼如下:
1 public class Client { 2 public static void main(String[] args) { 3 //生產車輛 4 Car car = CarFactory.createCar(FordCar.class); 5 } 6 } 7 8 //抽象產品 9 interface Car { 10 }; 11 //具體產品類 12 class FordCar implements Car { 13 }; 14 //具體產品類 15 class BuickCar implements Car { 16 }; 17 //工廠類 18 class CarFactory { 19 //生產汽車 20 public static Car createCar(Class<? extends Car> c) { 21 try { 22 return (Car) c.newInstance(); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 return null; 27 } 28 }
這是最原始的工廠方法模式,有兩個產品"福特騎車和別克騎車,然后通過工廠方法模式來生產,有了工廠方法模式,我們就不用關心一輛車具體是怎么生成的了,只要告訴工廠"給我生產一輛福特騎車"就可以了,下面是產出一輛福特騎車時客戶端的代碼:
Car car = CarFactory.createCar(FordCar.class);
這就是我們經常使用的工廠方法模式,但經常使用不代表就是最優秀的,最簡潔的.
此處在介紹一種通過枚舉實現工廠方法模式的方案,誰優誰劣自行評價.枚舉實現工廠方法模式有兩種方法:
(1)枚舉非靜態方法實現工廠方法模式
我們知道每個枚舉項都是該枚舉的實例對象,那是不是定義一個方法可以生成每個枚舉項的對應產品來實現此模式呢?代碼如下:
1 public class Client { 2 public static void main(String[] args) { 3 //生產汽車 4 Car car = CarFactory.BuickCar.create(); 5 } 6 } 7 8 interface Car { 9 }; 10 11 class FordCar implements Car { 12 }; 13 14 class BuickCar implements Car { 15 }; 16 17 enum CarFactory { 18 //定義工廠類能生產汽車的類型 19 FordCar, BuickCar; 20 //生產汽車 21 public Car create() { 22 switch (this) { 23 case FordCar: 24 return new FordCar(); 25 case BuickCar: 26 return new BuickCar(); 27 default: 28 throw new AssertionError("無效參數"); 29 } 30 } 31 }
create是一個非靜態方法,也就是只有通過FordCar,BuickCar枚舉項才能訪問,采用這種方式實現工廠方法模式時,客戶端要產生一輛汽車就很簡單了.代碼如下:
Car car = CarFactory.BuickCar.create();
(2)通過抽象方法生成產品
枚舉類型雖然不能繼承,但是可以用abstract修飾其方法,此時就標識該枚舉是一個抽象枚舉,需要每個枚舉項自行實現該方法,也就說枚舉項的類型是該枚舉的一個子類,看代碼:
1 public class Client { 2 public static void main(String[] args) { 3 Car car = CarFactory.BuickCar.create(); 4 } 5 } 6 7 interface Car { 8 }; 9 10 class FordCar implements Car { 11 }; 12 13 class BuickCar implements Car { 14 }; 15 16 enum CarFactory { 17 FordCar { 18 public Car create() { 19 return new FordCar(); 20 } 21 }, 22 BuickCar { 23 public Car create() { 24 return new BuickCar(); 25 } 26 }; 27 //抽象生產方法 28 public abstract Car create(); 29 }
首先定義一個抽象制造方法create,然后 每個枚舉項自行實現,這種方式編譯后會產生兩個CarFactory的匿名子類,因為每個枚舉項都 要實現抽象create方法,客戶端的調用與上一個方案相同,不再贅述.
為什么使用枚舉類型的工廠方法模式有以下三個優點:
(1)避免錯誤調用的發生
一般工廠方法模式中的生產方法(也就是createCar方法)可以接收三種類型的參數:類型參數(Class),String參數(生產方法中判斷String參數是需要生產什么產品),int參數(根據int值判斷需要生產什么類型的產品).
這三種參數都是寬泛的數據類型,很容易產生錯誤.比如邊界問題,null值問題,而且出現這類錯誤編譯器還不會報警.例如:
Car car = CarFactory.createCar(Car.class);
Car是一個接口,完全合乎createCar方法的要求,所以它在編譯時不會報任何錯誤,但一運行起來就會報java.lang.InstantiationException異常,而使用枚舉類型的工廠方法模式就不存在該問題.不需要傳遞任何參數,只需要選擇好生產什么類型的產品就可以了.
(2)性能好,使用便捷.
枚舉類型的計算是以int類型的計算為基礎的,這是最基本的操作,性能當然快.
(3)降低類間的耦合
不管生產方法接收的是Class,String還是int參數,都會成為客戶端類的負擔.這些類並不是客戶端需要的,而是因為工廠方法的限制必須輸入的.******
下一次,使用枚舉來實現工廠方法模式.
